多年老c++程序员在静态数组这里翻船了

2021-06-10 10:27:54 浏览数 (1)

事情的起因

事情是这样子滴,有一次我在代码评审的时候,发现有同事想使用运行时才能够获取到的值,去改变一个静态数组的元素个数,我当时就很诧异,因为我心里知道这样是不可行的,静态数组的元素个数在编译时就需要是固定不变的,一般只能是常量或者宏定义,否则编译就不能通过。

但是当时我提出来以后,把原因说了,包括写出这个代码的人和另外一位同事都没理解,弄得我有点怀疑自己了,难道是我搞错了?

我左思右想,最后我写了下面的代码来证实一下:

代码语言:javascript复制
#include <iostream>
using namespace std;

int get()
{
    int size = 0;
    cout << "please input a num:";
    cin >> size;
    return size;
}

int main()
{
    int size = 1000;
    size = get();
    int arr[size] = {0};

    cout << "arr's size is " << sizeof(arr)/sizeof(arr[0]) << endl;

    return 0;
}

我打心底认为,这段程序肯定是编译不通过的,但是结果打脸了。编译后执行显示如下:

代码语言:javascript复制
please input a num:10000
arr's size is 10000

不仅编译通过了,而且数组大小还在运行时修改了,可是我明明定义的是一个静态数组呀,而且就算我这里不给size赋初始值1000,结果也还是一样的。

探索的过程

简直是见了鬼了,我把代码看了又看,认为是我代码写错了,但是这么简单的一段代码,我不可能写错呀。后来我突然想到,会不会是有了新的语法?

因为我现在的gcc编译器,大家都知道是7.1.0的版本,基本上连c 17都能支持了,我就试了一下之前保留的gcc4.1.2的版本,结果报错啦,如下:

代码语言:javascript复制
test.cpp: In function ‘int main()’:
test.cpp:15: 错误:可变大小的对象 ‘arr’ 不能被初始化

所以很显然,我的记忆没有错误,之前静态数组的元素个数它就必须是个不可变的,否则编译就会出错。

然后我又看了下生产上用的编译器,是gcc4.8.5的版本,它也是支持c 11的,难道这个新的特性是c 11支持的吗?然后我去cppreference把c 11的新特性从头到尾翻了一遍,并没有对这个修改有说明,并且网上搜索,所有的文章都在说c和c 想使用变长数组,就必须要使用动态数组,我一度陷入了僵局,但是我并没有死心,继续探索。

后来我忽然想到,假如不是c 的新特性,那是不是c语言的新特性呢,想到这里,我把代码做了如下修改:

代码语言:javascript复制
#include <stdio.h>

int get()
{
    int size = 0;
    printf("please input a num:");
    scanf("%d", &size);
    return size;
}

int main()
{
    int size = 1000;
    size = get();
    int arr[size] = {0};

    printf("arr's size is %dn", sizeof(arr)/sizeof(arr[0]));

    return 0;
}

然后直接使用gcc命令编译,而没有使用g ,结果跟上面那段代码是一模一样的,到这里我大致上就明白了,这就是c语言里面的新特性。

欣喜的找到了结果

最后我在cppreference这个链接里面看到了对于c99的说明:

  1. 新特性:Bool 、 long long 、 stdint.h 、 inttypes.h 、 restrict 、复合字面量、变长度数组、伸缩数组成员、指派初始化器、 fenv.h 、变参数宏、复数、 func 、十六进制浮点格式( %a )、 lconv 的货币格式化、 isblank 、窄与宽字符串字面量的连接、枚举的尾逗号、类函数宏的空参数、 STDC* pragma 、 va_copy 、 tmpnam 的空返回、 setvbuf 中的空指针、 printf 的 hh 与 ll 长度指定符、 snprintf 、 _Exit 、 tgmath.h 、仿 POSIX strftime 说明符
  2. 来自 C :inline 、声明与代码混合、 for 循环的 init 子句中的声明、 // 注释、源代码中的通用字符名
  3. 移除隐式函数声明和隐式 int

看看,是不是明确说明了新特性是变长度数组,并且是c语言99年的标准,有点灯下黑了,以后如果再看到有人说c语言和c 的静态数组都只支持固定长度,要想变长就必须要使用malloc和new,就可以唾弃一下啦。

另外有一点需要注意的是,如果我们的场景真的是需要使用固定大小的数组,那么最好使用常量和宏定义,再不然,就使用c 11里面新增加的array。

好了,有关我和静态数组的故事就介绍到这里了,因为没有留言功能,如果有问题需要咨询的,可以通过公众号菜单【联系作者】获取作者联系方式进行咨询哈。

0 人点赞