C char数组作为指针

时间:2012-10-12 16:40:48

标签: c arrays pointers

我想明白:

  • 为什么有时会将C中的char[1]用作char*(为什么要这样做?)和
  • 内部是如何工作的(正在发生什么)

提供以下示例程序:

#include <stdio.h>
#include <string.h>

struct test_struct {
    char *a;
    char b[1];
} __attribute__((packed)); ;

int main() {

    char *testp;
    struct test_struct test_s;

    testp = NULL;
    memset(&test_s, 0, sizeof(struct test_struct));

    printf("sizeof(test_struct) is: %lx\n", sizeof(struct test_struct));

    printf("testp at: %p\n", &testp);
    printf("testp is: %p\n", testp);

    printf("test_s.a at: %p\n", &test_s.a);
    printf("test_s.a is: %p\n", test_s.a);

    printf("test_s.b at: %p\n", &test_s.b);
    printf("test_s.b is: %p\n", test_s.b);

    printf("sizeof(test_s.b): %lx \n", sizeof(test_s.b));

    printf("real sizeof(test_s.b): %lx \n", ((void *)(&test_s.b) - (void *)(&test_s.a)) );

    return 0;
}

我得到以下输出(OS X,64位):

sizeof(test_struct) is: 9
testp at: 0x7fff62211a98
testp is: 0x0
test_s.a at: 0x7fff62211a88
test_s.a is: 0x0
test_s.b at: 0x7fff62211a90
test_s.b is: 0x7fff62211a90
sizeof(test_s.b): 1 
real sizeof(test_s.b): 8 

查看内存地址,可以看到即使结构大9字节,也分配了16个字节,这似乎是由char b[1]引起的。但是我不确定是否由于优化/内存对齐原因而分配了那些额外的字节,或者这是否与C对char数组的内部处理有关。

<fts.h>

中可以看到真实世界的例子 `man 3 fts`将结构成员`fts_name`显示为:

            char *fts_name;                 /* file name */

而/usr/include/fts.h将成员定义为:

            char fts_name[1];               /* file name */

最后,fts_name可以真正用作指向C字符串的指针。例如,使用printf("%s", ent->fts_name)打印到stdout可以正常工作。

因此,如果char[1]实际上是一个大字节,则它不能用作我的64位机器上的内存指针。另一方面,将其视为一个完整的char *也不起作用,如上面的test_s.b is输出所示,它应该显示一个NULL指针然后......

2 个答案:

答案 0 :(得分:2)

这是描述char[1]技巧的answer。基本上,我们的想法是在malloc()结构时分配更多内存,以便在没有额外分配的情况下为字符串提供一些存储空间。有时甚至可以看到char something[0]用于相同的目的,这使得更不直观。

  

另一方面,将其视为完整的烧焦*不起作用   或者,如上面输出的test_s.b所见,应该是   然后显示一个NULL指针......

如果某个东西是一个数组,那么它的名字和&name都只是指向C中数组的开头。无论它是结构中的成员还是独立变量,它都可以工作。 / p>

printf("real sizeof(test_s.b): %lx \n", ((void *)(&test_s.b) - (void *)(&test_s.a)) );

此行显示在此结构中为 a 而非b分配的空间大小。在b之后放一些东西并用它来减去。使用packed属性(这意味着你不允许编译器弄乱对齐等),你应该得到1.

#include <stdio.h>
#include <string.h>

struct test_struct {
    char *a;
    char b[1];
    char c;
} __attribute__((packed));

int main() {
  struct test_struct s;
  printf("%lx\n", ((void*)&s.c) - ((void*)&s.b));
  return 0;
}

我得到1

答案 1 :(得分:2)

当C不是你的母语时,理解混乱。首先要清理几件事。

在C中,所有var[n]表示“取var表示的地址,将n*sizeof(var's type)个字节添加到该地址,返回结果地址。另外值得注意的是,C语言确实如此不停止你走过数组的声明的大小。

您经常会在结构的尾部找到您正在查看的格式,这些格式旨在覆盖更大,更重要的可变长度内存分配。在这种结构中,习惯上(通常是强制性的)让前一个结构成员之一指示尾缓冲区空间的实际有效字节。

示例:

typedef struct X
{
   unsigned int count;
   char data[1];
} X;

这与声明指针成员明显不同,指针成员只不过是持有地址的变量。

typedef struct Y
{
    unsigned int count;
    char *dataptr;
} Y;

在Y中,dataptr 持有 一个地址(并且还有一个地址)。在X中,data 地址。

为什么这样做?看看这个。以下内存转储假定小端,1字节结构打包,以及4字节的整数和指针本机大小:

0x00000000  0x10 0x00 0x00 0x00 0x01 0x02 0x03 0x04 
0x00000008  0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C
0x00000010  0x0D 0x0E 0x0F 0x10;

现在在此上覆盖一个struct X,你有

count : 16
data[] : { 0x01, 0x02, 0x03, ... 0x010 };

struct Y上叠加count : 16 dataptr : 0x01020304 会产生明显不同的结果。

struct X

请记住,在C语言中,您可以轻松地(通常是悲惨地)走出数组声明大小的末尾。这种叠加技术只不过是对该功能的利用。鉴于以上对于struct X * pX = funcThatReturnsTheMemoryAddressAbove(); for (unsigned int i=0; i<pX->count; i++) { do something with pX->data[i]; } 头部占用的内存区域,您可以执行以下操作:

{{1}}

显然,您需要保持警惕,如何分配管理内存来执行此类操作。

不确定这是否有助于澄清事情,但希望有所帮助。