前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >C语言0长度数组(可变数组/柔性数组)详解

C语言0长度数组(可变数组/柔性数组)详解

作者头像
用户6280468
发布于 2022-06-09 13:19:56
发布于 2022-06-09 13:19:56
6.1K00
代码可运行
举报
文章被收录于专栏:txp玩Linuxtxp玩Linux
运行总次数:0
代码可运行

零长度数组概念:

众所周知, GNU/GCC 在标准的 C/C++ 基础上做了有实用性的扩展, 零长度数组(Arrays of Length Zero) 就是其中一个知名的扩展.

多数情况下, 其应用在变长数组中, 其定义如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
struct Packet
{
    int state;
    int len;
    char cData[0]; //这里的0长结构体就为变长结构体提供了非常好的支持
};

首先对 0长度数组, 也叫柔性数组 做一个解释 :

  • 用途 : 长度为0的数组的主要用途是为了满足需要变长度的结构体
  • 用法 : 在一个结构体的最后, 申明一个长度为0的数组, 就可以使得这个结构体是可变长的. 对于编译器来说, 此时长度为0的数组并不占用空间, 因为数组名本身不占空间, 它只是一个偏移量, 数组名这个符号本身代表了一个不可修改的地址常量

(注意 : 数组名永远都不会是指针!), 但对于这个数组的大小, 我们可以进行动态分配

注意 :如果结构体是通过calloc、malloc或 者new等动态分配方式生成,在不需要时要释放相应的空间。

优点 :比起在结构体中声明一个指针变量、再进行动态分 配的办法,这种方法效率要高。因为在访问数组内容时,不需要间接访问,避免了两次访存。

缺点 :在结构体中,数组为0的数组必须在最后声明,使 用上有一定限制。

对于编译器而言, 数组名仅仅是一个符号, 它不会占用任何空间, 它在结构体中, 只是代表了一个偏移量, 代表一个不可修改的地址常量!

0长度数组的用途:

我们设想这样一个场景, 我们在网络通信过程中使用的数据缓冲区, 缓冲区包括一个len字段和data字段, 分别标识数据的长度和传输的数据, 我们常见的有几种设计思路:

  • 定长数据缓冲区, 设置一个足够大小 MAX_LENGTH 的数据缓冲区
  • 设置一个指向实际数据的指针, 每次使用时, 按照数据的长度动态的开辟数据缓冲区的空间

我们从实际场景中应用的设计来考虑他们的优劣. 主要考虑的有, 缓冲区空间的开辟, 释放和访问。

1、定长包(开辟空间, 释放, 访问):

比如我要发送 1024 字节的数据, 如果用定长包, 假设定长包的长度 MAX_LENGTH 为 2048, 就会浪费 1024 个字节的空间, 也会造成不必要的流量浪费:

  • 数据结构定义:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//  定长缓冲区
struct max_buffer
{
    int     len;
    char    data[MAX_LENGTH];
};
  • 数据结构大小:考虑对齐, 那么数据结构的大小 >= sizeof(int) + sizeof(char) * MAX_LENGTH

由于考虑到数据的溢出, 变长数据包中的 data 数组长度一般会设置得足够长足以容纳最大的数据, 因此 max_buffer 中的 data 数组很多情况下都没有填满数据, 因此造成了浪费

  • 数据包的构造:假如我们要发送 CURR_LENGTH = 1024 个字节, 我们如何构造这个数据包呢;一般来说, 我们会返回一个指向缓冲区数据结构 max_buffer 的指针:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    ///  开辟
    if ((mbuffer = (struct max_buffer *)malloc(sizeof(struct max_buffer))) != NULL)
    {
        mbuffer->len = CURR_LENGTH;
        memcpy(mbuffer->data, "Hello World", CURR_LENGTH);


        printf("%d, %s\n", mbuffer->len, mbuffer->data);
    }
  • 访问:这段内存要分两部分使用;前部分 4 个字节 p->len, 作为包头(就是多出来的那部分),这个包头是用来描述紧接着包头后面的数据部分的长度,这里是 1024, 所以前四个字节赋值为 1024 (既然我们要构造不定长数据包,那么这个包到底有多长呢,因此,我们就必须通过一个变量来表明这个数据包的长度,这就是len的作用);而紧接其后的内存是真正的数据部分, 通过 p->data, 最后, 进行一个 memcpy() 内存拷贝, 把要发送的数据填入到这段内存当中
  • 释放:那么当使用完毕释放数据的空间的时候, 直接释放就可以了
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    /// 销毁
    free(mbuffer);
    mbuffer = NULL;

2、小结:

  • 使用定长数组, 作为数据缓冲区, 为了避免造成缓冲区溢出, 数组的大小一般设为足够的空间 MAX_LENGTH, 而实际使用过程中, 达到 MAX_LENGTH 长度的数据很少, 那么多数情况下, 缓冲区的大部分空间都是浪费掉的
  • 但是使用过程很简单, 数据空间的开辟和释放简单, 无需程序员考虑额外的操作

3、 指针数据包(开辟空间, 释放, 访问):

如果你将上面的长度为 MAX_LENGTH 的定长数组换为指针, 每次使用时动态的开辟 CURR_LENGTH 大小的空间, 那么就避免造成 MAX_LENGTH - CURR_LENGTH 空间的浪费, 只浪费了一个指针域的空间:

  • 数据包定义:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
