关于结构内存分配机制的一些困惑?

Some confusions about struct memory allocation mechanism?

在我的项目中,我遇到了 C 程序。

  1. 如下图,htmp是一个struct指针。我们首先为它分配一块内存。但是为什么要为它的元素word再分配一块内存呢?
  2. 如果必须为结构的每个元素分配内存,为什么不为其其他元素分配内存,idnext
#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) != '[=10=]'; 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;
}

当您为结构分配内存时,只为成员分配了足以容纳指针的内存 word,该指针还必须指向您随后可以分配的有效内存。

在没有指向有效内存的情况下,它的值为 不确定 并试图用 不确定 值取消引用此类指针是未定义的行为.

你需要在脑海中区分两件事(1)我要存储的东西存储在什么内存中?;和 (2) 什么变量(指针)保存它存储位置的地址,以便我可以再次找到它。

你首先声明两个指向struct hashrec的指针:

HASHREC *htmp, *hprv;

指针不过是一个变量,它将指向其他内容的地址作为其值。当您第一次声明这两个指针时,它们 未初始化 并且没有地址。然后,您以一种相当尴尬的方式在 for 循环声明中初始化两个指针,例如hprv = NULL, htmp = ht[hval] 和后来的 hprv = htmp, htmp = htmp->next 所以大概两个指针现在都有一个地址并指向某处。

在循环之后(主体为空),您测试 if (htmp == NULL),这意味着 htmp 没有指向地址(如果您找到 hash-index 兴趣为空)。

然后为了为一个 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值,但是wordnext呢?它们都是指针。指针持有什么?可以找到他们指向的东西的地址。在哪里可以找到word?你想要的东西目前被w指向,但是不能保证w会继续持有你想要的东西,所以你要制作一个副本并将其作为HASHREC。所以你看:

htmp->word = malloc(strlen(w) + 1);    /* useless cast removed */

现在 malloc return 是什么?它 return 是新内存块开始的地址,strlen(w) + 1 字节长。由于指针保存其他东西的值作为它的值,htmp->word 现在将地址存储到新内存块的开头作为它的值。所以 htmp->word "points" 到新的内存块,你可以使用 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.[=60= 时你就到了末尾]

换个思路就是之前的struct hashrec next现在可以指向你刚刚分配的这个节点了。同样不需要额外分配,前一个节点 ->next 指针简单地指向序列中的下一个节点,不需要分配任何特定的新内存。它只是用作参考(或指向)链中的下一个节点。

这里有很多信息,但是当你思考确定(1)我想存储的东西存储在什么内存中时?;和 (2) 什么变量(指针)保存它存储位置的地址,以便我可以再次找到它...... - 事情开始落实到位。希望这有帮助。

malloc(size) 将分配给定大小的内存和 returns 分配内存的起始地址。地址存储在指针变量中。

char *word中,变量word是指针类型,只能存放char类型的指针,即一个字符数据在内存中的地址。

所以,当你给struct类型变量htmp分配内存时,它会分配如下内存,2字节为*word,[= id 39=]4 字节,*next

2 字节

现在,假设您要将以下内容存储到您的结构中:

{
word = Elephant
id = 1245
}

现在您可以通过 htmp->id = 1245 将 id 直接保存到 id 成员中。但是你还想在你的结构中保存一个单词“Elephant”,如何实现?

htmp->word = 'Elephant'会导致错误,因为word是一个指针。所以,现在你分配内存来存储实际的字符串文字 Elephant 并将起始地址存储在 htmp->word.

您可以在不需要为成员 word 单独分配内存的地方使用 char word[size],而不是在您的结构中使用 char *word。不这样做的原因是,你想 select 一些随机大小的单词,如果你存储的字符较少,这会浪费内存,如果单词太大,它甚至会丢失。