到目前为止我们只接触过一种字面值,那就是字符串,除了字符串C99还允许在C语言中使用字面值创建数组、结构体这样的数据集合,格式为:
(类型){数据}
对于数组int a[3]={1,2,3}来说,类型为int[3]或 int[],值为{1,2,3},因此字面值为(int[3]){1,2,3} 或(int[]){1,2,3},对于结构体:
struct Point
{
float x;
float y;
} p={3,5};
p的类型为struct Point,值为{3,5},因此字面值为(struct Point){3,5},下例用字面值创建一个数组和一个结构体变量:
#include <stdio.h>
struct Point
{
float x;
float y;
};
int main()
{
int* a = (int[]){1,2,3};
struct Point p = (struct Point){3,5};
printf("%d,%f",a[0],p.x);
return(0);
}
只要是支持C99的编译器都可以成功运行改代码,这里将字面值指定给变量a和p,需要注意的是数组按引用传递,结构体按值传递。可以看到用字面值的方式初始化变量并不简洁,字面值的优势在于创建匿名数组和结构体,特别是在传递参数时,例如:
#include <stdio.h>
struct Point
{
float x;
float y;
};
void printP(struct Point p)
{
printf("%f,%f\n",p.x,p.y);
}
void testArr(int a[][2])
{
printf("%d\n",a[0][0]);
}
int main()
{
printP((struct Point) { 1, 2 });
testArr((int[][2]) { {4,4}, {2,3}, {5,1} });
return(0);
}
当我们测试printP()函数时,可以直接传入一个结构体字面值,省略了变量声明。当我们测试一个二维数组时,可以直接传入一个二维数组的字面值,非常方便。
在讲解字符串时我们讲过,对于"abc"来说它是放在静态区的一个常量,返回的是常量字符数组地址,那么字面值创建的数组和结构体如何呢?我们也可以通过下面代码验证:
#include <stdio.h>
struct Point
{
float x;
float y;
};
int main()
{
//测试数组字面值的副本
int* a = (int[]){1,2,3};
int* b = (int[]){1,2,3};
a[0] = 0;
printf("%d,%d\n",a[0],b[0]);
//测试结构体字面值的副本
struct Point arr[2] = { (struct Point) { 1,1 },(struct Point) {1,1} };
arr[0].x = 2;
arr[0].y = 2;
printf("%f,%f\n",arr[1].x,arr[1].y);
return(0);
}
可以看到数组和结构体字面值创建后都可以修改,而且每次都会创建新的副本,因此数组和结构体的字面值和字符串字面值完全是两种处理方式,它们不是常量,只是在使用上可以当作常量来给变量赋值,它们也不会放入内存静态区,而是可以作为全局变量和局部变量值,而且匿名的字面值在循环中也和显式声明的变量一样会自动提升声明,只被初始化一次,这种机制会导致了一个问题:当字面值的地址丢失后,就再也找不回来了,这通常发生在将字面值作为参数发送给函数,这些字面值在使用一次后就丢弃了,下次会重新创建副本。如果字面值是在自定义函数中创建的还好,局部变量会在函数执行完毕后销毁,如果是在main()中创建的就成了无法引用的垃圾,需要慎用。