struct point_buffer
{
    int     len;
    char    *data;
};
  • 数据结构大小:考虑对齐, 那么数据结构的大小 >= sizeof(int) + sizeof(char *)
  • 空间分配:但是也造成了使用在分配内存时,需采用两步
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    // =====================
    // 指针数组  占用-开辟-销毁
    // =====================
    ///  占用
    printf("the length of struct test3:%d\n",sizeof(struct point_buffer));
    ///  开辟
    if ((pbuffer = (struct point_buffer *)malloc(sizeof(struct point_buffer))) != NULL)
    {
        pbuffer->len = CURR_LENGTH;
        if ((pbuffer->data = (char *)malloc(sizeof(char) * CURR_LENGTH)) != NULL)
        {
            memcpy(pbuffer->data, "Hello World", CURR_LENGTH);


            printf("%d, %s\n", pbuffer->len, pbuffer->data);
        }
    }

首先, 需为结构体分配一块内存空间;其次再为结构体中的成员变量分配内存空间。

这样两次分配的内存是不连续的, 需要分别对其进行管理. 当使用长度为的数组时, 则是采用一次分配的原则, 一次性将所需的内存全部分配给它。

  • 释放:相反, 释放时也是一样的:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    /// 销毁
    free(pbuffer->data);
    free(pbuffer);
    pbuffer = NULL;
  • 小结:
    • 使用指针结果作为缓冲区, 只多使用了一个指针大小的空间, 无需使用 MAX_LENGTH 长度的数组, 不会造成空间的大量浪费
    • 但那是开辟空间时, 需要额外开辟数据域的空间, 施放时候也需要显示释放数据域的空间, 但是实际使用过程中, 往往在函数中开辟空间, 然后返回给使用者指向 struct point_buffer 的指针, 这时候我们并不能假定使用者了解我们开辟的细节, 并按照约定的操作释放空间, 因此使用起来多有不便, 甚至造成内存泄漏。

4、变长数据缓冲区(开辟空间, 释放, 访问)

定长数组使用方便, 但是却浪费空间, 指针形式只多使用了一个指针的空间, 不会造成大量空间分浪费, 但是使用起来需要多次分配, 多次释放, 那么有没有一种实现方式能够既不浪费空间, 又使用方便的呢?

GNU C 的0长度数组, 也叫变长数组, 柔性数组就是这样一个扩展. 对于0长数组的这个特点,很容易构造出变成结构体,如缓冲区,数据包等等:

  • 数据结构定义:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//  0长度数组
struct zero_buffer
{
    int     len;
    char    data[0];
};
  • 数据结构大小:这样的变长数组常用于网络通信中构造不定长数据包, 不会浪费空间浪费网络流量, 因为char data[0]; 只是个数组名, 是不占用存储空间的:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
sizeof(struct zero_buffer) = sizeof(int)
  • 开辟空间:那么我们使用的时候, 只需要开辟一次空间即可
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    ///  开辟
    if ((zbuffer = (struct zero_buffer *)malloc(sizeof(struct zero_buffer) + sizeof(char) * CURR_LENGTH)) != NULL)
    {
        zbuffer->len = CURR_LENGTH;
        memcpy(zbuffer->data, "Hello World", CURR_LENGTH);


        printf("%d, %s\n", zbuffer->len, zbuffer->data);
    }
  • 释放空间:释放空间也是一样的, 一次释放即可
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    ///  销毁
    free(zbuffer);
    zbuffer = NULL;
  • 总结:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// zero_length_array.c
#include <stdio.h>
#include <stdlib.h>


#define MAX_LENGTH      1024
#define CURR_LENGTH      512

//  0长度数组
struct zero_buffer
{
    int     len;
    char    data[0];
}__attribute((packed));


//  定长数组
struct max_buffer
{
    int     len;
    char    data[MAX_LENGTH];
}__attribute((packed));


//  指针数组
struct point_buffer
{
    int     len;
    char    *data;
}__attribute((packed));

int main(void)
{
    struct zero_buffer  *zbuffer = NULL;
    struct max_buffer   *mbuffer = NULL;
    struct point_buffer *pbuffer = NULL;


    // =====================
    // 0长度数组  占用-开辟-销毁
    // =====================
    ///  占用
    printf("the length of struct test1:%d\n",sizeof(struct zero_buffer));
    ///  开辟
    if ((zbuffer = (struct zero_buffer *)malloc(sizeof(struct zero_buffer) + sizeof(char) * CURR_LENGTH)) != NULL)
    {
        zbuffer->len = CURR_LENGTH;
        memcpy(zbuffer->data, "Hello World", CURR_LENGTH);


        printf("%d, %s\n", zbuffer->len, zbuffer->data);
    }
    ///  销毁
    free(zbuffer);
    zbuffer = NULL;


    // =====================
    // 定长数组  占用-开辟-销毁
    // =====================
    ///  占用
    printf("the length of struct test2:%d\n",sizeof(struct max_buffer));
    ///  开辟
    if ((mbuffer = (struct max_buffer *)malloc(sizeof(struct max_buffer))) != NULL)
    {
        mbuffer->len = CURR_LENGTH;
        memcpy(mbuffer->data, "Hello World", CURR_LENGTH);


        printf("%d, %s\n", mbuffer->len, mbuffer->data);
    }
    /// 销毁
    free(mbuffer);
    mbuffer = NULL;

    // =====================
    // 指针数组  占用-开辟-销毁
    // =====================
    ///  占用
    printf("the length of struct test3:%d\n",sizeof(struct point_buffer));
    ///  开辟
    if ((pbuffer = (struct point_buffer *)malloc(sizeof(struct point_buffer))) != NULL)
    {
        pbuffer->len = CURR_LENGTH;
        if ((pbuffer->data = (char *)malloc(sizeof(char) * CURR_LENGTH)) != NULL)
        {
            memcpy(pbuffer->data, "Hello World", CURR_LENGTH);


            printf("%d, %s\n", pbuffer->len, pbuffer->data);
        }
    }
    /// 销毁
    free(pbuffer->data);
    free(pbuffer);
    pbuffer = NULL;


    return EXIT_SUCCESS;
}

