在声明cString的这五种不同方式中发生了什么?

What is going on in these five different ways of declaring a cString?

本文关键字:五种 方式中 发生了 什么 cString 声明      更新时间:2023-10-16

另一个问题(我现在找不到)的解释是,"string"和{'s','t','r',' I ','n','g',''}是等价的。实际上,下面的前四个声明都可以编译,并且似乎做同样的事情。但是为什么第五个声明不能编译呢?看来它们根本就不相等。所以我的问题是在四个正确的和一个不正确的cstring声明中到底发生了什么?

int main()
{
    char cString1[]="string";
    char * cString2 = "string";
    char cString3[] = {'s','t','r','i','n','g', ''};
    char cString4[7] = {'s','t','r','i','n','g',''};
    char * cString5 = {'s','t','r','i','n','g',''};

    return 0;
}

声明并不完全相等:第一、第三和第四个声明创建了字符串文字的可写副本,而第二个声明不创建副本。因此,进行如下赋值操作是合法的

cString1[0] = 'S'; // cString3 and cString4 would work as well

当试图对cString2做同样的事情时,会触发未定义行为。

声明3和声明4是相同的,除了声明3让编译器计算出数组的大小,而声明4则显式地指定了数组的大小。你可以像这样重写第一个声明

char cString1a[7]="string";

遵循相同的模式。

虽然"string"可以在数组声明中重写为{'s','t','r','i','n','g',''},但在初始化指针的上下文中是不合法的。这就是为什么最后一个声明不起作用。

然而,在c99中,你可以用复合文字重写它,使其工作(演示):
char * cString5 = (char[]){'s','t','r','i','n','g',''};

它们(显然)不相等。
"string"是包含's','t','r','i','n','g',''字符的静态字符数组。然而,{stuff}是一个初始化列表。它是一个静态元素数组,用于初始化左侧。数组衰减为指针,但初始化列表不会,因为它不是数组。更令人困惑的是,在C和c++中有一个特殊的情况,char[]可以从字符串字面值初始化,尽管像这样通过"copy"初始化数组是非法的。

char cString1[]="string"; //special case for string literal array copy
char * cString2 = "string"; //array decays to pointer
char cString3[] = {'s','t','r','i','n','g', ''}; //constructing array from list
char cString4[7] = {'s','t','r','i','n','g',''}; //constructing array from list
char * cString5 = {'s','t','r','i','n','g',''}; //list DOES NOT decay to pointer.
char cString7[7] = "string"; //also special case for string literal array copy

如dasblinkenlight所述,其中两个是指向静态数据的指针,而其中三个是通过复制静态数据初始化的本地数组。它们很相似,但又不相同。

char cString1[]="string";声明了一个未指定大小的char数组(由编译器计算),并将其初始化为一个7 char(包括''终止符)的const数组

char * cString2 = "string";' declares a pointer to char and initializes it with a const array of 7 char (including the '' '终止符

char cString3[] = {'s','t','r','i','n','g', ''};与第一个类似,只是在这种情况下,初始化数组是逐字符显式定义的,包括终止符。注意:如果您在此代码中省略终止符,则字符串将不终止,并且可能导致您的程序访问无效内存(例如当尝试打印该字符串时)

char cString4[7] = {'s','t','r','i','n','g',''};与前一个类似,只是您定义了数组的大小。如果初始化数组中的元素比指定的多(或少!),编译器将不高兴

char * cString5 = {'s','t','r','i','n','g',''};是非法的,因为你声明了一个指向char的指针,但用char数组初始化它

"string"{'s','t','r','i','n','g',''}是等价的

作为字符数组的初始化项,可以。在其他上下文中,no.

下面的前四个声明都可以编译,并且似乎做同样的事情。

不,他们没有;其中三个声明数组,而两个(尝试)声明指针。

但是为什么第五个声明不能编译呢?

因为它试图用不适合初始化指针的方式初始化指针。

在四个正确的和一个不正确的cstring声明中究竟发生了什么?

第一个声明了一个局部数组,通过复制字符串字面值(包括它的结束符)来初始化。大小是从字面量推断出来的。

第二个声明了一个局部指针,初始化为指向字符串字面值。字面量本身是const char的静态数组。现代编译器不应该接受这个,因为字面量是const,而指针不是。为了与旧代码兼容,旧版本的语言接受了这种危险的转换。

第三个声明了一个从列表初始化的局部数组。大小从列表中推断出来。结果与第一个相同。

第四个与第三个相同,但显式指定了大小。

第五个无效;指针必须指向对象(否则为空),而初始化列表不是对象。

在c++中,通常使用std::string来表示字符串比使用低级数组和指针更方便。

cString1是一个包含1个对象[0]的字符串数组[]

cString2是包含6个字符的字符串

cString3是一个包含7个对象的字符串数组[][7]

cString4是一个包含7个对象的字符串数组[7]

cString5是一个字符串,你试图声明为7个对象