哪个是使用字符串文字初始化字符数组的更好方法?
我是C
语言的新手。 我试图理解C中的数组概念。我对数组初始化有困惑。
哪个是使用字符串文字初始化字符数组的更好方法?
char arr[3] = "xyz";
要么
char arr[] = "xyz";
提前致谢。
除非在特殊情况下,总是更喜欢第二种方式,即不明确键入数组的大小。 这样可以避免在您的示例中看似无意识地创建的错误。
要理解这一点,首先应该了解字符串究竟是什么。 空字符用'\0'
。 字符串是一系列零个或多个非空char
, 由单个空字符终止 。 最后一点非常重要。 看下面的代码:
const char* my_string = "xyz"; size_t string_len = strlen( my_string ); // string_Len == 3
指针只是一个内存地址。 它本身不包含任何类型的大小或长度信息。 然后, strlen()
如何测量my_string
的长度? 当然,这是通过测量字符串开头到终止空字符之前的非空字符数量。 您现在可能已经注意到,终止空字符隐含在字符串文字中。 上面的字符串文字在内存中创建一个如下所示的数组:
_______ _______ _______ _______ | | | | | | 'x' | 'y' | 'z' | '\0' | |_______|_______|_______|_______| ^ | `my_string` is a pointer to this cell
数组本身没有命名,但编译器设法将其第一个元素的地址作为my_string
的值。 那么,你的第一个例子会发生什么?
char my_string[ 3 ] = "abc";
根据标准的定义,字符串文字的类型为char[ N ]
,其中N
是字符串的长度加上一个要计算的空字符(请注意,由于历史原因字符串文字未声明为const
,但仍然是未定义的行为以修改它们)。 因此,上面的表达式"abc"
具有char[ 4 ]
类型。 另一方面, my_string
(现在是一个数组,而不是指针,BTW)的类型为char[ 3 ]
。 也就是说,您将较小的数组设置为较大的数组,因为4 > 3
。 标准规定,在这种精确的情况下,字符串文字的空字符不适合数组,它应该被截断。 因此, my_string
在内存中看起来像这样:
_______ _______ _______ | | | | | 'a' | 'b' | 'c' | |_______|_______|_______|
看起来不错,但……等等。 终止空字符在哪里? 你通过明确声明arrays的大小来切断它! 现在, strlen()
应该如何确定字符串的长度? 它将继续读取字符串之后的字符,直到通过巧合找到空字符。 这是未定义的行为。 另一方面,通过这样做:
const char[] my_string = "abc";
你不会冒这样做的风险。 my_string
的类型将自动推导为const char[ 4 ]
,并保留null字符。
tl; dr不要忘记终止空字符!
每当使用字符串文字初始化一个字符数组时,不要指定用字符串文字初始化的字符串的边界,因为编译器将自动为整个字符串文字分配足够的空间,包括终止空字符。
C标准(c11 – 6.7.8:第14段)说:
字符类型数组可以由字符串文字或UTF-8字符串文字初始化,可选地用大括号括起来。 字符串文字的连续字节(如果有空间或数组大小未知,则包括终止空字符)初始化数组的元素。
char arr[3] = "xyz";
在此示例中, arr
大小为3
,但字符串文字的大小为4
。 string定义了一个比数组更能容纳的字符(终止’\ 0’)。
char arr[] = "xyz";
在此示例中,未指定数组初始化中字符数组的边界。 如果省略数组绑定,则编译器会分配足够的大小来存储整个字符串文字,包括空字符。
你使用第二个,因为在第二个中你想要初始化另一个超过3个字符的string
,所以它会自动处理。
示例代码
int main() { int i; char arr[] = "xyz Hello World"; for(i=0;i
如果您使用第一个,那么当您想存储超过3个字符串时,它将在编译时显示warning
警告
warning: initializer-string for array of chars is too long [enabled by default] char arr[3] = "xyz Hello World";
所以你应该使用第二个帽子是使用string
初始化character
数组的更好方法。
考虑也使用
const char* arr = "xyz";
它是相同的(除了’const’关键字使得它不会意外地更改数组),但数据不会被复制到堆栈上,您在可执行文件的数据段中使用静态副本。 特别是对于大弦,这可能很重要。