GNU Document中 变长数组的支持:

参考:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
6.17 Arrays of Length Zero

C Struct Hack – Structure with variable length array

在 C90 之前, 并不支持0长度的数组, 0长度数组是 GNU C 的一个扩展, 因此早期的编译器中是无法通过编译的;对于 GNU C 增加的扩展, GCC 提供了编译选项来明确的标识出他们:

  • -pedantic 选项,那么使用了扩展语法的地方将产生相应的警告信息
  • -Wall 使用它能够使GCC产生尽可能多的警告信息
  • -Werror, 它要求GCC将所有的警告当成错误进行处理
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 1.c
#include <stdio.h>
#include <stdlib.h>


int main(void)
{
    char a[0];
    printf("%ld", sizeof(a));
    return EXIT_SUCCESS;
}

我们来编译:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
gcc 1.c -Wall   # 显示所有警告
#none warning and error

gcc 1.c -Wall -pedantic  # 对GNU C的扩展显示警告
1.c: In function ‘main’:
1.c:7: warning: ISO C forbids zero-size array ‘a’


gcc 1.c -Werror -Wall -pedantic # 显示所有警告同时GNU C的扩展显示警告, 将警告用error显示
cc1: warnings being treated as errors
1.c: In function ‘main’:
1.c:7: error: ISO C forbids zero-size array ‘a’

0长度数组其实就是灵活的运用的数组指向的是其后面的连续的内存空间:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
struct buffer
{
    int     len;
    char    data[0];
};

在早期没引入0长度数组的时候, 大家是通过定长数组和指针的方式来解决的, 但是:

  • 定长数组定义了一个足够大的缓冲区, 这样使用方便, 但是每次都造成空间的浪费
  • 指针的方式, 要求程序员在释放空间是必须进行多次的free操作, 而我们在使用的过程中往往在函数中返回了指向缓冲区的指针, 我们并不能保证每个人都理解并遵从我们的释放方式

所以 GNU 就对其进行了0长度数组的扩展. 当使用data[0]的时候, 也就是0长度数组的时候,0长度数组作为数组名, 并不占用存储空间.

在C99之后,也加了类似的扩展,只不过用的是 char payload[]这种形式(所以如果你在编译的时候确实需要用到-pedantic参数,那么你可以将char payload[0]类型改成char payload[], 这样就可以编译通过了,当然你的编译器必须支持C99标准的,如果太古老的编译器,那可能不支持了)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 2.c payload
#include <stdio.h>
#include <stdlib.h>

struct payload
{
    int   len;
    char  data[];
};

int main(void)
{
    struct payload pay;
    printf("%ld", sizeof(pay));
    return EXIT_SUCCESS;
}

使用 -pedantic 编译后, 不出现警告, 说明这种语法是 C 标准的

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
gcc 2.c -pedantic -std=c99

所以结构体的末尾, 就是指向了其后面的内存数据。因此我们可以很好的将该类型的结构体作为数据报文的头格式,并且最后一个成员变量,也就刚好是数据内容了.

GNU手册还提供了另外两个结构体来说明,更容易看懂意思:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
struct f1 {
    int x;
    int y[];
} f1 = { 1, { 2, 3, 4 } };

struct f2 {
    struct f1 f1;
    int data[3];
} f2 = { { 1 }, { 5, 6, 7 } };

我把f2里面的2,3,4改成了5,6,7以示区分。如果你把数据打出来。即如下的信息:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
f1.x = 1
f1.y[0] = 2
f1.y[1] = 3
f1.y[2] = 4

也就是f1.y指向的是{2,3,4}这块内存中的数据。所以我们就可以轻易的得到,f2.f1.y指向的数据也就是正好f2.data的内容了。打印出来的数据:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
f2.f1.x = 1
f2.f1.y[0] = 5
f2.f1.y[1] = 6
f2.f1.y[2] = 7

如果你不是很确认其是否占用空间. 你可以用sizeof来计算一下。就可以知道sizeof(struct f1)=4,也就是int y[]其实是不占用空间的。但是这个0长度的数组,必须放在结构体的末尾。如果你没有把它放在末尾的话。编译的时候,会有如下的错误:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
main.c:37:9: error: flexible array member not at end of struct
     int y[];
         ^

到这边,你可能会有疑问,如果将struct f1中的int y[]替换成int *y,又会是如何?这就涉及到数组和指针的问题了. 有时候吧,这两个是一样的,有时候又有区别。

首先要说明的是,支持0长度数组的扩展,重点在数组,也就是不能用int *y指针来替换。sizeof的长度就不一样了。把struct f1改成这样:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
struct f3 {
    int x;
    int *y;
};

