我是C
语言的新手。我试图理解C中的数组概念。我对数组初始化有困惑。
使用字符串文字初始化字符数组的更好方法是什么?
char arr[3] = "xyz";
或
char arr[] = "xyz";
提前致谢。
答案 0 :(得分:8)
除非在特殊情况下,否则总是更喜欢第二种方式,即不明确键入数组的大小。这样可以避免在您的示例中看似无意识地创建的错误。
要理解这一点,首先应该了解字符串究竟是什么。空字符由'\0'
表示。字符串是一系列零个或多个非空char
s,由单个空字符终止。最后一点非常重要。请查看以下代码:
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' |
|_______|_______|_______|
看起来不错,但是......等等。终止空字符在哪里?你通过明确声明阵列的大小来切断它!现在,strlen()
如何确定字符串的长度?它将继续读取字符串后面的字符,直到通过巧合找到空字符。这是未定义的行为。另一方面,通过这样做:
const char[] my_string = "abc";
你不会冒这样做的风险。 my_string
的类型会自动推断为const char[ 4 ]
,并且会保留空字符。
tl; dr 不要忘记终止空字符!
答案 1 :(得分:4)
每当使用字符串文字初始化一个字符数组时,不要指定用字符串文字初始化的字符串的边界,因为编译器会自动为整个字符串文字分配足够的空间,包括终止空字符。
C标准(c11 - 6.7.8:第14段)说:
字符类型数组可以由字符串初始化 文字或UTF-8字符串文字,可选择用大括号括起来。 字符串文字的连续字节(包括终止空值) 字符,如果有空间或数组的大小未知) 初始化数组的元素。
char arr[3] = "xyz";
在此示例中,arr
的大小为3
,但字符串文字的大小为4
。字符串定义了一个字符(终止' \ 0'),而不是数组可以容纳的字符。
char arr[] = "xyz";
在此示例中,未指定数组初始化中字符数组的边界。如果省略数组绑定,则编译器会分配足够的大小来存储整个字符串文字,包括空字符。
答案 2 :(得分:2)
你使用第二个,因为在第二个中你想要初始化另一个超过3个字符的string
,所以它会自动处理。
示例代码
int main()
{
int i;
char arr[] = "xyz Hello World";
for(i=0;i<sizeof(arr)-1;i++){
printf("%c",arr[i]);
}
printf("\n");
return 0;
}
如果您使用第一个,那么当您想要存储超过3个char
字符串时,它将在编译时显示warning
警告强>
warning: initializer-string for array of chars is too long [enabled by default]
char arr[3] = "xyz Hello World";
所以你应该使用第二个帽子是使用character
初始化string
数组的更好方法。
答案 3 :(得分:0)
还考虑使用
const char* arr = "xyz";
它是一样的(除了使用它之后的常量关键字,所以你不会意外地更改数组),但数据不会被复制在堆栈中,您可以在可执行文件的数据段中使用静态副本。特别是对于大弦乐,这很重要。