我正在搞乱使用Linked List类型的数据结构来更好地使用C中的指针和结构,我不明白这一点。
我认为malloc
将第一个大小为sizeof
的内存块的地址返回给指针。
在这种情况下,我的node struct
看起来像这样,是16个字节:
typedef struct node{
int index;
struct node* next;
}node;
如果我尝试这样做,我希望如此:node* root = malloc(sizeof(int))
malloc
只分配一个4字节的块,并将该块的地址返回给指针node
。
但是,我仍然能够为索引分配一个值并让root指向下一个节点,如下所示:
root->index = 0;
root->next = malloc(sizeof(node));
最奇怪的部分是,如果我尝试运行:printf("size of pointer root: %lu \n", sizeof(*root));
当我明确希望看到size of pointer root: 16
时,我得到4
。
发生了什么?
编辑:我刚试过malloc(sizeof(char))
,它仍然告诉我*root
是16个字节。
答案 0 :(得分:3)
这里有一些事情,还有一个在这个例子中可能不是问题,但一般来说是一个问题。
1)int
不保证是4个字节,尽管在大多数C编译器实现中它们都是。我会仔细检查sizeof(int)
,看看你得到了什么。
2)node* root = malloc(sizeof(int))
可能会导致各种问题,因为sizeof(struct node)
与int
不同。只要您尝试访问root->next
,就会有未定义的行为。
3)sizeof(struct node)
不仅仅是int
,而是int
和pointer
。指针是(据我所知,有人引用标准,如果没有)整个程序中的大小相同,具体取决于它的编译方式(例如32位与64位)。您可以使用sizeof(void*)
在编译器上轻松检查这一点。它应与sizeof(int*)
或sizeof(double*)
或任何其他指针类型相同。
4)你的struct 应该是sizeof(int) + sizeof(node*)
,但不能保证。例如,假设我有这个结构:
struct Example
{
char c;
int i;
double d;
};
你希望它的大小为sizeof(char) + sizeof(int) + sizeof(double)
,在我的编译器上是1 + 4 + 8 = 13,但实际上它不会。编译器可以在内部“对齐”成员以匹配底层指令体系结构,这通常会增加结构体大小。权衡是他们可以更快地访问数据。这不是标准化的,并且因编译器而异,甚至是具有不同设置的相同编译器的不同版本。 You can learn more about it here
5)你的行printf("size of pointer root: %lu \n", sizeof(*root))
不是指向root的指针的大小,它是struct root的大小。这使我相信您将其编译为64位代码,因此sizeof(int)
为4,sizeof(void*)
为8,并且它们被对齐以匹配系统字(8字节),尽管如果没有看到您的编译器,系统和设置,我就无法肯定。如果您想知道指向root的指针的大小,则需要sizeof(node*)
或sizeof(root)
。您取消引用版本中的指针,因此它等同于说sizeof(node)
最重要的是,您遇到的奇怪现象是未定义的行为。你不会找到具体的答案,只是因为你认为你在行为中发现了一个模式并不意味着你应该使用它(除非你想要以后找不到让你痛苦的bug)。
答案 1 :(得分:2)
你没有提到什么系统(M $或linux,32位或64位),但你对内存分配的假设是错误的。内存分配与某些指定边界对齐,以保证所支持类型的所有分配都正确对齐 - 对于64位模式,通常为16字节。
检查一下 - libc手册:
http://www.gnu.org/software/libc/manual/html_node/Aligned-Memory-Blocks.html
在GNU系统中由malloc或realloc返回的块的地址是 始终是八的倍数(或64位系统上的十六个)。如果你需要 一个块,其地址是两个高于2的幂的倍数 那,使用aligned_alloc或posix_memalign。 aligned_alloc和 posix_memalign在stdlib.h中声明。
答案 2 :(得分:2)
这里发生了一些事情。首先, C没有边界检查 。 C不会跟踪您为变量分配的内存量。你没有为一个节点分配足够的内存,但是C没有检查它。以下"工作",但实际上它没有。
node* root = malloc(sizeof(int));
root->index = 0;
root->next = malloc(sizeof(node));
由于没有为结构分配足够的内存,其他人的内存已被覆盖。你可以通过打印出指针来看到这一点。
printf("sizeof(int): %zu\n", sizeof(int));
printf("root: %p\n", root);
printf("&root->index: %p\n", &root->index);
printf("&root->next: %p\n", &root->next);
sizeof(int): 4
root: 0x7fbde5601560
&root->index: 0x7fbde5601560
&root->next: 0x7fbde5601568
我只分配了4个字节,所以我只能从0x7fbde5601560到0x7fbde5601564。 root->index
很好,但root->next
正在写给别人的记忆。它可能是未分配的,在这种情况下,它可能会分配给其他变量,然后您会看到发生的奇怪事情。或者它可能是某个现有变量的内存,在这种情况下它会覆盖该内存并导致很难调试内存问题。
但是它没有走出界限,以便走出分配给整个过程的内存,所以它没有触发你的操作系统memory protection 。这通常是segfault。
注意root->next
在root->index
之后是8个字节,因为这是64位机器,所以elements of a struct align on 8 bytes。如果要在索引之后将另一个整数放入结构中,则接下来仍然会关闭8个字节。
还有另一种可能性:即使您只询问sizeof(int)
内存,malloc
可能会分配更多内存。大多数内存分配器都以块的形式工作。但这是所有实现定义的,因此您的代码仍然具有undefined behavior。
最奇怪的部分是,如果我尝试运行:printf("指针根的大小:%lu \ n",sizeof(* root));我得到指针根的大小:16,当我明显期望看到4。
root
是一个指向结构的指针,你希望sizeof(root)
是指针大小的,64位机器上的8个字节可以处理64位内存。
*root
取消引用指针sizeof(*root)
是结构的实际大小。这是16个字节。 (4表示整数,4表示填充,8表示结构指针)。同样,C不会跟踪您分配的内存量,它只跟踪变量的大小。