在32/64位下, int均是4个字节, sizeof(struct f1)=4,而sizeof(struct f3)=16

因为 int *y 是指针, 指针在64位下, 是64位的, sizeof(struct f3) = 16, 如果在32位环境的话, sizeof(struct f3) 则是 8 了, sizeof(struct f1) 不变. 所以 int *y 是不能替代 int y[] 的;

代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 3.c
#include <stdio.h>
#include <stdlib.h>


struct f1 {
    int x;
    int y[];
} f1 = { 1, { 2, 3, 4 } };

struct f2 {
    struct f1 f1;
    int data[3];
} f2 = { { 1 }, { 5, 6, 7 } };


struct f3
{
    int x;
    int *y;
};

int main(void)
{
    printf("sizeof(f1) = %d\n", sizeof(struct f1));
    printf("sizeof(f2) = %d\n", sizeof(struct f2));
    printf("szieof(f3) = %d\n\n", sizeof(struct f3));

    printf("f1.x = %d\n", f1.x);
    printf("f1.y[0] = %d\n", f1.y[0]);
    printf("f1.y[1] = %d\n", f1.y[1]);
    printf("f1.y[2] = %d\n", f1.y[2]);


    printf("f2.f1.x = %d\n", f1.x);
    printf("f2.f1.y[0] = %d\n", f2.f1.y[0]);
    printf("f2.f1.y[1] = %d\n", f2.f1.y[1]);
    printf("f2.f1.y[2] = %d\n", f2.f1.y[2]);

    return EXIT_SUCCESS;
}

0长度数组的其他特征:

1、为什么0长度数组不占用存储空间:

0长度数组与指针实现有什么区别呢, 为什么0长度数组不占用存储空间呢?

其实本质上涉及到的是一个C语言里面的数组和指针的区别问题. char a[1]里面的a和char *b的b相同吗?

《 Programming Abstractions in C》(Roberts, E. S.,机械工业出版社,2004.6)82页里面说:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
“arr is defined to be identical to &arr[0].

也就是说,char a[1]里面的a实际是一个常量,等于&a[0]。而char *b是有一个实实在在的指针变量b存在。所以,a=b是不允许的,而b=a是允许的。两种变量都支持下标式的访问,那么对于a[0]和b[0]本质上是否有区别?我们可以通过一个例子来说明。

参见如下两个程序 gdb_zero_length_array.c 和 gdb_zero_length_array.c:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//  gdb_zero_length_array.c
#include <stdio.h>
#include <stdlib.h>

struct str
{
    int len;
    char s[0];
};

struct foo
{
    struct str *a;
};

int main(void)
{
    struct foo f = { NULL };

    printf("sizeof(struct str) = %d\n", sizeof(struct str));

    printf("before f.a->s.\n");
    if(f.a->s)
    {
        printf("before printf f.a->s.\n");
        printf(f.a->s);
        printf("before printf f.a->s.\n");
    }

    return EXIT_SUCCESS;
}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//  gdb_pzero_length_array.c
#include <stdio.h>
#include <stdlib.h>

struct str
{
    int len;
    char *s;
};

struct foo
{
    struct str *a;
};

int main(void)
{
    struct foo f = { NULL };

    printf("sizeof(struct str) = %d\n", sizeof(struct str));

    printf("before f.a->s.\n");

    if (f.a->s)
    {
        printf("before printf f.a->s.\n");
        printf(f.a->s);
        printf("before printf f.a->s.\n");
    }

    return EXIT_SUCCESS;
}

可以看到这两个程序虽然都存在访问异常, 但是段错误的位置却不同

我们将两个程序编译成汇编, 然户 diff 查看他们的汇编代码有何不同

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
gcc -S gdb_zero_length_array.c -o gdb_test.s
gcc -S gdb_pzero_length_array.c -o gdb_ptest
diff gdb_test.s gdb_ptest.s

1c1
<   .file   "gdb_zero_length_array.c"
---
>   .file   "gdb_pzero_length_array.c"
23c23
<   movl    $4, %esi
---
>   movl    $16, %esi
30c30
<   addq    $4, %rax
---
>   movq    8(%rax), %rax
36c36
<   addq    $4, %rax
---
>   movq    8(%rax), %rax
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#    printf("sizeof(struct str) = %d\n", sizeof(struct str));
23c23
<   movl    $4, %esi    #printf("sizeof(struct str) = %d\n", sizeof(struct str));
---
>   movl    $16, %esi  #printf("sizeof(struct str) = %d\n", sizeof(struct str));

从64位系统中, 汇编我们看出, 变长数组结构的大小为4, 而指针形式的结构大小为16:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
f.a->s
30c30/36c36
<   addq    $4, %rax
---
>   movq    8(%rax), %rax

可以看到有:

  • 对于 char s[0] 来说, 汇编代码用了 addq 指令, addq $4, %rax
  • 对于 char*s 来说,汇编代码用了 movq 指令, movq 8(%rax), %rax

addq 对 %rax + sizeof(struct str), 即str结构的末尾即是char s[0]的地址, 这一步只是拿到了其地址, 而 movq 则是把地址里的内容放进去, 因此有时也被翻译为leap指令, 参见下一列子

