关于结构内存分配机制的一些困惑?
Some confusions about struct memory allocation mechanism?
在我的项目中,我遇到了 C 程序。
- 如下图,
htmp
是一个struct指针。我们首先为它分配一块内存。但是为什么要为它的元素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) != '[=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
值,但是word
和next
呢?它们都是指针。指针持有什么?可以找到他们指向的东西的地址。在哪里可以找到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 一些随机大小的单词,如果你存储的字符较少,这会浪费内存,如果单词太大,它甚至会丢失。
在我的项目中,我遇到了 C 程序。
- 如下图,
htmp
是一个struct指针。我们首先为它分配一块内存。但是为什么要为它的元素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) != '[=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
值,但是word
和next
呢?它们都是指针。指针持有什么?可以找到他们指向的东西的地址。在哪里可以找到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
现在,假设您要将以下内容存储到您的结构中:
{
word = Elephant
id = 1245
}
现在您可以通过 htmp->id = 1245
将 id 直接保存到 id
成员中。但是你还想在你的结构中保存一个单词“Elephant”,如何实现?
htmp->word = 'Elephant'
会导致错误,因为word是一个指针。所以,现在你分配内存来存储实际的字符串文字 Elephant 并将起始地址存储在 htmp->word
.
您可以在不需要为成员 word
单独分配内存的地方使用 char word[size]
,而不是在您的结构中使用 char *word
。不这样做的原因是,你想 select 一些随机大小的单词,如果你存储的字符较少,这会浪费内存,如果单词太大,它甚至会丢失。