初始化字符串采用的方法取决于变量是被声明为字符数组还是字符指针,字符串所用的内存要么是数组要么是指针指向的一块内存。我们可以用字符串字面量或者一系列字符初始化字符串,或者从别的地方(比如说标准输入)得到字符。接下来我们会研究这些方法。
初始化char
数组
我们可以用初始化操作符初始化char
数组。在下例中,header
数组被初始化为字符串字面量中所包含的字符:
char header[] = "Media Player";
字面量"Media Player"
的长度为12个字符,表示这个字面量需要13字节,我们就为数组分配了13字节来持有字符串。初始化操作会把这些字符复制到数组中,以NUL
结尾,如下图所示,这里假设在main
函数中声明数组。
我们也可以用strcpy
函数初始化数组,5.2.2节会详细讨论strcpy
。下面的代码片段将字符串字面量复制到了数组中。
char header[13];
strcpy(header,"Media Player");
更笨的办法是把字符逐个赋给数组元素,如下所示:
header[0] = 'M';
header[1] = 'e';
...
header[12] = '\0';
警告 下面的赋值是不合法的,我们不能把字符串字面量的地址赋给数组名字。
char header2[]; header2 = "Media Player";
初始化char
指针
动态内存分配可以提供更多的灵活性,当然也可能会让内存存在得更久。下面的声明用来说明这种技术:
char *header;
初始化这个字符串的常见方法是使用malloc
和strcpy
函数分配内存并将字面量复制到字符串中,如下所示:
char *header = (char*) malloc(strlen("Media Player")+1);
strcpy(header,"Media Player");
假设这段代码在main
函数中,下图显示了程序栈的状态。
char *header = (char*) malloc(13);
警告 在决定
malloc
函数要用到的字符串长度时,要注意以下事项。
- 一定要记得算上终结符
NUL
。- 不要用
sizeof
操作符,而是用strlen
函数来确定已有字符串的长度。sizeof
操作符会返回数组和指针的长度,而不是字符串的长度。
如果不用字符串字面量和strcpy
函数初始化字符串,我们也可以这么做:
*(header + 0) = 'M';
*(header + 1) = 'e';
...
*(header + 12) = '\0';
我们可以将字符串字面量的地址直接赋给字符指针,如下所示。不过,这样不会产生字符串的副本,如下图所示。
char *header = "Media Player";
警告 试图用字符字面量来初始化
char
指针不会起作用。因为字符字面量是int
类型,这其实是尝试把整数赋给字符指针。这样经常会造成应用程序在解引指针时终止:char* prefix = '+'; // 不合法
正确的做法是像下面这样用
malloc
函数:prefix = (char*)malloc(2); *prefix = '+'; *(prefix+1) = 0;
从标准输入初始化字符串
也可以用标准输入等外部源初始化字符串。不过,在从标准输入读入字符串时可能会出错,下面是个例子。这里会出问题是因为我们在使用command
变量之前没有为其分配内存:
char *command;
printf("Enter a Command: ");
scanf("%s",command);
要解决这个问题需要首先为指针分配内存,或者用定长数组代替指针。不过,用户输入的数据可能比我们所能装下的要多,第4章讨论了更健壮的方法。
字符串位置小结
我们可能将字符串分配在几个地方,下例解释了几种可能的变化,下图说明了这些字符串在内存中的布局。
char* globalHeader = "Chapter";
char globalArrayHeader[] = "Chapter";
void displayHeader() {
static char* staticHeader = "Chapter";
char* localHeader = "Chapter";
static char staticArrayHeader[] = "Chapter";
char localArrayHeader[] = "Chapter";
char* heapHeader = (char*)malloc(strlen("Chapter")+1);
strcpy(heapHeader,"Chapter");
}
知道字符串存储的位置对理解程序的工作原理以及用指针访问字符串有帮助。字符串的位置决定它能存在多久,以及程序的哪些部分可以访问它。比如说,分配在全局内存的字符串会一直存在,也可以被多个函数访问;静态字符串也一直存在,不过只有定义它们的函数才能访问,分配在堆上的内存在释放之前会一直存在,也可以被多个函数访问。理解这些东西能让你作出更好的选择。