指针和数组的区别

rpb*_*ear 0 c

我有一段代码,它们有什么区别?第一个,bufstruct 的元素地址比struct的大4,而第二个不是.

第一

#include <stdio.h>

typedef struct A
{
    int i;
    char buf[];  //Here
}A;

int main()
{
    A *pa = malloc(sizeof(A));
    char *p = malloc(13);
    memcpy(p, "helloworld", 10);
    memcpy(pa->buf, p, 13);

    printf("%x %x %d %s\n", pa->buf, pa, (char *)pa->buf - (char *)pa, pa->buf);
}
Run Code Online (Sandbox Code Playgroud)

第二

typedef struct A
{
    int i;
    char *buf; //Here
}A;
Run Code Online (Sandbox Code Playgroud)

Jon*_*ler 6

第一个是C99'灵活阵列成员'.第二个是当你没有C99或更高版本时可靠的后备.

使用灵活的阵列成员,您可以为阵列分配所需的空间以及主结构:

A *pa = malloc(sizeof(A) + strlen(string) + 1);

pa->i = index;
strcpy(pa->buf, string);

...use pa...

free(pa);
Run Code Online (Sandbox Code Playgroud)

就内存分配而言,buf成员没有大小(因此sizeof(A) == sizeof(int)除非由于数组对齐而存在填充问题 - 例如,如果你有一个灵活的数组double).

替代方案需要两个分配(和两个版本),或者在设置中需要注意:

typedef struct A2
{
    int   i;
    char *buf;
} A2;

A2 *pa2 = malloc(sizeof(A2));
pa2->buff = strdup(string);

...use pa2...

free(pa2->buff);
free(pa2);
Run Code Online (Sandbox Code Playgroud)

要么:

A2 *pa2 = malloc(sizeof(A2) + strlen(string) + 1);
pa2->buff = (char *)pa2 + sizeof(A2);

...use pa2...

free(pa2);
Run Code Online (Sandbox Code Playgroud)

请注意,使用A2需要更多内存,可以是指针的大小(单个分配),也可以是指针的大小和第二个内存分配的开销(双重分配).

你有时会看到一些被称为'struct hack'的东西; 这比C99标准早,并且被灵活的阵列成员淘汰.这个代码看起来像:

typedef struct A3
{
    int  i;
    char buf[1];
} A3;

A3 *pa3 = malloc(sizeof(A3) + strlen(string) + 1);
strcpy(pa3->buf, string);
Run Code Online (Sandbox Code Playgroud)

这几乎与灵活的阵列成员相同,但结构更大.在该示例中,在大多数机器上,结构A3将是8字节长(而不是4字节A).

GCC对零长度阵列有一些支持; 您可能会看到数组维度为0的struct hack.这对于任何不模仿GCC的编译器都是不可移植的.

它被称为'struct hack',因为它不能保证可以通过语言标准移植(因为你在声明的数组的边界之外访问).然而,根据经验,它"一直有效",可能会继续这样做.不过,您应该优先使用灵活的数组成员而不是struct hack.


ISO/IEC 9899:2011§6.7.2.1结构和联合说明符

3结构或联合不应包含具有不完整或函数类型的成员(因此,结构不应包含其自身的实例,但可包含指向其自身实例的指针),但结构的最后一个成员除外多个命名成员可能具有不完整的数组类型; 这样的结构(以及可能递归地包含这种结构的成员的任何联合)不应是结构的成员或数组的元素.

18作为一种特殊情况,具有多个命名成员的结构的最后一个元素可能具有不完整的数组类型; 这被称为灵活的阵列成员.在大多数情况下,将忽略灵活数组成员.特别地,结构的尺寸好像省略了柔性阵列构件,除了它可以具有比省略意味着更多的拖尾填充.但是,当一个.(或->)运算符有一个左操作数(指向一个具有灵活数组成员的结构)和右操作数命名该成员时,它的行为就好像该成员被替换为最长的数组(具有相同的元素类型)不会使结构大于被访问的对象; 数组的偏移量应保持为灵活数组成员的偏移量,即使这与替换数组的偏移量不同.如果这个数组就没有的元素,它的行为就好像它有一个元素,但如果任何试图访问该元素或产生一个指向一个过去,它的行为是不确定的.