在我的项目中,我遇到了C程序。
htmp
是一个结构指针。我们首先为它分配一个内存。但为什么我们要再次为其元素word
分配一个内存?id
和next
分配内存?#define HASHREC bitewisehash
typedef struct hashrec {
char *word;
long long id;
struct hashrec *next;
} HASHREC;
/* Move-to-front hashing and hash function from Hugh Williams, http://www.seg.rmit.edu.au/code/zwh-ipl/ */
/* Simple bitwise hash function */
unsigned int bitwisehash(char *word, int tsize, unsigned int seed) {
char c;
unsigned int h;
h = seed;
for (; (c =* word) != '\0'; word++) h ^= ((h << 5) + c + (h >> 2));
return((unsigned int)((h&0x7fffffff) % tsize));
}
/* Insert string in hash table, check for duplicates which should be absent */
void hashinsert(HASHREC **ht, char *w, long long id) {
HASHREC *htmp, *hprv;
unsigned int hval = HASHFN(w, TSIZE, SEED);
for (hprv = NULL, htmp = ht[hval]; htmp != NULL && scmp(htmp->word, w) != 0; hprv = htmp, htmp = htmp->next);
if (htmp == NULL) {
htmp = (HASHREC *) malloc(sizeof(HASHREC)); # allocate memory for htmp
htmp->word = (char *) malloc(strlen(w) + 1); # why allocate memory again ?
strcpy(htmp->word, w); #
htmp->id = id; # why not allocate memory for htmp->id ?
htmp->next = NULL; # why nor allocate memory for htmp->next?
if (hprv == NULL) ht[hval] = htmp;
else hprv->next = htmp;
}
else fprintf(stderr, "Error, duplicate entry located: %s.\n",htmp->word);
return;
}
你需要在脑海中分离两件事(1)我想存储的东西是什么内存? (2)什么变量(指针)将地址保存到存储的位置,以便我可以再次找到它。
你首先声明两个指向struct hashrec
的指针:
HASHREC *htmp, *hprv;
指针只不过是一个变量,它将地址保存为其他值作为其值。当你第一次声明这两个指针时,它们是未初始化的并且没有地址。然后,你以一种相当尴尬的方式,在for
循环声明中初始化两个指针,例如: hprv = NULL, htmp = ht[hval]
和后来的hprv = htmp, htmp = htmp->next
所以大概两个指针现在都有一个地址并指向某个地方。
在循环之后(使用空体),您测试if (htmp == NULL)
,这意味着htmp
不指向地址(如果您发现感兴趣的哈希索引为空,则可能是这种情况)。
然后,为了为一个HASHREC
(例如struct hashrec
)提供存储空间,您需要分配存储空间,以便在内存块中存储要存储的内容。所以你分配一个块来保存一个结构。 (见:Do I cast the result of malloc?)
现在,看看你为内存分配了什么:
typedef struct hashrec {
char *word;
long long id;
struct hashrec *next;
} HASHREC;
您已为包含(1)char *word;
(指向char的指针 - 8字节(x86上为4个字节))的结构分配存储空间; (2)一个long long id;
(两个都是8个字节)和(3)一个指针,用于保存序列中下一个HASHREC
的地址。
毫无疑问,id
可以持有long long
值,但word
和next
呢?它们都是指针。指针有什么作用?可以找到他们指向的东西的地址。哪里可以找到word
?你想要的东西目前由w
指出,但不能保证w
会继续保留你想要的单词,所以你打算复制并将它存储为HASHREC
的一部分。所以你看:
htmp->word = malloc(strlen(w) + 1); /* useless cast removed */
现在malloc
回归了什么?它将地址返回到新的内存块的开头,strlen(w) + 1
bytes long。由于指针将其他值保存为其值,因此htmp->word
现在将地址存储到新内存块的开头作为其值。所以htmp->word
“指向”新的内存块,你可以使用htmp->word
作为参考来引用那块内存。
接下来发生的事情很重要:
strcpy(htmp->word, w); #
htmp->id = id; # why not allocate memory for htmp->id ?
htmp->next = NULL; # why nor allocate memory for htmp->next?
strcpy(htmp->word, w);
将w
复制到新的记忆块中。 htmp->id = id;
将id
的值分配给htmp->id
,因为当你分配时,不需要分配:
htmp = malloc(sizeof(HASHREC)); /* useless cast removed */
你为(1)char *
指针,(2)long long id;
和(3)struct hashrec*
指针分配存储 - 你已经为long long
分配了所以htmp->id
可以将id
的值存储在long long
的内存中。
htmp->next = NULL; # why nor allocate memory for htmp->next?
您试图存储的是什么,需要为htmp->next
进行新的分配? (提示:目前没什么)它将指向下一个struct hashrec
。目前它正在初始化为NULL
,以便下次迭代到所有struct hashrec next
指针的末尾时,你知道当你到达NULL
时你就到了最后。
另一种思考方式是,之前的struct hashrec next
现在可以指向您刚刚分配的节点。同样不需要额外的分配,前一个节点->next
指针只是依次指向下一个节点,不需要分配任何特定的新内存。它仅用作引用(或指向)链中下一个节点的引用。
这里有很多信息,但是当你经历思考过程中确定(1)我要存储的东西是什么内存? (2)什么变量(指针)将地址保存到存储的位置,以便我可以再次找到它... - 事情开始落实到位。希望这可以帮助。
为结构分配内存时,只为成员word
分配了足够的内存来保存指针,该指针也必须指向有效内存,然后可以分配。
在没有指向有效内存的情况下,它的值是不确定的并且试图用不确定的值取消引用这样的指针是未定义的行为。
malloc(size)
将分配给定大小的内存并返回分配的内存的起始地址。地址存储在指针变量中。
在char *word
中,变量word
是指针类型,它只能保存char类型的指针,即内存中字符数据的地址。
因此,当您为结构类型变量htmp
分配内存时,它将按如下方式分配内存,*word
为2个字节,id
为4个字节,*next
为2个字节
现在,假设您希望将以下内容存储到结构中:
{
word = Elephant
id = 1245
}
现在你可以通过这个id
直接将id保存到htmp->id = 1245
成员。但是你也想在你的结构中保存一个单词“Elephant”,如何实现这个目标?
htmp->word = 'Elephant'
会导致错误,因为word是一个指针。所以,现在你分配内存来存储实际的字符串文字Elephant并将起始地址存储在htmp->word
中。
您可以使用char *word
而不需要为成员char word[size]
分别分配内存,而不是在您的结构中使用word
。不这样做的原因是你要为单词选择一些随机大小,如果你存储较少的字符会浪费内存,如果单词太大则甚至会掉落。