在C语言中,数组默认情况下是否是可变长度的?

4

我试图创建一个基于数组的线性表,然后编译了这个代码:

char size = 0;
char testarray[10];

int main() { 
    add('a'); add('b'); add('c');
    add('d'); add('e'); add('f');
    add('g'); add('h'); add('i');
    add('j'); add('k'); add('l');
    add('m'); add('n'); add('o');
    print();
    return 0;
} 

void add(char newchar) {
    testarray[++size] = newchar;
}

void print() {
    char i = 0;
    for (i = 0; i <= size; i++) {
        printf("%c ", testarray[i]);
    }
 }

我使用gcc arraytest.c编译它,但数组工作得很好。这是不是意味着数组默认是可变长度的?我以为这是C99才有的特性。

它在Gentoo (gcc version 4.5.3 (Gentoo 4.5.3-r2 p1.1, pie-0.4.7)和Ubuntu (gcc version 4.6.1 (Ubuntu/Linaro 4.6.1-9ubuntu3)下都编译通过。

哦,这不是有点危险吗?


1
请注意,在C语言中,“可变长度数组”仍具有静态大小 - 只是在每次调用函数时确定一个运行时大小(可能对于每个调用不同,但在调用期间始终保持不变,之后该数组超出范围)。 - Chris Lutz
4个回答

7
不,它们的大小是固定的。你只是在数组的末尾写入了一些数据并覆盖了其他内存。只是没有检查程序让这一点显而易见。

5
他正在引起未定义行为,这意味着任何事情都有可能发生,从看似正常工作到引发大气燃烧,再到以你的信用卡买披萨。 - Seth Carnegie
所以说这还是很危险的。哦,好的,谢谢。我想我有点太习惯于Java过度保护了。 - user1002327

5


把C语言看作是世界上最棒的汇编语言,而且它还是可移植的。这与被设计为内存安全的语言完全不同。(这将包括大多数其他语言。)

你可以通过使用cc -S ...编译并检查生成的汇编代码来回答这种问题(即"C到底在做什么")。 即使您不熟悉机器语言,您也可以大概知道它不会调用或检查下标范围。

C实际上永远无法真正做到这一点,因为x[i]被定义为*(x+i),所以它确确实实是一个像人们说的那样的"高级汇编语言"


正如有人在Lounge<C++>(有点儿玩笑地)所说:“C++拥有汇编语言的所有强大功能和汇编语言的所有便利性。” - Seth Carnegie

4
只要在应用程序的分配范围内,您可以随意使用指针/地址。如果一直走得太远,您要么破坏程序,要么到达分配的内存边缘并获得某种保护错误。运行时检查很昂贵,反正也不需要。
交换你的两个变量。
char testarray[10];
char size = 0;

运行这段代码并查看结果...

然后执行以下操作:

char size = 0;
char testarray[10];
char stuff[10];

在开始向testarray添加元素之前,先初始化变量,然后在完成操作后打印出stuff[]数组。你应该能够看到溢出情况。在C语言中,将非数组变量放在赋值列表的最前面,将数组或多维数组放在最后面是一个好习惯,这样有助于调试。


2
如 Vaugh 所说,你只是“幸运”的,因为数组后面的内存是可写的。存在段错误的风险。事实上,如果你稍微改变程序,让数组在堆栈上,然后通过 valgrind 运行它,它会向你报告大量警告。
* “幸运”是指程序没有崩溃。不幸的是……它以后会崩溃。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接