C中的字符串常量和数组

ksb*_*ksb 1 c arrays string

在C中,我们可以创建一个指向常量字符串的指针

char *s = "abc";
Run Code Online (Sandbox Code Playgroud)

但由于字符串基本上是空终止字符数组,为什么不允许这样做?

char *s = {'a', 'b', 'c' , '\0' };
Run Code Online (Sandbox Code Playgroud)

Kei*_*son 10

这基本上是因为语言不允许它.

字符串文字是类型的表达式char[N+1],其中N是文字的长度.它指的是具有静态存储持续时间的匿名数组对象,这意味着该对象存在于整个程序执行中.

(请注意,与C++不同,它不是 const char[N+1],但尝试修改它具有未定义的行为.)

和数组类型的任何表达式一样,它隐式转换为指向大多数上下文中数组的第一个元素的指针.

所以在这个宣言中:

char *s = "abc";
Run Code Online (Sandbox Code Playgroud)

表达式"abc"被隐式转换为指向数组的第一个字符的指针,用于初始化s.但是这样编写它会更安全:

const char *s = "abc";
Run Code Online (Sandbox Code Playgroud)

这样你就不会意外地尝试修改数组.

那么为什么这不起作用呢?

char *s = { 'a', 'b', 'c', '\0' };
Run Code Online (Sandbox Code Playgroud)

这是因为{ 'a', 'b', 'c', '\0' } 不是表达.它是一个初始化程序,可用于初始化数组对象,但不能用于初始化指针对象.如果你改为写:

char arr[] = { 'a', 'b', 'c', '\0' };
Run Code Online (Sandbox Code Playgroud)

然后初始化器用于初始化数组对象; 它不会创建数组对象.要使char *s版本起作用,必须为指向的指针创建一个对象s.Brace封闭的初始化列表只是不这样做.

C99添加了一个新功能,复合文字,在某些方面类似于字符串文字但更通用.例如,这个:

(char[]){ 'a', 'b', 'c', '\0' }
Run Code Online (Sandbox Code Playgroud)

是类型的表达式char[4]- 再次,就像数组类型的任何表达式一样,它在大多数上下文中被隐式转换为指针.所以这:

char *arr = (char[]){ 'a', 'b', 'c', '\0' };
Run Code Online (Sandbox Code Playgroud)

是有效的,并arr指向由复合文字创建的匿名数组对象的第一个元素.

有一个显着的区别:该数组对象的生命周期不一定是静态的.如果复合文字出现在任何函数之外,则数组对象确实存在于整个程序执行中; 否则,它就像一个具有自动存储持续时间的本地对象.

如果你使用的是不支持C99的编译器(*cough*Microsoft*cough*),你可以先声明一个数组对象:

const char arr[] = { 'a', 'b', 'c', '\0' };
const char *s = arr;
Run Code Online (Sandbox Code Playgroud)

另请参阅comp.lang.c FAQ的第6节,其中讨论了数组和指针(并且很好地解决了一些关于它们的常见误解).


Moh*_*oun 8

这是因为指针不是数组.(您可以将数组衰减到指向第一个元素地址的指针,但这不会使指针成为数组.)

你可以这样做:

   char s[] = {'a', 'b', 'c', '\0'};
// ^^^^^^^^   ^^^^^^^^^^^^^^^^^^^^^
// array      valid initializer for char[]
Run Code Online (Sandbox Code Playgroud)

但不是这个:

   char* s = {'a', 'b', 'c', '\0'};
// ^^^^^^^   ^^^^^^^^^^^^^^^^^^^^^
// pointer   invalid initializer for char*
Run Code Online (Sandbox Code Playgroud)