1.前言
C语言中对字符和字符串的处理很是频繁,但是C语言本身是没有字符串类型的,字符串通常放在
常量字符串中或者字符数组中。
字符串常量适合于那些对他不做修改的函数。
2.库函数及其模拟实现
2.1 strlen函数
size_t strlen ( const char * str );
函数要求:
- 字符串中将’\0’ 作为结束标志,strlen函数返回的是在字符串中’\0’ 前面出现的字符个数(不包含’\0’ )。
- 参数的指向的字符串必须要以’\0’结束,否则计算结果是随机值。
- 要注意strlen函数的返回值是size_t,是无符号数。
函数功能:计算str指向的字符串的长度。
测试代码:
int main() { char arr1[10] = "ab"; char arr2[10] = "abc"; if ((strlen(arr1) - strlen(arr2)) > 0) { printf("arr1更大"); } else { printf("arr2更大"); } return 0; }
运行结果:
结果明显是不符合现象的。
原因:strlen函数的返回值是size_t类型的,也就是unsigned int类型,返回的数是无符号数,这里的返回值分别是2和3,他们的类型都是unsigned int,在得到运算结果-1时,也会将-1当作无符号数处理,内存中的-1存储的是补码,这个补码会被当做原码处理,所以得到的结果就会是一个很大的数。
所以要注意:strlen函数的返回值的类型是unsigned int类型的。
strlen函数的模拟实现:
//方法一 unsigned int my_strlen(char* arr) { int count = 0; while (*arr) { count++; arr++; } return count; } int main() { char arr[10] = "abcdef"; printf("%d\n", my_strlen(arr)); return 0; }
//方法二 unsigned int my_strlen(char* arr) { if (*arr == '\0') return; else return 1 + my_strlen(arr+1); } int main() { char arr[10] = "abcdef"; printf("%d\n", my_strlen(arr)); return 0; }
2.2 strcpy函数
char* strcpy(char * destination, const char * source );
函数要求:
- 源字符串必须以 '\0'结束
- 函数会将源字符串中的’\0’ 拷贝到目标空间
- 目标空间必须足够大,以确保能存放源字符串
- 目标空间必须可变
函数功能:将source指向的字符串拷贝到destination指向的字符串中。
测试代码:
int main() { char* p = "abcdef"; char arr1 = "hello"; strcpy(p, arr1); printf("%s", p); return 0; }
运行这段代码时,程序会直接挂掉,因为p是一个指针变量,并不是一个数组。
strcpy函数的模拟实现:
char* my_strcpy(char* dest,const char* src) { assert(dest && src); char* ret = dest; while (*src) { *dest = *src; dest++; src++; } *dest = '\0'; return ret; } int main() { char src[] = "hello world!"; char dest[20] = "abcdef"; printf("%s", my_strcpy(dest, src)); return 0; }
2.3 strcat函数
char * strcat ( char * destination, const char * source );
函数要求:
- 源字符串必须以’\0’ 结束。
- 目标空间必须有足够的大,能容纳下源字符串的内容。
- 目标空间必须可修改。
函数功能:将source指向的字符串拷贝到destination所指向的字符串。
测试代码:
int main() { char* p = "abcdef"; char arr1 = "hello"; strcat(p, arr1); printf("%s", p); return 0; }
同样:运行这段代码时,程序会直接挂掉,因为p是一个指针变量,并不是一个数组。
strcat函数在运行时,会先找到第一个字符串的末尾的位置(也就是’\0’之前的位置),在该末尾位置将第二个字符串进行连接。
strcat函数的模拟实现:
char* my_strcat(char* dest,const char* src) { assert(dest && src); char* ret = dest; while (*dest) { dest++; } while (*src) { *dest = *src; src++; dest++; } *dest = '\0'; return ret; } int main() { char arr1[20] = "xx"; char arr2[20] = "hello world!"; printf("%s", my_strcat(arr2, arr1)); return 0; }
注意:strcat函数不能对有关联的内存空间进行操作,否则会发生死循环。例如:以下这种用法就是错误的。因为arr和arr+2这两个内存空间是有关联关系的。
int main() { char arr[20] = "hello world!"; printf("%s", strcat(arr, arr+2)); return 0; }
2.4 strcmp函数
int strcmp ( const char * str1, const char * str2 );
函数功能:按照字典序比较str1和str2这两个字符串的大小。
标准规定:
- 第一个字符串大于第二个字符串,则返回大于0的数字。
- 第一个字符串等于第二个字符串,则返回0。
- 第一个字符串小于第二个字符串,则返回小于0的数字。
strcmp函数的模拟实现:
int my_strcmp(char* str1, char* str2) { assert(str1 && str2); while (*str1 == *str2) { if (*str1 == '\0') { return 0; } str1++; str2++; } return *str1 - *str2; } int main() { char arr1[10] = "abcdef"; char arr2[10] = "a"; printf("%d", my_strcmp(arr1,arr2)); return 0; }
2.5 strncpy函数
char * strncpy ( char * destination, const char * source, size_t num );
函数的第三个参数是要拷贝的字节的大小。与strcpy函数相比,由于能规定拷贝的字节大小,所以strncpy函数更可控。
函数功能:
- 拷贝num个字符从源字符串到目标空间。
- 如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。
使用案例:
int main() { char arr1[20] = "xx"; char arr2[20] = "hello world!"; printf("%s", strncpy(arr1, arr2,4)); return 0; }
2.6 strncat函数
char * strncat ( char * destination, const char * source, size_t num );
函数功能:
- 将source指向的字符串的前sum个字节追加到destination所指向的字符串中,并自动追加'\0'。
- 如果source中字符串的长度小于num,其追加的长度仍为source的长度,并不会自己添加其他的内容。
使用案例:
int main() { char arr1[20] = "xx"; char arr2[20] = "hell\0oworld!"; printf("%s", strncat(arr2, arr1,4)); return 0; }
2.7 strncmp函数
int strncmp ( const char * str1, const char * str2, size_t num );
函数功能:
- 比较直到出现字符不同或者一个字符串结束或者num个字符全部比较完毕。
使用案例:
#include <stdio.h> #include <string.h> int main () { char str[][5] = { "R2D2" , "C3PO" , "R2A6" }; int n; puts ("Looking for R2 astromech droids..."); for (n=0 ; n<3 ; n++) if (strncmp (str[n],"R2xx",2) == 0) { printf ("found %s\n",str[n]); } return 0; }