从这里可以看到, 访问成员数组名其实得到的是数组的相对地址, 而访问成员指针其实是相对地址里的内容(这和访问其它非指针或数组的变量是一样的):

  • 访问相对地址,程序不会crash,但是,访问一个非法的地址中的内容,程序就会crash。
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 4-1.c
#include <stdio.h>
#include <stdlib.h>

int main(void)
{

    char *a;
    printf("%p\n", a);

    return EXIT_SUCCESS;
}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
4-2.c
#include <stdio.h>
#include <stdlib.h>

int main(void)
{

    char a[0];
    printf("%p\n", a);

    return EXIT_SUCCESS;
}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ diff 4-1.s 4-2.s
1c1
<       .file   "4-1.c"
---
>       .file   "4-2.c"
13c13
<       subl    $16, %esp
---
>       subl    $32, %esp
15c15
<       leal    16(%esp), %eax
---
>       movl    28(%esp), %eax
  • 对于 char a[0] 来说, 汇编代码用了 leal 指令, leal 16(%esp), %eax:
  • 对于 char *a 来说,汇编代码用了 movl 指令, movl 28(%esp), %eax

2、地址优化:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 5-1.c
#include <stdio.h>
#include <stdlib.h>

int main(void)
{

    char a[0];
    printf("%p\n", a);

    char b[0];
    printf("%p\n", b);

    return EXIT_SUCCESS;
}

由于0长度数组是 GNU C 的扩展, 不被标准库任可, 那么一些巧妙编写的诡异代码, 其执行结果就是依赖于编译器和优化策略的实现的.

比如上面的代码, a和b的地址就会被编译器优化到一处, 因为a[0] 和 b[0] 对于程序来说是无法使用的, 这让我们想到了什么?

编译器对于相同字符串常量, 往往地址也是优化到一处, 减少空间占用:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//  5-2.c
#include <stdio.h>
#include <stdlib.h>

int main(void)
{

    const char *a = "Hello";
    printf("%p\n", a);

    const char *b = "Hello";
    printf("%p\n", b);

    const char c[] = "Hello";
    printf("%p\n", c);

    return EXIT_SUCCESS;
}

