在 C 语言中,字符串实际上是使用空字符 \0 结尾的一维字符数组。因此,\0 是用于标记字符串的结束。
空字符(Null character)又称结束符,缩写 NUL,是一个数值为 0 的控制字符,\0 是转义字符,意思是告诉编译器,这不是字符 0,而是空字符。
下面的声明和初始化创建了一个UESTC 字符串。由于在数组的末尾存储了空字符 \0,所以字符数组的大小比单词 UESTC 的字符数多一个。
char site[6] = {'U', 'E', 'S', 'T', 'C', '\0'};
依据数组初始化规则,您可以把上面的语句写成以下语句:
char site[] = "UESTC";
用双引号括起来的内容称为字符串字面量,也叫做字符串常量。
其实,不需要把 null 字符放在字符串常量的末尾。C 编译器会在初始化数组时,自动把 \0 放在字符串的末尾。
以下是 C/C++ 中定义的字符串的内存表示:
我们可以以数组形式声明字符串:
char ar[] = "I like C++";
也可以用指针形式声明字符串:
const char *p = "I like C++";
以上面的声明为例,数组形式在计算机的内存中分配为一个内含11个元素的数组(每个元素对应一个字符,还加上末尾的空字符’\0’),每个元素被初始化为字符串常量对应的字符。
通常,字符串都作为可执行文件的一部分存储在数据段中,当把程序载入内存时,也载入了程序中的字符串。字符串存储在静态存储区中。但是,程序开始运行时才会为该数组分配内存。此时,才将字符串拷贝到数组中。
注意,此时字符串有2个副本,一个是在静态内存中的字符串常量,另一个是存储在数组 ar 里的字符串。此后,编译器便把数组名 ar 识别为该数组首元素地址的别名。ar 是地址常量,不能被修改。
指针形式也使得编译器在计算机的内存中分配为一个内含11个元素的数组来存储字符串。另外,一旦开始执行程序,编译器会为指针变量 p 留出一个存储位置,并把字符串的地址存储在指针变量中。该变量最初指向该字符串的首个字符,但是它的值可以改变。
字符串常量被视为 const 数据。由于 p 指向这个 const 数据,所以应该把 p 声明为指向 const 数据的指针。这意味着不能用 p 来改变它指向的数据,但 p 本身可以改变。如果把一个字符串常量拷贝给一个数组,就可以随意改变数据,除非数组声明为 const。
总而言之,初始化数组把静态存储区中的字符串拷贝给数组,而初始化指针只把字符串的地址拷贝给指针。
实例:
#include
#include
#define MSG "I like C++"
int main(void)
{
char ar[] = MSG;
const char *p = MSG;
printf("%p\n", "I like C++");
printf("%p\n", MSG);
printf("%p\n", ar);
printf("%p\n", p);
printf("%p\n", "I like C++");
system("pause");
return 0;
}
运行结果:
该程序说明,p 和 MSG 的地址相同,而 ar 的地址不同。
还说明,编译器会把多次使用的相同字符串常量存储在同一处。
C 库函数 char *gets(char *str) 从标准输入 stdin 读取一行,并把它存储在 str 所指向的字符串中。当读取到换行符时,或者到达文件末尾时,它会停止,具体视情况而定。然后丢弃换行符,存储其余字符,并在这些字符的末尾添加一个空字符使其成为一个 C 字符串。
下面是 gets() 函数的声明:
char *gets(char *str)
参数 str 是指向一个字符数组的指针,该数组存储了 C 字符串。
如果成功,该函数返回 str。如果发生错误或者到达文件末尾时还未读取任何字符,则返回 NULL。
实例:
#include
#include
int main(void)
{
char str[50];
printf("请输入一个字符串:");
gets(str);
printf("您输入的字符串是:%s\n", str);
system("pause");
return 0;
}
运行结果:
不安全的gets()
gets() 函数唯一的参数是 str,它无法检查数组是否装得下输入行。如果输入的字符串过长,会导致缓冲区溢出,即多余的字符超出了指定的目标空间。
C 库函数 int puts(const char *str) 把一个字符串写入到标准输出 stdout,直到空字符,但不包括空字符。
puts() 会在待输出字符串末尾添加一个换行符。
下面是 puts() 函数的声明。
int puts(const char *str)
参数 str 是要被写入的 C 字符串。
如果成功,该函数返回一个非负值为字符串长度(包括末尾的 \0),如果发生错误则返回 EOF。
实例:
#include
#include
#include
int main(void)
{
char str1[15];
char str2[15];
strcpy(str1, "RUNOOB1");
strcpy(str2, "RUNOOB2");
puts(str1);
puts(str2);
system("pause");
return 0;
}
运行结果:
C 库函数 char *fgets(char *str, int n, FILE *stream) 从指定的流 stream 读取一行,并把它存储在 str 所指向的字符串内。当读取 (n-1) 个字符时,或者读取到换行符时,或者到达文件末尾时,它会停止,具体视情况而定。
如果 fgets() 读到了一个换行符,会把它存储在字符串中。这一点与 gets() 不同。
下面是 fgets() 函数的声明:
char *fgets(char *str, int n, FILE *stream)
参数:
返回值:
如果成功,该函数返回相同的 str 参数。如果到达文件末尾或者没有读取到任何字符,str 的内容保持不变,并返回一个空指针。
如果发生错误,返回一个空指针。
C 库函数 int fputs(const char *str, FILE *stream) 把字符串写入到指定的流 stream 中,但不包括空字符。
fputs() 不在字符串末尾添加换行符。这一点与 puts() 不同。
下面是 fputs() 函数的声明:
int fputs(const char *str, FILE *stream)
参数:
返回值:
该函数返回一个非负值,如果发生错误则返回 EOF。
C11 新增的 gets_s() 函数和 fgets() 类似,用一个参数限制读入的字符数。
与 fgets() 不同的是,gets_s() 只从标准输入里读取数据。如果 gets_s() 读到了一个换行符,会丢弃它而不是存储它。
如果 gets_s() 读到最大字符数都没有读到换行符,首先会把目标数组的首字符设为空字符,读取并丢弃随后的输入直至遇到换行符或文件结尾,然后返回空指针。
如果fgets()返回NULL,说明读到文件结尾或出现读取错误,s_gets()函数跳过了这个过程。
效果:如果字符串中出现换行符,就要空字符替换它。
C 中有大量操作字符串的函数。ANSI C 把这些函数的原型放在 string.h 头文件中。
C 库函数 size_t strlen(const char *str) 计算字符串 str 的长度,直到空结束字符,但不包括空结束字符。
下面是 strcat() 函数的声明:
char *strcat(char *dest, const char *src)
下面是 strlen() 函数的声明:
size_t strlen(const char *str)
参数:
返回值:
该函数返回字符串的长度。
C 库函数 char *strcat(char *dest, const char *src) 把 src 所指向的字符串追加到 dest 所指向的字符串的结尾。
下面是 strcat() 函数的声明:
char *strcat(char *dest, const char *src)
参数:
返回值:
该函数返回一个指向最终的目标字符串 dest 的指针。
C 库函数 char *strncat(char *dest, const char *src, size_t n) 把 src 所指向的字符串追加到 dest 所指向的字符串的结尾,直到 n 字符长度为止。
下面是 strncat() 函数的声明:
char *strncat(char *dest, const char *src, size_t n)
参数:
返回值:
该函数返回一个指向最终的目标字符串 dest 的指针。
C 库函数 int strcmp(const char *str1, const char *str2) 把 str1 所指向的字符串和 str2 所指向的字符串进行比较。
下面是 strcmp() 函数的声明:
int strcmp(const char *str1, const char *str2)
参数:
该函数返回值如下:
实际上,strcmp() 的返回值是 str1 和 str2 第一个相异字符之间的差值。但我们一般只关心比较的结果是正、负、还是0。
C 库函数 char *strcpy(char *dest, const char *src) 把 src 所指向的字符串复制到 dest。
需要注意的是如果目标数组 dest 不够大,而源字符串的长度又太长,可能会造成缓冲溢出的情况。
下面是 strcpy() 函数的声明:
char *strcpy(char *dest, const char *src)
参数:
返回值:
该函数返回一个指向最终的目标字符串 dest 的指针。
C 库函数 char *strncpy(char *dest, const char *src, size_t n) 把 src 所指向的字符串复制到 dest,最多复制 n 个字符。当 src 的长度小于 n 时,dest 的剩余部分将用空字节填充。
下面是 strncpy() 函数的声明:
char *strncpy(char *dest, const char *src, size_t n)
参数:
返回值:
该函数返回最终复制的字符串。
C 库函数 char *strchr(const char *str, int c) 在参数 str 所指向的字符串中搜索第一次出现字符 c(一个无符号字符)的位置。
下面是 strchr() 函数的声明:
char *strchr(const char *str, int c)
参数:
返回值:
该函数返回在字符串 str 中第一次出现字符 c 的位置,如果未找到该字符则返回 NULL。
C 库函数 char *strstr(const char *haystack, const char *needle) 在字符串 haystack 中查找第一次出现字符串 needle 的位置,不包含终止符 ‘\0’。
下面是 strstr() 函数的声明:
char *strstr(const char *haystack, const char *needle)
参数:
返回值:
该函数返回在 haystack 中第一次出现 needle 字符串的位置,如果未找到则返回 null。