# 扩展 ## 静态数组和动态数组 在C语言中,数组一旦被定义后,占用的内存空间就是固定的,容量就是不可改变的,既不能在任何位置插入元素,也不能在任何位置删除元素,只能读取和修改元素,我们将这样的数组称为静态数组。 反过来说,如果数组在定义后可以改变容量,允许在任意位置插入或者删除元素,那么这样的数组称为动态数组。PHP、JavaScript 等解释型的脚本语言一般都支持动态数组,而 C、C++ 等编译型的语言一般不支持动态数组。总之,C语言中的数组是静态的,一旦定义后长度就不能改变了,大家要注意这一点,不要尝试去插入或删除元素。 如果由于项目要求,必须要在数组中插入或者删除元素,该怎么办呢?没办法,只能再造一个新数组! 不能插入和删除数组元素有时候会非常麻烦,比如一个数组保存了某个班级的学生学号,现在有一名学生退学了,就得把 TA 从数组中剔除,但是C语言并不支持这么做,这就给编程带来了不小的麻烦。数组元素都是紧挨着排布的,中间没有空隙,不管是插入元素还是删除元素,都得移动该元素后面的内存: - 在第 i 个元素后面插入一个新元素时,第 i 个元素后面的所有元素都要往后移动一个元素的位置,从而给新元素腾出位置来。如果该数组后面紧跟的是其它有用数据,那么为了防止覆盖有用数据,还不敢直接往后移动元素,必须得重新开辟一块内存,把所有的元素都复制过去。 - 删除第 i 个元素就比较简单了,不管三七二十一,把第 i 个元素后面的所有元素都向前移动即可。 插入和删除数组元素都要移动内存,甚至重新开辟一块内存,这是相当消耗资源的。如果一个程序中有大量的此类操作,那么程序的性能将堪忧,这有悖于「C语言非常高效」的初衷,所以C语言并不支持动态数组。 ## 数组越界 C语言数组是静态的,不能自动扩容,当下标小于零或大于等于数组长度时,就发生了越界(Out Of Bounds),访问到数组以外的内存。如果下标小于零,就会发生下限越界(Off Normal Lower);如果下标大于等于数组长度,就会发生上限越界(Off Normal Upper)。 C语言为了提高效率,保证操作的灵活性,并不会对越界行为进行检查,即使越界了,也能够正常编译,只有在运行期间才可能会发生问题。所以这一点需要格外注意。 越界访问的数组元素的值都是不确定的,没有实际的含义,因为数组之外的内存我们并不知道是什么,可能是其它变量的值,可能是函数参数,可能是一个地址,这些都是不可控的。 由于C语言的”放任“,我们访问数组时必须非常小心,要确保不会发生越界。每个C语言程序员的生涯中都遇到过越界错误,所以千万不要大意,因为越界错误有时候不容易发现,也不容易复现。 当发生数组越界时,如果我们对该内存有使用权限,那么程序将正常运行,但会出现不可控的结果;如果我们对该内存没有使用权限,或者该内存压根就没有被分配,那么程序将会崩溃。 ```c #include #include int main(int argc, char *argv[]) { int a[3]; printf("%d\n", a[2]); system("pause"); return 0; } ``` ## 数组溢出 当赋予数组的元素个数超过数组长度时,就会发生溢出(Overflow)。比如 ```c int a[3] = {1, 2, 3, 4, 5, 6, 7}; ``` 上面的a数组只能保存前3个元素,后面的元素被丢弃。 GCC、LLVM/Clang、低版本的 VS(例如 VS2010)发现数组溢出只会给出警告,并不会报错。我试了VS2017的编译器,仍然编译通过了,给出了相应的数组界限溢出警告。 在用字符串给字符数组赋值时,要保证数组长度大于字符串长度,以容纳结束符`'\0'`。