变长数组(VLA)
变长数组不能改变大小,但可以使用变量指定数组的维度
int quarters = 4;
int regions = 5;
double sales[regions][quarters]; // 一个变长数组(VLA)
使用变长数组时要先声明使用的变量:
int sum2d(int rows, int cols, int ar[rows][cols]); // ar是一个变长数组(VLA)
int sum2d(int ar[rows][cols], int rows, int cols); // 无效的顺序
而C99/C11标准规定,可以省略原型中的形参名,但是在这种情况下,必须要用星号来代替省略的维度:
int sum2d(int, int, int ar[][]); // ar是一个变长数组(VLA),省略了维度形参名
上代码:
/*将数组中的元素相加*/
int sum2d(int rows, int cols, int ar[rows][cols])/*将符号常量COLS替换成了变量cols*/
{
int r;
int c;
int tot = 0;
for (r = 0; r < rows; r++)
for (c = 0; c < cols; c++)
tot += ar[r][c];
return tot;
}
该函数可以处理任意大小的二维int数组
以变长数组作为形参的函数既可以处理传统C数组,也可以处理变长数组(该程序要求编译器支持变长数组特性)
//vararr2d.c -- 使用变长数组的函数
#include <stdio.h>
#define ROWS 3
#define COLS 4
int sum2d(int rows, int cols, int ar[rows][cols]);
int main(void)
{
int i, j;
int rs = 3;
int cs = 10;
int junk[ROWS][COLS] = {
{ 2, 4, 6, 8 },
{ 3, 5, 7, 9 },
{ 12, 10, 8, 6 }
};
int morejunk[ROWS - 1][COLS + 2] = {
{ 20, 30, 40, 50, 60, 70 },
{ 5, 6, 7, 8, 9, 10 }
};
int varr[rs][cs]; // 变长数组(VLA)
for (i = 0; i < rs; i++)
for (j = 0; j < cs; j++)
varr[i][j] = i * j + j;
printf("3x4 array\n");
printf("Sum of all elements = %d\n", sum2d(ROWS, COLS, junk));
printf("2x6 array\n");
printf("Sum of all elements = %d\n", sum2d(ROWS - 1, COLS + 2, morejunk));
printf("3x10 VLA\n");
printf("Sum of all elements = %d\n", sum2d(rs, cs, varr));
return 0;
}
// 带变长数组形参的函数
int sum2d(int rows, int cols, int ar[rows][cols])
{
int r;
int c;
int tot = 0;
for (r = 0; r < rows; r++)
for (c = 0; c < cols; c++)
tot += ar[r][c];
return tot;
}
输出结果应为:
3x4 array
Sum of all elements = 80
2x6 array
Sum of all elements = 315
3x10 VLA
Sum of all elements = 270
复合字面量
字面量:除符号常量外的常量
对于数组,复合字面量类似数组初始化列表,前面是用括号括起来的类型名。
数组初始化:
int diva[2] = {10, 20};
下面的复合字面量创建了一个和diva数据相同的匿名数组,也有两个int类型的值:
(int [2]){10, 20} // 内含3个元素的复合字面量
注意,去掉声明中的数组名,留下的int[2]即是复合字面量的类型名。
初始化有数组名的数组时可以省略数组的大小,复合字面量也可以省略大小,编译器会自动计算数组当前的元素个数:
(int []){50, 20, 90} // 内含3个元素的复合字面量
因为复合字面量是匿名的,所以不能先创建然后再使用,必须在创建的同时使用它。使用指针记录地址就是一种用法。上代码:
int * pt1;
pt1 = (int [2]) {10, 20};
与有数组名的数组类似,复合字面量的类型名也代表首元素的地址,所以可以把它赋给指向int的指针,然后便可以使用这个指针。例如,上述的*pt1是10,pt1[1]是20
还可以把复合字面量作为实际参数传递给带有匹配形式参数的函数:
int sum(const int ar[], int n);
...
int total3;
total3 = sum((int []){4,4,4,5,5,5}, 6);
在此例中,第一个实参是内含6个int类型值的数组,和数组名类似,这同时也是该数组的首元素的地址。这种用法的好处是,把信息传入函数前不用先创建数组(典型用法)
以此类推,这种用法也应用于二维数组或多维数组,例如:
int (*pt2)[4]; // 声明一个指向二维数组的指针,该数组内含2个数组元素,
// 每个元素是内含4个int类型值的数组
pt2 = (int [2][4]) { {1,2,3,-9}, {4,5,6,-8} };
当应用于实际代码中时:
// flc.c -- 有趣的常量
#include <stdio.h>
#define COLS 4
int sum2d(const int ar[][COLS], int rows);
int sum(const int ar[], int n);
int main(void)
{
int total1, total2, total3;
int * pt1;
int(*pt2)[COLS];
pt1 = (int[2]) { 10, 20 };
pt2 = (int[2][COLS]) { {1, 2, 3, -9}, { 4, 5, 6, -8 } };
total1 = sum(pt1, 2);
total2 = sum2d(pt2, 2);
total3 = sum((int []){ 4, 4, 4, 5, 5, 5 }, 6);
printf("total1 = %d\n", total1);
printf("total2 = %d\n", total2);
printf("total3 = %d\n", total3);
return 0;
}
int sum(const int ar [], int n)
{
int i;
int total = 0;
for (i = 0; i < n; i++)
total += ar[i];
return total;
}
int sum2d(const int ar [][COLS], int rows)
{
int r;
int c;
int tot = 0;
for (r = 0; r < rows; r++)
for (c = 0; c < COLS; c++)
tot += ar[r][c];
return tot;
}
该程序需要支持C99的编译器才能正常运行
复合字面量的使用
表示字符串和字符串组I/O
前情回顾:字符串是以空字符(\0)结尾的char类型数组
在程序中表示字符串:
// strings1.c
#include <stdio.h>
#define MSG "I am a symbolic string constant."
#define MAXLENGTH 81
int main(void)
{
char words[MAXLENGTH] = "I am a string in an array.";
const char * pt1 = "Something is pointing at me.";
puts("Here are some strings:");
puts(MSG);
puts(words);
puts(pt1);
words[8] = 'p';
puts(words);
return 0;
}
puts函数也属于stdio.h系列的输入/输出函数,但puts函数只显示字符串,而且自动在显示的字符串末尾加上换行符。
显示字符串