c99标准下数组新特性———可变长度数组与数组字面量(复合字面量)
一、可变长度数组(VLA)
一、什么是可变长度数组
误区澄清:“可变”指的是“定义时可变”,不是“定义后可扩容”
很多人听到“可变长度数组”,第一反应是“这个数组像气球一样,能随时吹大或缩小”。这是错的。
在C99标准中,可变长度数组(VLA)的“可变”仅仅是指:数组的长度可以由一个变量在运行时决定,而不必像传统C89那样必须用一个常量**(如 #define SIZE 10)在编译时决定。
-
C89(固定数组):长度必须是常量。
c
int arr[10]; // 正确,10是常量 int n = 5; int arr2[n]; // 错误!C89不允许变量做长度
-
C99(可变长度数组):长度可以是变量。
c
int n = 5; int arr[n]; // 正确!C99允许,这就是VLA // 此时arr的长度在运行时被确定为5,占用的内存大小就固定了
二、可变长度数组的作用和不确定性
c
int n = 5; int arr[n]; // 此时arr长度确定为5,占用20个字节(int占4字节) n = 100; // 哪怕把n改成100,arr的长度依然是5,不会变! // 如果你试图访问 arr[10],属于数组越界,程序会崩溃或出现野值。
三、那VLA到底有什么用?
既然不能扩容,要它何用?它的价值在于处理函数参数和临时小数组时非常灵活:
以前你要写一个处理任意大小数组的函数,如果数组很大,必须用 malloc 在堆(heap)上动态分配(比较慢,还要记得释放)。有了VLA,你可以直接在栈(stack)上快速创建一个大小恰到好处的数组:
c
// 这个函数可以处理任意尺寸的二维矩阵,尺寸由参数传入
void processMatrix(int rows, int cols, int matrix[rows][cols]) {
// 这里可以直接使用matrix,长度由调用时传入的rows和cols决定
}
int main() {
int x = 3, y = 4;
int myArr[x][y]; // 运行时才确定大小,3行4列,创建在栈上,速度极快
processMatrix(x, y, myArr);
}
总结一句话:VLA解决了“编译时不知道大小,但运行时才知道”的问题,但它依然是“一锤子买卖”——确定了就改不了。
四、为什么可变长度数组又是“不可变的”
有人会问“既然允许用变量来定义长度了,为什么还要规定‘确定后不能改’?”
我们可以分为三个瞬间看待这个可变过程。
-
确定时机:普通数组在编译时就定好了大小;VLA在程序运行到这一行代码时,根据当时变量的值(比如上面的
n)来确定大小。 -
不可变性:一旦这行代码执行完,这个数组的长度就彻底锁死了。在它的生命周期内,你绝对无法通过修改
n的值来改变arr的长度。
1. 第一瞬间:程序运行到这里(读取变量值)
当CPU(中央处理器)执行到 int arr[n]; 这一行代码时,它做的第一件事就是去内存里把变量 n 当前的值取出来。
假设此时 n 里面存的是数字 10。
这个动作发生在程序运行的微秒级别,就是你说的“一瞬间”。
2. 第二瞬间:计算并分配内存(栈指针移动)
CPU拿到数字 10 后,会立刻计算需要多少字节(比如 10 * 4 = 40 字节)。
3. 第三瞬间:数组长度“被遗忘”
这是最关键的一点,也是解开你矛盾感的金钥匙。
当CPU把栈顶指针移动完毕,给数组腾出空间后,CPU就彻底把 n 的值(那个10)给忘了。
总结:看似加上变量为可变性,实际上只是对于编译期,当程序运行起来,数组长度就是不可变性。
二、数组字面量
一、什么是复合字面量
什么是复合字面量?
传统说法:数组字面量就是直接用花括号 {} 括起来的一组数据值,用来表示一个数组类型的常量数据。
复合字面量(Compound Literal) 是 C99 标准引入的一个语法特性,它的核心就是:在数组字面量前面加上类型名,把它变成一个带类型的临时数据块。
对比一下:
| 数组字面量(不带类型) | 复合字面量(带类型) | |
|---|---|---|
| 写法 | {1, 2, 3} |
(int[3]){1, 2, 3} |
| 区别 | 没有类型,只能用在初始化场景 | 有类型,可以独立使用 |
本质上,复合字面量就是“带类型的数组字面量”。
二、为什么需要这个类型?
因为 C 语言是强类型语言,编译器必须知道一段数据的类型才能正确处理它。
当你写 {1, 2, 3} 时,它只是一堆数字,编译器不知道它是 int 数组还是 long 数组,所以它只能出现在初始化赋值给某个已知类型变量的地方。
但当你写成 (int[3]){1, 2, 3} 时,编译器一眼就认出:这是一个包含 3 个 int 类型元素的数据块。有了类型,它就可以独立出现在代码的任何位置了。
三、复合字面量的本质是什么?
它就是一个临时存在的匿名数组。
-
“临时”:它不像变量那样有固定的生命周期,用完就没了。
-
“匿名”:它没有名字,所以不能在其他地方通过名字引用它。
你可以把它理解成:在代码里随手写出的一块数据,随用随扔。
四、它最基本的用法
既然复合字面量是一个有类型的数组数据,它最基本的用法就是:
直接取出它的地址,赋给指针,然后使用。
c
int *p = (int[3]){10, 20, 30};
这一行干了什么事?
-
在内存中创建了一个匿名数组,存着
{10, 20, 30} -
把数组的起始地址赋给指针变量
p -
之后通过
p[0]、p[1]、p[2]就能访问这些数据
如果不用复合字面量,想达到同样效果,必须先定义一个变量:
c
int temp[3] = {10, 20, 30};
int *p = temp;
五、它的存储位置
复合字面量在内存中的位置取决于它出现在哪里:
-
出现在函数内部:分配在栈上,函数返回后就销毁。
-
出现在函数外部(全局):分配在静态存储区,整个程序运行期间都存在。
这意味着,如果你在一个函数里创建了复合字面量并返回它的指针,千万不要在函数外面使用这个指针——因为那块内存已经没了。
最后总结:复合字面量省掉了定义变量这一步,代码更简洁。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐


所有评论(0)