文章参考:https://kernel.blog.csdn.net/article/details/64131322

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-04-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 txp玩Linux 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
学生家乡网页设计作品静态HTML网页—— HTML+CSS+JavaScript制作辽宁沈阳家乡主题网页源码(11页)
家乡旅游景点网页作业制作 网页代码运用了DIV盒子的使用方法,如盒子的嵌套、浮动、margin、border、background等属性的使用,外部大盒子设定居中,内部左中右布局,下方横向浮动排列,大学学习的前端知识点和布局方式都有运用,CSS的代码量也很足、很细致,使用hover来完成过渡效果、鼠标滑过效果等,使用表格、表单补充模块,为方便新手学习页面中没有使用js有需要的可以自行添加。 <font color='#b44846' size='4px'> ❤</font> 【作者主页——🔥获取更多优质
IT司马青衫
2022/08/24
1.5K0
学生家乡网页设计作品静态HTML网页—— HTML+CSS+JavaScript制作辽宁沈阳家乡主题网页源码(11页)
简单学校网页设计作业 静态HTML校园博客主页 DW大学网站模板下载 大学生简单我的学校网页作品代码 个人网页制作 学生个人网页设计作业
✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 💂 作者主页: 【主页——🚀获取更多优质源码】 🎓 web前端期末大作业: 【📚毕设项目精品实战案例 (1000套) 】 🧡 程序员有趣的告白方式:【💌HTML七夕情人节表白网页制作 (110套) 】 🌎超炫酷的Echarts大屏可视化源码:【🔰 echarts大屏展示大数据平台可视化(150套) 】 🎁 免费且实用的WEB前端学习指南: 【📂web前端零基础到高级学习视频教程 120G干货分享】 🥇 关于作者: 历任研发工程师
IT司马青衫
2022/08/18
8070
简单学校网页设计作业 静态HTML校园博客主页 DW大学网站模板下载 大学生简单我的学校网页作品代码 个人网页制作 学生个人网页设计作业
关于城市旅游的HTML网页设计——中国旅游HTML+CSS+JavaScript 出游旅游主题度假酒店 计划出行网站设计
👨‍🎓静态网站的编写主要是用 HTML DⅣV+ CSSJS等来完成页面的排版设计👩‍🎓,一般的网页作业需要融入以下知识点:div布局、浮动定位、高级css、表格、表单及验证、js轮播图、音频视频Fash的应用、uli、下拉导航栏、鼠标划过效果等知识点,学生网页作业源码,制作水平和原创度都适合学习或交作业用,记得点赞。 💝 【作者主页——🔥获取更多优质源码】 💝 【web前端期末大作业——🔥🔥毕设项目精品实战案例(1000套)】 一、网站题目👨‍🎓 🚀 旅游景点介绍、旅游风景区、家乡介绍、等网站的设计与制
IT司马青衫
2022/08/15
1.7K0
关于城市旅游的HTML网页设计——中国旅游HTML+CSS+JavaScript 出游旅游主题度假酒店 计划出行网站设计
学生家乡网页设计作品静态HTML网页模板源码 广西旅游景点网页设计 大学生家乡主题网站制作 简单家乡介绍网页设计成品
家乡旅游景点网页作业制作 网页代码运用了DIV盒子的使用方法,如盒子的嵌套、浮动、margin、border、background等属性的使用,外部大盒子设定居中,内部左中右布局,下方横向浮动排列,大学学习的前端知识点和布局方式都有运用,CSS的代码量也很足、很细致,使用hover来完成过渡效果、鼠标滑过效果等,使用表格、表单补充模块,为方便新手学习页面中没有使用js有需要的可以自行添加。 <font color='#b44846' size='4px'> ❤</font> 【作者主页——🔥获取更多优质
IT司马青衫
2022/08/24
1.4K0
学生家乡网页设计作品静态HTML网页模板源码 广西旅游景点网页设计 大学生家乡主题网站制作 简单家乡介绍网页设计成品
校园网页设计成品 学校班级网页制作模板 dreamweaver网页作业 简单网页课程成品 大学生静态HTML网页源码
✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 💂 作者主页: 【主页——🚀获取更多优质源码】 🎓 web前端期末大作业: 【📚毕设项目精品实战案例 (1000套) 】 🧡 程序员有趣的告白方式:【💌HTML七夕情人节表白网页制作 (110套) 】 🌎超炫酷的Echarts大屏可视化源码:【🔰 echarts大屏展示大数据平台可视化(150套) 】 🎁 免费且实用的WEB前端学习指南: 【📂web前端零基础到高级学习视频教程 120G干货分享】 🥇 关于作者
IT司马青衫
2022/08/24
5.3K0
校园网页设计成品 学校班级网页制作模板 dreamweaver网页作业 简单网页课程成品 大学生静态HTML网页源码
个人网页设计成品DW静态网页 HTML网页设计结课作业 web课程设计网页规划与设计 Web大学生个人网页成品 web网页设计期末课程大作业
✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 💂 作者主页: 【主页——🚀获取更多优质源码】 🎓 web前端期末大作业: 【📚毕设项目精品实战案例 (1000套) 】 🧡 程序员有趣的告白方式:【💌HTML七夕情人节表白网页制作 (110套) 】 🌎超炫酷的Echarts大屏可视化源码:【🔰 echarts大屏展示大数据平台可视化(150套) 】 🎁 免费且实用的WEB前端学习指南: 【📂web前端零基础到高级学习视频教程 120G干货分享】 🥇 关于作者: 历任研发工程师,技
IT司马青衫
2022/08/15
1.8K0
个人网页设计成品DW静态网页 HTML网页设计结课作业 web课程设计网页规划与设计 Web大学生个人网页成品 web网页设计期末课程大作业
学生网页设计作品 dreamweaver作业静态HTML网页设计模板 美食文化网页作业制作
✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 💂 作者主页: 【主页——🚀获取更多优质源码】 🎓 web前端期末大作业: 【📚毕设项目精品实战案例 (1000套) 】 🧡 程序员有趣的告白方式:【💌HTML七夕情人节表白网页制作 (110套) 】 🌎超炫酷的Echarts大屏可视化源码:【🔰 echarts大屏展示大数据平台可视化(150套) 】 🎁 免费且实用的WEB前端学习指南: 【📂web前端零基础到高级学习视频教程 120G干货分享】 🥇 关于作
IT司马青衫
2022/08/24
2.5K0
学生网页设计作品 dreamweaver作业静态HTML网页设计模板 美食文化网页作业制作
DIV布局个人介绍网页模板代码 家乡海阳个人简介网页制作 简单个人静态HTML网页设计作品 DW个人网站制作成品 web网页制作与实现
✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 💂 作者主页: 【主页——🚀获取更多优质源码】 🎓 web前端期末大作业: 【📚毕设项目精品实战案例 (1000套) 】 🧡 程序员有趣的告白方式:【💌HTML七夕情人节表白网页制作 (110套) 】 🌎超炫酷的Echarts大屏可视化源码:【🔰 echarts大屏展示大数据平台可视化(150套) 】 🎁 免费且实用的WEB前端学习指南: 【📂web前端零基础到高级学习视频教程 120G干货分享】 🥇 关于作者: 历任研发工程师
IT司马青衫
2022/08/28
2K0
大一Web课程设计 基于HTML家乡主题网页项目的设计与实现——中国牡丹之都山东菏泽(6页)
家乡旅游景点网页作业制作 网页代码运用了DIV盒子的使用方法,如盒子的嵌套、浮动、margin、border、background等属性的使用,外部大盒子设定居中,内部左中右布局,下方横向浮动排列,大学学习的前端知识点和布局方式都有运用,CSS的代码量也很足、很细致,使用hover来完成过渡效果、鼠标滑过效果等,使用表格、表单补充模块,为方便新手学习页面中没有使用js有需要的可以自行添加。 --- @TOC 一、网页介绍📖 1 网页简介:此作品为学生个人主页网页设计题材,HTML+CSS 布局制作,web前
IT司马青衫
2022/08/14
1810
大一Web课程设计 基于HTML家乡主题网页项目的设计与实现——中国牡丹之都山东菏泽(6页)
基于HTML+CSS+JavaScript制作简单的大学生网页设计——我的家乡湖南
家乡旅游景点网页作业制作 网页代码运用了DIV盒子的使用方法,如盒子的嵌套、浮动、margin、border、background等属性的使用,外部大盒子设定居中,内部左中右布局,下方横向浮动排列,大学学习的前端知识点和布局方式都有运用,CSS的代码量也很足、很细致,使用hover来完成过渡效果、鼠标滑过效果等,使用表格、表单补充模块,为方便新手学习页面中没有使用js有需要的可以自行添加。 <font color='#b44846' size='4px'> ❤</font> 【作者主页——🔥获取更多优质
IT司马青衫
2022/08/16
8860
基于HTML+CSS+JavaScript制作简单的大学生网页设计——我的家乡湖南
HTML+CSS美食静态网页设计
1、首先,新建一个文件,名字可以随便取,我这里的文件名叫:爱尚美食网页。在 爱尚美食网页 文件夹里面还需要有一个 css文件夹,一个images文件夹,和一个index.html文件。如下图所示:
全栈程序员站长
2022/11/09
3.2K0
HTML+CSS美食静态网页设计
高仿英雄联盟游戏网页制作作业 英雄联盟LOL游戏HTML网页设计模板 简单学生网页设计 静态HTML CSS网站制作成品
✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 💂 作者主页: 【主页——🚀获取更多优质源码】 🎓 web前端期末大作业: 【📚毕设项目精品实战案例 (1000套) 】 🧡 程序员有趣的告白方式:【💌HTML七夕情人节表白网页制作 (110套) 】 🌎超炫酷的Echarts大屏可视化源码:【🔰 echarts大屏展示大数据平台可视化(150套) 】 🎁 免费且实用的WEB前端学习指南: 【📂web前端零基础到高级学习视频教程 120G干货分享】 🥇 关于作者: 历任研发
IT司马青衫
2022/08/14
1.1K0
高仿英雄联盟游戏网页制作作业 英雄联盟LOL游戏HTML网页设计模板 简单学生网页设计 静态HTML CSS网站制作成品
个人简介网页设计作业 静态HTML个人介绍网页作业 DW个人网站模板下载 WEB静态大学生简单网页 个人网页作品代码 个人网页制作 学生个人网页
✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 💂 作者主页: 【主页——🚀获取更多优质源码】 🎓 web前端期末大作业: 【📚毕设项目精品实战案例 (1000套) 】 🧡 程序员有趣的告白方式:【💌HTML七夕情人节表白网页制作 (110套) 】 🌎超炫酷的Echarts大屏可视化源码:【🔰 echarts大屏展示大数据平台可视化(150套) 】 🎁 免费且实用的WEB前端学习指南: 【📂web前端零基础到高级学习视频教程 120G干货分享】 🥇 关于作者:
IT司马青衫
2022/08/14
4.9K0
个人简介网页设计作业 静态HTML个人介绍网页作业 DW个人网站模板下载 WEB静态大学生简单网页 个人网页作品代码 个人网页制作 学生个人网页
开心消消乐游戏网页设计作品 学生dreamweaver作业静态HTML网页设计模板 游戏主题网页作业制作
✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 💂 作者主页: 【主页——🚀获取更多优质源码】 🎓 web前端期末大作业: 【📚毕设项目精品实战案例 (1000套) 】 🧡 程序员有趣的告白方式:【💌HTML七夕情人节表白网页制作 (110套) 】 🌎超炫酷的Echarts大屏可视化源码:【🔰 echarts大屏展示大数据平台可视化(150套) 】 🎁 免费且实用的WEB前端学习指南: 【📂web前端零基础到高级学习视频教程 120G干货分享】 🥇 关于作者: 历任研发工程师
IT司马青衫
2022/08/19
2.6K0
开心消消乐游戏网页设计作品 学生dreamweaver作业静态HTML网页设计模板 游戏主题网页作业制作
大一学生网页课程作业 南京介绍网页设计 学生家乡网页设计作品静态 HTML网页模板源码 html我的家乡网页作业
家乡旅游景点网页作业制作 网页代码运用了DIV盒子的使用方法,如盒子的嵌套、浮动、margin、border、background等属性的使用,外部大盒子设定居中,内部左中右布局,下方横向浮动排列,大学学习的前端知识点和布局方式都有运用,CSS的代码量也很足、很细致,使用hover来完成过渡效果、鼠标滑过效果等,使用表格、表单补充模块,为方便新手学习页面中没有使用js有需要的可以自行添加。 --- @TOC 一、网页介绍📖 1 网页简介:此作品为学生个人主页网页设计题材,HTML+CSS 布局制作,web前
IT司马青衫
2022/08/14
2.1K0
大一学生网页课程作业 南京介绍网页设计 学生家乡网页设计作品静态 HTML网页模板源码 html我的家乡网页作业
仿英雄联盟网页HTML代码 学生网页设计与制作期末作业下载 大学生网页设计与制作成品下载 DW游戏介绍网页作业代码下载
✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 💂 作者主页: 【主页——🚀获取更多优质源码】 🎓 web前端期末大作业: 【📚毕设项目精品实战案例 (1000套) 】 🧡 程序员有趣的告白方式:【💌HTML七夕情人节表白网页制作 (110套) 】 🌎超炫酷的Echarts大屏可视化源码:【🔰 echarts大屏展示大数据平台可视化(150套) 】 🎁 免费且实用的WEB前端学习指南: 【📂web前端零基础到高级学习视频教程 120G干货分享】 🥇 关于作者: 历任研发
IT司马青衫
2022/08/14
1.1K0
仿英雄联盟网页HTML代码 学生网页设计与制作期末作业下载 大学生网页设计与制作成品下载 DW游戏介绍网页作业代码下载
【网页制作课作业】用HTML+CSS制作一个简单的学校网页(9页)
✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 💂 作者主页: 【主页——🚀获取更多优质源码】 🎓 web前端期末大作业: 【📚毕设项目精品实战案例 (1000套) 】 🧡 程序员有趣的告白方式:【💌HTML七夕情人节表白网页制作 (110套) 】 🌎超炫酷的Echarts大屏可视化源码:【🔰 echarts大屏展示大数据平台可视化(150套) 】 🎁 免费且实用的WEB前端学习指南: 【📂web前端零基础到高级学习视频教程 120G干货分享】 🥇 关于作者: 历任研发工程师,技
IT司马青衫
2022/08/10
2K0
【网页制作课作业】用HTML+CSS制作一个简单的学校网页(9页)
《web课程设计》用HTML CSS做一个简洁、漂亮的个人博客网站
✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 💂 作者主页: 【主页——🚀获取更多优质源码】 🎓 web前端期末大作业: 【📚毕设项目精品实战案例 (1000套) 】 🧡 程序员有趣的告白方式:【💌HTML七夕情人节表白网页制作 (110套) 】 🌎超炫酷的Echarts大屏可视化源码:【🔰 echarts大屏展示大数据平台可视化(150套) 】 🎁 免费且实用的WEB前端学习指南: 【📂web前端零基础到高级学习视频教程 120G干货分享】 🥇 关于作者:
IT司马青衫
2022/08/10
1.2K0
基于HTML节日主题网页项目的设计与实现——圣诞节日介绍(HTML+CSS)
✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 💂 作者主页: 【主页——🚀获取更多优质源码】 🎓 web前端期末大作业: 【📚毕设项目精品实战案例 (1000套) 】 🧡 程序员有趣的告白方式:【💌HTML七夕情人节表白网页制作 (110套) 】 🌎超炫酷的Echarts大屏可视化源码:【🔰 echarts大屏展示大数据平台可视化(150套) 】 🎁 免费且实用的WEB前端学习指南: 【📂web前端零基础到高级学习视频教程 120G干货分享】 🥇 关于作者: 💬历任研发工程师
IT司马青衫
2022/08/17
9450
基于HTML节日主题网页项目的设计与实现——圣诞节日介绍(HTML+CSS)
html静态网页设计代码_静态网页设计心得
<!DOCTYPE html> <html> <head> <mate charset=”utf-8″/> <title>标题</title> </head> <body> 主要内容 </body> </html>
全栈程序员站长
2022/11/08
6.7K0
html静态网页设计代码_静态网页设计心得
推荐阅读
学生家乡网页设计作品静态HTML网页—— HTML+CSS+JavaScript制作辽宁沈阳家乡主题网页源码(11页)
1.5K0
简单学校网页设计作业 静态HTML校园博客主页 DW大学网站模板下载 大学生简单我的学校网页作品代码 个人网页制作 学生个人网页设计作业
8070
关于城市旅游的HTML网页设计——中国旅游HTML+CSS+JavaScript 出游旅游主题度假酒店 计划出行网站设计
1.7K0
学生家乡网页设计作品静态HTML网页模板源码 广西旅游景点网页设计 大学生家乡主题网站制作 简单家乡介绍网页设计成品
1.4K0
校园网页设计成品 学校班级网页制作模板 dreamweaver网页作业 简单网页课程成品 大学生静态HTML网页源码
5.3K0
个人网页设计成品DW静态网页 HTML网页设计结课作业 web课程设计网页规划与设计 Web大学生个人网页成品 web网页设计期末课程大作业
1.8K0
学生网页设计作品 dreamweaver作业静态HTML网页设计模板 美食文化网页作业制作
2.5K0
DIV布局个人介绍网页模板代码 家乡海阳个人简介网页制作 简单个人静态HTML网页设计作品 DW个人网站制作成品 web网页制作与实现
2K0
大一Web课程设计 基于HTML家乡主题网页项目的设计与实现——中国牡丹之都山东菏泽(6页)
1810
基于HTML+CSS+JavaScript制作简单的大学生网页设计——我的家乡湖南
8860
HTML+CSS美食静态网页设计
3.2K0
高仿英雄联盟游戏网页制作作业 英雄联盟LOL游戏HTML网页设计模板 简单学生网页设计 静态HTML CSS网站制作成品
1.1K0
个人简介网页设计作业 静态HTML个人介绍网页作业 DW个人网站模板下载 WEB静态大学生简单网页 个人网页作品代码 个人网页制作 学生个人网页
4.9K0
开心消消乐游戏网页设计作品 学生dreamweaver作业静态HTML网页设计模板 游戏主题网页作业制作
2.6K0
大一学生网页课程作业 南京介绍网页设计 学生家乡网页设计作品静态 HTML网页模板源码 html我的家乡网页作业
2.1K0
仿英雄联盟网页HTML代码 学生网页设计与制作期末作业下载 大学生网页设计与制作成品下载 DW游戏介绍网页作业代码下载
1.1K0
【网页制作课作业】用HTML+CSS制作一个简单的学校网页(9页)
2K0
《web课程设计》用HTML CSS做一个简洁、漂亮的个人博客网站
1.2K0
基于HTML节日主题网页项目的设计与实现——圣诞节日介绍(HTML+CSS)
9450
html静态网页设计代码_静态网页设计心得
6.7K0
相关推荐
学生家乡网页设计作品静态HTML网页—— HTML+CSS+JavaScript制作辽宁沈阳家乡主题网页源码(11页)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档