2017-02-02 67 views
1

我正在搞链接列表类型的数据结构以获得更好的C指针和结构,我不明白这一点。为什么node * root = malloc(sizeof(int))分配16个字节的内存而不是4个?

我认为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

计算中的下一位会发生什么? –

+0

@EdHeal,对不起,我不确定你在问什么。 –

+0

未定义行为意味着任何事情都可能发生,包括出现工作。 – aschepler

回答

3

这里有一些事情正在进行,再加上一个可能在这个例子中不是问题,但总的来说是一个问题。

1)int不能保证是4个字节,虽然在大多数C编译器实现它们。我会仔细检查sizeof(int)看看你得到什么。

2)node* root = malloc(sizeof(int))可能会导致各种问题,因为sizeof(struct node)是不一样的int。只要您尝试访问root->next,您就有未定义的行为。

3)sizeof(struct node)不只是int,它是intpointer。指针是(据我所知,有人引用标准,如果不是的话)整个程序的大小相同,取决​​于编译的方式(例如32位与64位)。您可以使用sizeof(void*)轻松地在编译器上进行检查。它应该与sizeof(int*)sizeof(double*)或任何其他指针类型相同。

4)您的结构应该sizeof(int) + sizeof(node*),但不能保证是。例如,假设我有这样的结构:

struct Example 
{ 
    char c; 
    int i; 
    double d; 
}; 

你会想到它的大小为,这是1 + 4 + 8 =在我的编译器13,但在实践中也不会。编译器可以在内部“对齐”成员以匹配底层的指令体系结构,这通常会增加结构体的大小。权衡是他们可以更快地访问数据。这不是标准化的,因编译器而异,甚至具有不同设置的相同编译器的不同版本。 You can learn more about it here

5)您的行printf("size of pointer root: %lu \n", sizeof(*root))不是指向根的指针的大小,它是结构根的大小。这使我相信你正在编译它为64位代码,所以sizeof(int)是4,而sizeof(void*)是8,并且它们被对齐以匹配系统字(8字节),虽然我不能看到没有看到你的编译器,系统和设置。如果您想知道指向root的指针的大小,则需要执行sizeof(node*)sizeof(root)。您取消引用您的版本的指针,所以说sizeof(node)

底线相当于是,您所遇到的怪事是不确定的行为。你不会找到一个具体的答案,只是因为你认为你在行为中发现了一个模式并不意味着你应该使用它(除非你希望以后不可能找到让你悲惨的错误)。

+0

谢谢你这个好的答案。回复:4)当你谈论底层架构时,这是关于* word *大小,其中单词是最快的高速缓存可以一次处理什么或类似的东西?并重新:5)我检查,我的指针大小为8个字节,整数是4个字节。我打印sizeof(* root)的原因是为了获得结构本身的大小。我期待4个字节,但我猜想类型设置实际上是告诉C如何导航保留在该地址的内存块,对吧?有点像int数组会导航4个字节4个字节。 –

+0

在4上,它与字的大小密切相关,但我不确定它是否会在x86到ARM之间变化。据我了解,它只是不标准化,每个编译器可以定义它的方式。我的理解是它应该是字的大小,除非数据类型溢出字大小(在32位x86上8字节的“double”),在这种情况下,我不知道。 – Cody

+0

5,我的意思是澄清你的'printf'中的文本是不正确的,因为这不是指针,它是数据类型(虽然你的评论似乎澄清这是你的意图)。因为你说你的int是4个字节,所以它的结构绝对大于4个字节,所以它需要更多的指针空间。我认为在理解对齐意味着什么时,你有正确的概念,如果你需要更多的解释,你应该问一个单独的问题或[浏览这些](http://stackoverflow.com/search?q=c+struct+alignment) 。 – Cody

1

你没有提及什么系统(M $或linux,32位或64位),但你对内存分配的假设是错误的。内存分配与某些指定的边界对齐,以保证支持类型的所有分配都正确对齐 - 对于64位模式,通常为16字节。

检查这 - libc的手册:

http://www.gnu.org/software/libc/manual/html_node/Aligned-Memory-Blocks.html

通过的malloc或realloc在GNU系统返回的块的地址是 总是八(或16在64位系统)的倍数。如果您需要 一个地址为2的倍数的块,它使用aligned_alloc或posix_memalign。 aligned_alloc和 posix_memalign在stdlib.h中声明。

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->nextroot->index之后的8个字节,因为这是一个64位的机器,所以elements of a struct align on 8 bytes。如果你要在索引后面加入另一个整数,下一个仍然是8个字节。

还有另一种可能性:即使您只请求sizeof(int)内存,malloc可能会分配更多。大多数内存分配器以大块的方式工作。但是这是所有的实现定义,所以你的代码仍然有undefined behavior


,而最古怪的是,如果我尝试运行:输出( “指针根的大小:%lu个\ N” 的sizeof(*根));我得到的指针根的大小:16,当我清楚地期望看到4

root是一个指向一个结构,你会期望sizeof(root)是指针大小,8个字节的64位机器上地址64位的内存。

*root指针的解引用,sizeof(*root)是结构的实际大小。那是16个字节。 (4为整数,4为填充,8为结构指针)。同样,C没有跟踪你分配了多少内存,它只跟踪变量的大小应该是什么。

+0

谢谢,这澄清了很多事情。 –

相关问题