简介 C风格的字符串其实就是特殊的 char 数组。特殊在于,数组中至少有一个 ‘’ (其ASCII码值为0)空字符元素来作为字符串结束的标志。否则,这个数组只能看做是字符数组,而不能看做是字符串。C处理字符串的标准库string.h,以及其他的字符串处理函数,都建立在这种约定上,所以如果不满足这种约定,则它们不能正确工作。 例如 "C Language"是一个字符串常量,或者叫字符串字面值。它有9个英文字母和1个空格,但它占用11个字节,因为编译器自动在它末尾添加’‘作为字符串结束标志。也就是说不管是程序员,还是编译器,都遵守这样一个规则:C语言字符串以’'结尾。 C L a n g u a g e
小编分类整理了许多C语言进阶学习材料和BAT面试题,需要资料的请进Q群:731611386免费领取
下面通过例子说明’'对字符串的重要意义以及字符串和字符数组的差别。
空字符串
空字符串的类型是char[1],如
char empty_str1[1] = “”; 类型是:const char[1]
char empty_str2[1] = {’’} 类型是:char[1]
字符串字面值
直接书写的,用双引号表示的就是字符串字面值。形如 “hello” 这样的叫做字符串字面值,或者字符串文字常量。它是字符串,但与普通的数组存储的字符串相比有一些其他的特性。
ANSI C声明:修改一个字符串字面值的后果是未定义的,因此现在的编译器都不允许程序员修改字符串字面值的内容。像下面的代码是不允许的,即便编译通过,也会发生运行时错误。
小编分类整理了许多C语言进阶学习材料和BAT面试题,需要资料的请进Q群:731611386免费领取
ANSI C这样做的一个好处是:程序中使用相同内容的字符串字面值的可能会共享同一个字符串内存数据实体,以避免内存的浪费。
下面两处使用了相同的字符串字面值,好的编译器会仅仅存储一份 "hello"实体在内存中,而指针s 和 msg指向相同的字符串去使用他们。
ANSI C这样做的一个好处是:程序中使用相同内容的字符串字面值的可能会共享同一个字符串内存数据实体,以避免内存的浪费。
小编分类整理了许多C语言进阶学习材料和BAT面试题,需要资料的请进Q群:731611386免费领取
其次,当字符串字面值作为右值使用时,那么表达式的值就是这个字符串字面值存储在内存中的起始地址,即第一个字符在内存中的地址。
char* p = “hello”;
putchar(*p); //打印出:h
再者,字符串字面值是存放在内存的静态存储区的,静态区的数据从程序载入内存运行,到程序结束都一直存在,因此,像下面的代码是合法有效的:即从函数返回一个字符串字面值(实质上是这个字符串在内存中的地址),并不会因为函数执行完而将字符串字面值析构掉。
#include<stdio.h>
char* foo()
{
return “hello world”;
}
int main(int argc, char *argv[])
{
char *msg = foo();
printf(msg);
}
标准库中的字符串函数
以下的字符串函数大多都定义在string.h中。给出的实现源代码从linux2.6内核中提取,仅供参考。
size_t strlen( const char *str );
一种可能的实现方式:
size_t strlen(const char * s)
{
const char *sc;
for (sc = s; *sc != '\0'; ++sc)
/* nothing */;
return sc - s;
1
2
3
}
返回一个字符串的长度。字符串str必须以’\0’结尾。
char* strcpy( char* dest, const char* src );
一种可能的实现方式:
char * strcpy(char * dest,const char *src)
{
char *tmp = dest;
while ((*dest++ = *src++) != '\0')
/* nothing */;
return tmp;
1
2
3
}
将src的字符( 包括末尾的\0’)拷贝到des中去。
dest字符串必须保证至少有 strlen(src) +1 的空间。
如果des的容量不足以容纳src的全部字符和末尾的\0,则结果未定义。
如果des 和src内存发生重叠,则结果未定义
char *strncpy( char *dest, const char *src, std::size_t count );
示例:
char * strncpy(char * dest,const char *src,size_t count)
{
char *tmp = dest;
while (count) {
if ((*tmp = *src) != 0) src++;
tmp++;
count--;
}
return dest;
1
2
3
4
5
6
}
将字符串src中最多count个字符拷贝到 dest中。返回字符串dest。
理想的情况是: count==strlen(src) +1
1、如果count 小于 src的长度 +1,则dest将不会以’\0’结尾。
2、如果 count 大于 src 的长度 +1 ,则在完全拷贝src之后,继续在dest后面追加’\0’,直到总共拷贝 count 个 字符到 dest
dest 和 src的内存块不能重叠,否则结果未定义。
int strcmp( const char *lhs, const char *rhs );
示例:
int strcmp(const char * cs,const char * ct)
{
register signed char __res;
while (1) {
if ((__res = *cs - *ct++) != 0 || !*cs++)
break;
}
return __res;
1
2
3
4
5
6
}
按照字典顺序(ASCII表)比较2个字符串。总之,如果第一个字符串“大于”第二个字符串,则返回一个正数,如果第一个字符串“小于”第二个字符串,则返回一个负数,如果2个字符串一样,则返回0。
2个字符串必须i是以’\0’结尾的。
如果2个字符串完全相同,则返回0。
如果在循环比较的某个索引位置上,前者lhs的字符“小于”rhs的字符,则返回一个负数。
如果在循环比较的某个索引位置上,前者lhs的字符“大于”rhs的字符,则返回一个正数。
int strncmp( const char *lhs, const char *rhs, size_t count );
和strcmp一样,只不过它最多比较count个连续的字符。
2个字符串必须i是以’\0’结尾的。
count如果过大,使得strncmp在比较过程中访问了超出字符串所在的数组的空间则是未定义的。
char *strcat( char *dest, const char *src );
一种可能的实现方式:
char * strcat(char * dest, const char * src)
{
char *tmp = dest;
while (*dest)
dest++; //退出循环后,dest指向目标字符串末尾的'\0'
while ((*dest++ = *src++) != '\0') //拷贝,直到遇到src中的'\0'
;
return tmp;
1
2
3
4
5
6
7
}
将字符串src追加到字符串dest的末尾,返回新的字符串dest的指针 。2个字符串必须i是以’\0’结尾的。
用src[0]替换dest中的原来的用于指示字符串末尾的’\0’,且最终dest依然是以’\0’结尾。
如果dest所在的数组无法容纳原来dest中的字符个数+src的字符个数+1,则结果未定义。
如果dest和是src内存重叠,则结果未定义。
小编分类整理了许多C语言进阶学习材料和BAT面试题,需要资料的请进Q群:731611386免费领取