十舍七匹狼於~ http://www.oschina.net/ html
3.1 shell
使用2048做爲hash桶的數目: 鏈表最長的有5個元素,最少的1個元素,的Atom_read函數遍歷hash表, 而後寫入文件, 沒具體統計數字,肉眼觀察了下。 數據結構
主程序3_1.c 函數
#include <sys/time.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include "assert.h" #include "atom.h" #include "getOneword.h" int main(int argc, char const *argv[]) { char buf[1024]; int size; FILE *fp; int num; struct timeval tv_start, tv_end; double timeuse, totaltime = 0; size = sizeof(buf); fp = fopen("test_3_1.txt", "rb"); assert(fp); for (num = 0; num < 10000; ++num) { if (getOneword(fp, buf, size, first, rest) == 0) break; gettimeofday(&tv_start, NULL); Atom_new(buf, strlen(buf)); gettimeofday(&tv_end, NULL); timeuse = (tv_end.tv_sec - tv_start.tv_sec) * 1000000 + (tv_end.tv_usec - tv_start.tv_usec); timeuse /= 1000000; totaltime += timeuse; } printf("[totaltime = %fs]\n", totaltime); fclose(fp); Atom_read(); return 0; }
#include <stdio.h> #include <ctype.h> int getOneword(FILE *fp, char *buf, int size, int first(int c), int rest(int c)) { int i = 0; int c; c = getc(fp); for ( ; c != EOF; c = getc(fp)) if (first(c)) { if (i < size - 1) buf[i++] = c; c = getc(fp); break; } for ( ; c != EOF && rest(c); c = getc(fp)) if (i < size - 1) buf[i++] = c; if (i < size) buf[i] = '\0'; else buf[size - 1] = '\0'; if (c != EOF) ungetc(c, fp); return i > 0; } int first(int c) { return isalpha(c); } int rest(int c) { return isalpha(c) || c == '_'; }
int Atom_read() { int i; struct atom *p; FILE *fp; fp = fopen("result.txt", "wb"); assert(fp); for (i = 0; i < NELEMS(buckets); i++) if (buckets[i] != NULL) { for (p = buckets[i]; p; p = p->link) fprintf(fp, "buckets[%d]: [%s]\n", i, p->str); fprintf(fp, "\n\n\n"); } fclose(fp); return i > 0; }
3.3
使用strncmp有個顯著缺陷,那就是遇到'\0'就會終止比較。好比有2個字符序列:
str1="abcd\0g" 和 str2="abcd\0m", 那麼使用strncmp函數就會就會認爲str1和
str2相等,這不可接受。書中使用逐個字符比較,固然也能夠使用memcmp進行比較。
但這裏對於書中的寫法,仍然有個現實的考量,就是它會在拷貝完畢字符串以後顯
式的綴上'\0', 做者明顯是爲了取字符串方便, 不過這由應用程序調用時,是會產
生二義性的。
3.4
本題不明白。聽說這種 char str[1] 的寫法很hack. 測試
3.5
寫上hash碼對於特定操做是有明顯的好處: 當要擴展這個hash表時,不須要
再作一次hash了,直接將每一個節點的hash碼模上新的hash桶的數目就獲得了
在新hash表中所在的鏈表。這樣作不只節省了時間,還帶來了一個額外的好
處,就是在舊錶中同一個表項的節點,在新表中仍然在一塊兒,由於它們再舊
表中的hash碼是相同的,模上新的hash桶固然也相同了。這樣在搜索表項時,
舊有代碼照用不誤。
3.6
Atom_length函數之因此會慢,根本緣由在於,最壞狀況下它要遍歷全部存在
於hash表中的節點。一個簡單的解決方法是,稍微複雜化原子的數據結構,
即添加使用上述的hash碼。有了hash碼就能夠直接定位到特定的某個鏈表。
那麼對atom的改造變爲 atom
static struct atom { struct atom *link; unsigned long atom_hash; int len; char *str; } *buckets[2048];
3.7
extern void Atom_init(int hint); 實現這個函數的現實意義暫時還沒發現。 spa
3.8
代碼見下面,其中四個字符串是在作 3.1 題時找到的具備相同hash碼的兩個字符串:
3_8.c .net
#include <sys/time.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include "assert.h" #include "atom.h" int main(int argc, char const *argv[]) { char *s1 = "rebuttal"; char *s2 = "handler"; char *s3 = "hpl"; char *s4 = "Zero"; const char *p1 = Atom_new(s1, strlen(s1)); const char *p2 = Atom_new(s2, strlen(s2)); const char *p3 = Atom_new(s3, strlen(s3)); const char *p4 = Atom_new(s4, strlen(s4)); Atom_list(); Atom_free(p1); Atom_free(p3); // Atom_reset(); Atom_list(); return 0; }
void Atom_free(const char *str) { struct atom *p, *prev; int i; assert(str); for (i = 0; i < NELEMS(buckets); i++) for (p = buckets[i]; p; p = p->link) { if (p->str == str) { printf("to free: [%s]\n", str); if (p == buckets[i]) buckets[i] = p->link; else prev->link = p->link; free(p); // p = NULL; } prev = p; } } void Atom_list() { struct atom *p; int i; for (i = 0; i < NELEMS(buckets); i++) for (p = buckets[i]; p; p = p->link) printf("%d: [%s]\n", i, p->str); } void Atom_reset(void) { struct atom *p; int i; for (i = 0; i < NELEMS(buckets); i++) for (p = buckets[i]; p;) { buckets[i] = p->link; free(p); p = buckets[i]; } }
3.9
見下述代碼,主要使用了可變參數列表。
3_9.c 指針
#include <sys/time.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include "assert.h" #include "atom.h" int main(int argc, char const *argv[]) { char *s1 = "rebuttal"; char *s2 = "handler"; char *s3 = "hpl"; char *s4 = "Zero"; printf("Atom_vload:\n"); Atom_vload(s1, s2, s3, s4, NULL); Atom_list(); Atom_reset(); printf("\n\nAtom_aload:\n"); const char *strs[] = {"rebuttal", "handler", "hpl", "Zero", NULL}; Atom_aload(strs); Atom_list(); Atom_reset(); return 0; }
void Atom_vload(const char *str, ...) { va_list ap; va_start(ap, str); for (; str; str = va_arg(ap, const char *)) Atom_new(str, strlen(str)); va_end(ap); } void Atom_aload(const char *strs[]) { int i; const char *p; for (i = 0, p = strs[i]; p; p = strs[++i]) Atom_new(p, strlen(p)); }
最後列出添加了新函數的atom.c文件,其中沒有使用後面內存管理章節的FREE和ALLOC函數,使用的是標準庫函數,目的是編譯方便,如今編譯時只須要: rest
gcc -g 3_1.c atom.c getword.c