zoukankan      html  css  js  c++  java
  • C语言柔性数组

      结构中最后一个元素允许是未知大小的数组,这个数组就是柔性数组。但结构中的柔性数组前面必须至少一个其他成员,柔性数组成员允许结构中包含一个大小可变的数组,sizeof返回的这种结构大小不包括柔性数组的内存。包含柔数组成员的结构用malloc函数进行内存的动态分配,且分配的内存应该大于结构的大小以适应柔性数组的预期大小。柔性数组到底如何使用?

    欢迎关注我的个人博客:www.wuyudong.com, 更多精彩文章与您分享

    不完整类型

    C和C++对于不完整类型的定义是一样的,不完整类型是这样一种类型,它缺乏足够的信息例如长度去描述一个完整的对象。

    不完整类型举例:
    前向声明就是一种常用的不完整类型

    struct test; //test 只给出了声明,没有给出定义

    不完整数据类型必须通过某种方式补充完整,才能使它们进行实例化。否则只能用于定义指针或引用,因为此时实例化的是指针或引用本身,不是base和test对象

    一个未知长度的数组也属于不完整类型:

    extern int a[];

    extern 关键字不能去掉,因为数组的长度未知,不能作为定义出现。不完整类型的数组需要补充完整才能使用。不完整类型的数组可以通过几种方式补充完整,大括号形式的初始化就是其中的一种方式:

    int a[] = { 10,20 };

    结构体

    首先,我们需要知道——所谓变量,其实是内存地址的一个抽像名字罢了。在静态编译的程序中,所有的变量名都会在编译时被转成内存地址。机器是不知道我们取的名字的,只知道地址。

    所以有了——栈内存区,堆内存区,静态内存区,常量内存区,我们代码中的所有变量都会被编译器预先放到这些内存区中。

    有了上面这个基础,我们来看一下结构体中的成员的地址是什么?我们先简单化一下代码:

    struct test{
        int i;
        char *p;
    };

    上面代码中,test结构中i和p指针,在C的编译器中保存的是相对地址——也就是说,他们的地址是相对于struct test的实例的。如果我们有这样的代码:

    struct test t;

    下面做个实验:

    #include<stdio.h>
    struct test{
        int i;
        char *p;
    };
    int main(void)
    {
        struct test t;
        printf("%p
    ", &t);
        printf("%p
    ", &(t.i));
        printf("%p
    ", &(t.p));
        return 0;
    }

    运行结果:

    我们可以看到,t.i的地址和t的地址是一样的,t.p的址址相对于t的地址多了个8。说白了,t.i 其实就是(&t + 0×0)t.p 的其实就是 (&t + 0×8)。0×0和0×8这个偏移地址就是成员i和p在编译时就被编译器给hard code了的地址。于是,你就知道,不管结构体的实例是什么——访问其成员其实就是加成员的偏移量

    下面再来做个实验:

    #include<stdio.h>
    struct test{
        int i;
        short c;
        char *p;
    };
    int main(void)
    {
        struct test *pt=NULL;
        printf("%p
    ", &(pt->i));
        printf("%p
    ", &(pt->c));
        printf("%p
    ", &(pt->p));
        return 0;
    }

    运行结果:

     注意:上面的pt->p的偏移之所以是0×8而不是0×6,是因为内存对齐了(我在64位系统上)。关于内存对齐,可参看《C语言内存对齐详解》一文。

    柔性数组

      柔性数组成员(flexible array member)也叫伸缩性数组成员,这种代码结构产生于对动态结构体的需求。在日常的编程中,有时候需要在结构体中存放一个长度动态的字符串,一般的做法,是在结构体中定义一个指针成员,这个指针成员指向该字符串所在的动态内存空间,例如:

    struct s_test
    {
        int a;
        double b;
        char* p;
    };

    p指向字符串,这种方法造成字符串与结构体是分离的,不利于操作。把字符串和结构体连在一起的话,效果会更好,可以修改如下:

    char a[] = "Hello world";
    struct s_test *ptest = (struct s_test*)malloc(sizeof(s_test)+streln(a)+1);
    strcpy(ptest+1,a);

    这样一来,(char*)(ptestt + 1)就是字符串“hello world”的地址。这时候p成了多余的东西,可以去掉。但是,又产生了另外一个问题:老是使用(char*)(ptestt + 1)不方便。如果能够找出一种方法,既能直接引用该字符串,又不占用结构体的空间,就完美了,符合这种条件的代码结构应该是一个非对象的符号地址,在结构体的尾部放置一个0长度的数组是一个绝妙的解决方案。不过,C/C++标准规定不能定义长度为0的数组,因此,有些编译器就把0长度的数组成员作为自己的非标准扩展,例如:

    struct s_test2
    {
        int a;
        double b;
        char c[0];
    };

    c就叫柔性数组成员,如果把ptest指向的动态分配内存看作一个整体,c就是一个长度可以动态变化的结构体成员,柔性一词来源于此。c的长度为0,因此它不占用test的空间,同时ptest->c就是“hello world”的首地址,不需要再使用(char*)(ptestt + 1)这么丑陋的语法了。

    鉴于这种代码结构所产生的重要作用,C99甚至把它收入了标准中:

    As a special case, the last element of a structure with more than one named member may have an incomplete array type; this is called a flexible array member.
    C99使用不完整类型实现柔性数组成员,标准形式是这样的:

    struct s_test
    {
      int a;
      double b;
      char c[];
    };

    c同样不占用test的空间,只作为一个符号地址存在,而且必须是结构体的最后一个成员。柔性数组成员不仅可以用于字符数组,还可以是元素为其它类型的数组,例如:

    struct s_test
    {
        int a;
        double b;
        float[];
    };

    首先,我们要知道,0长度的数组在ISO C和C++的规格说明书中是不允许的。这也就是为什么在VC++2012下编译你会得到一个警告:“arning C4200: 使用了非标准扩展 : 结构/联合中的零大小数组”。

    那么为什么gcc可以通过而连一个警告都没有?那是因为gcc 为了预先支持C99的这种玩法,所以,让“零长度数组”这种玩法合法了。关于GCC对于这个事的文档在这里:“Arrays of Length Zero”,文档中给了一个例子,完整代码如下:

    #include <stdlib.h>
    #include <string.h>
    struct line {
       int length;
       char contents[0]; // C99的玩法是:char contents[]; 没有指定数组长度
    };
    int main(){
        int this_length=10;
        struct line *thisline = (struct line *)
                         malloc (sizeof (struct line) + this_length);
        thisline->length = this_length;
        memset(thisline->contents, 'a', this_length);
        return 0;
    }

    上面这段代码的意思是:我想分配一个不定长的数组,于是我有一个结构体,其中有两个成员,一个是length,代表数组的长度,一个是contents,代码数组的内容。后面代码里的 this_length(长度是10)代表是想分配的数据的长度。

    参考资料

    酷壳--C语言结构体里的成员数组和指针

    GCC文档--Arrays of Length Zero

  • 相关阅读:
    VIJOS-P1340 拯救ice-cream(广搜+优先级队列)
    uva 11754 Code Feat
    uva11426 GCD Extreme(II)
    uvalive 4119 Always an Interger
    POJ 1442 Black Box 优先队列
    2014上海网络赛 HDU 5053 the Sum of Cube
    uvalive 4795 Paperweight
    uvalive 4589 Asteroids
    uvalive 4973 Ardenia
    DP——数字游戏
  • 原文地址:https://www.cnblogs.com/wuyudong/p/c-flexible-array.html
Copyright © 2011-2022 走看看