常见字符函数和字符串函数
前言
C语言中对字符和字符串的处理非常频繁,但是C语言本身没有字符串的类型,字符串通常放在常量字符串中或者字符数组中。
字符串常量适用于那些对它不做修改的字符串函数。
求字符串长度
strlen
函数:size_t strlen(const char* str);
参数:
- 入参:
const char* str
为待计算的字符串首地址。 - 返回值:
size_t
为计算出的字符串长度。
说明:
- 字符串以’\0’作为结束标志,strlen函数返回的是在字符串中’\0’前出现的字符个数(不包括’\0’)。
- 入参指向的字符串必须以’\0’结尾。
- 注意函数的返回值size_t是无符号的(易错)
案例:
请思考以下代码段的执行结果:
int main()
{
if(strlen("abc") - strlen("abcdef") > 0)
{
printf("111\n");
}
else
{
printf("222\n");
}
return 0;
}
答案:以上代码的输出结果为
111
,这是因为strlen
的返回值类型size_t
是一个无符号整型(unsigned int
类型),两个无符号类型相减结果为无符号类型,无符号类型数一定大于等于0。
长度不受限制的字符串函数
strcpy
函数:char* strcpy(char* destination, const char* source);
参数:
- 形参1:
char* destination
被拷贝的目标地址 - 形参2:
const char* source
为待拷贝的字符串首地址 - 返回值:
char*
被拷贝的目标地址
说明:
- 入参指向的字符串必须以’\0’结尾。
- 会将字符串中的’\0’拷贝到目标空间。
- 目标空间必须足够大,确保能存放源字符串。
- 目标空间必须可变(不能指向常量字符串)。
案例:
请思考以下代码段的执行结果:
int main()
{
char arr1[] = {'a','b'};
char arr2[] = ”abcdef“;
strcpy(arr2, arr1);
return;
}
答案:错误代码,程序会崩溃。因为arr1没有以’\0’结尾,strcpy不知道结束标志,可能出现数组下标越界访问。
strcat
函数:char* strcat(char* destination, const char* source);
参数:
- 形参1:
char* destination
待追加的目标空间首地址 - 形参2:
const char* source
为待追加的字符串首地址 - 返回值:
char*
被追加后的目标空间首地址
说明:
- 入参指向的字符串必须以’\0’结尾。
- 会将字符串中的’\0’拷贝到目标空间。
- 目标空间必须足够大,确保能存放源字符串。
- 目标空间必须可变(不能指向常量字符串)。
- 不能自己追加自己
案例:
请思考以下代码段的执行结果:
int main()
{
char arr1[30] = "abc";
strcat(arr1, arr1);
return;
}
答案:错误代码,程序会崩溃。自己追加自己会导致strcat函数一直找不到’\0’,最总下标越界访问。
strcmp
函数:int strcmp(const char* str1, const char* str2);
参数:
- 形参1:
const char* str1
待比较的字符串1 - 形参2:
const char* str2
待比较的字符串2 - 返回值:
int
比较结果- str1 > str2 ,则返回>0
- str1 = str2 ,则返回0
- str1 < str2 ,则返回0
说明:
- 注意,str1 = str2 的返回值并不是-1,0,1。当str1与str2不相等时,返回结果为
*str1 - *str2
的值。
长度受限制的字符串函数
说明:
长度不受限制的字符串在使用时,常出现目标空间不够或者下标越界的问题。所以为了提高编码的可靠性,C语言库函数提供了一些长度受限的字符串函数。
strncpy
函数:char* strcpy(char* destination, const char* source, size_t num);
说明:
- 拷贝num个字符串到目标空间。
- 如果源字符串的长度小于num,则拷贝完源字符串后,在后面追加’\0’,直到num个。
strncat
函数:char* strcat(char* destination, const char* source, size_t num);
strncmp
函数:int strcmp(const char* str1, const char* str2, size_t num);
字符串查找
strstr
函数:char* strstr(const char* string, const char* strCharSet);
参数:
- 形参1:
const char* string
主串 - 形参2:
const char* strCharSet
子串 - 返回值:
char*
子串在主串中首次出现的起始位置,未找到则返回空指针
说明:
- 拷贝num个字符串到目标空间。
- 如果源字符串的长度小于num,则拷贝完源字符串后,在后面追加’\0’,直到num个。
strtok
函数:char* strtok(char* str, const char* sep);
参数:
- 形参1:
const char* string
待分割的主串,它包含了0个或多个由sep字符串中的一个或多个分隔符分割的标记。 - 形参2:
const char* sep
是一个字符串,定义了作分隔符的字符集合(注意,是一个集合,也就是说可以有多种分隔符) - 返回值:
char*
子串在主串中首次出现的起始位置,未找到则返回空指针
说明:
- strtok函数会找到str中的下一个标记,并将其用
\0
结尾,返回一个指向这个标记的指针。(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可以修改。) - strtok函数的第一个参数不为NULL,函数将找到str中的第一个标记,strtok函数将保存它在字符串中的位置。
- strtok函数的第一个函数为NULL,函数将在同一个字符串中被保存的位置开始,查找下一个标记。
- 如果字符串中不存在更多的标记,则返回NULL指针。
案例:
请思考以下代码段的两次输出结果:
int main()
{
char arr[] = "abc.def@gh@";
char *p = ".@";
char temp[100] = {0};
strcmp(temp, arr);
char p1 = NULL;
p1 = strtok(temp, p);
printf("%s\n", p1);
p1 = strtok(NULL, p);
printf("%s\n", p1);
}
答案:
第一次输出:“abc”
第二次输出:“def”
因为第一次调用strtok时,将字符串中的第一个.号修改为了\0并返回了被切割后的子串"abc"的首地址。第二次调用strtok时,将字符串中的第一个@号修改为了\0并返回了被切割后的子串"def"的首地址。
strerror
函数:char* strerror(int errnum);
参数:
- 形参1:
coint errnum
错误码信息 - 返回值:
char*
错误码在C语言中对应的错误信息字符串
说明:
strerror
函数的作用是返回错误码,所对应的错误信息。strerror
函数需要与erron搭配使用。erron是一个全局的错误码的变量,当C语言的库函数在执行过程中,发生了错误,就会把对应的错误码赋值到erron中,通过strerror
函数就可以解析erron变量获取到错误信息。
案例:
int main()
{
FILE* pf = fopen("text.txt", "r"); // 以读方式打开文件
if(NULL == pf) // pf为空,表示文件打开失败
{
/* fopen方法执行失败时,会将错误码赋值给全局变量errno,使用strerror函数可以解析错误码对应的含义。*/
printf("%s\n", strerror(errno));
}
else
{
printf("open file success.\n");
}
}
字符操作
字符分类:
函数 | 如果他的参数符合以下条件就返回true |
---|---|
iscntrl | 任何控制字符 |
isspace | 空白字符:空格’ ‘,换页’\f’,换行’\n’,回车’\r’,制表符’\t’,垂直制表符’\v’ |
isdigit | 十进制0~9 |
isxdigit | 十六进制数字,包括所有十进制数字,小写字母af,大写字母AF |
islower | 小写字母a~z |
isupper | 大写字母A~Z |
isalnum | 字母或数字,az,AZ,0~9 |
ispunct | 标点符号,任意不属于数组或字母的图形符号(可打印) |
israph | 任何图形字符 |
isprint | 任何可打印字符,包括图形字符和空白字符 |
字符转换:
大写字母转小写:int tolower(int c)
小写字母转大写int toupper(int c)
参数:
- 形参1:
int c
表示待转换的字符 - 返回值:
int
表示转换后的字符
说明:
- 把大写字母转换为小写字母,如果参数c不是大写字母就不转换
- tolower函数的参数和返回值是整数,不是字符,因为在C语言中,字符就是整数
内存操作函数
memcpy
函数:void* memcpy(void* destination, const void* source, size_t num);
参数:
- 形参1:
void* destination
被拷贝到的目标地址 - 形参2:
const void* source
待拷贝的原始数据地址 - 形参3:
size_t num
需要拷贝几个字节 - 返回值:
void*
目的地的起始地址
说明:
- 函数memcpy会从source的位置开始向后复制num个字节的数据到destination的内存位置。
- memcpy函数遇到\0时不会停下来。
- 如果source和destination有任何的重叠,复制的结果都是未定义的。(使用memcpy时,要避免源地址和目的地址有重叠,如果内存空间有重叠,一般推荐使用memmove函数)
案例:
int main()
{
int arr1[] = {1,2,3,4,5};
int arr2[5] = {0};
memcpy(arr2, arr1, sizeof(arr1));
}
memmove
函数:void* memove(void* dest, const void* src, size_t count);
参数:
- 形参1:
void* dest
被拷贝到的目标地址 - 形参2:
const void* src
待拷贝的原始数据地址 - 形参3:
size_t count
需要拷贝几个字节 - 返回值:
void*
目的地的起始地址
说明:
- 函数memove会从src的位置开始向后复制count个字节的数据到dest的内存位置。
- memove函数遇到\0时不会停下来。
- 如果src和dest有任何的重叠,复制的结果都不会受到影响。
memset
函数:void* memset(void* dest, int c, size_t count);
参数:
- 形参1:
void* dest
待设置缓冲区的起始目标地址 - 形参2:
int c
待设置的字符 - 形参3:
size_t count
需要设置几个字节 - 返回值:
void*
在函数运行结束后返回这块空间的起始地址.
说明:
- memset函数的主要功能是将一段内存区域设置为指定值
- memset函数用于按字节初始化内存块(易错)
- memset函数用于使用单一字节值填充整个内存区域。对于需要复杂填充模式的情况,如交替字节值,应采用循环或其他编程逻辑来实现。
案例:
请思考以下代码段的输出结果:
int main()
{
int arr[10] = {0};
memset(arr, 1, 10);
for(int i = 0; i < sizeof(arr)/sizeof(arr[0]); i++) {
printf("%d ", arr[i]);
}
return 0;
}
答案:
输出结果为:16843009 16843009 257 0 0 0 0 0 0 0
因为memset是以字节为单位初始化内存的,执行
memset(arr, 1, 10);
后,会将arr地址开始往后10个字节全部改成1,但是一个int类型占4个字节。
memcmp
函数:int memcmp(const void *str1, const void *str2, size_t n);
参数:
- 形参1:
const void *str1
指向内存块的指针 - 形参2:
const void *str2
指向内存块的指针 - 形参3:
size_t count
要被比较的字节数 - 返回值:
- 如果返回值 < 0,则表示 str1 小于 str2
- 如果返回值 > 0,则表示 str2 小于 str1
- 如果返回值 = 0,则表示 str1 等于 str2
说明:
- memset函的功能是把存储区 str1 和存储区 str2 的前 n 个字节进行比较
- s1,s2为字符串时候memcmp(s1,s2,1)就是比较s1和s2的第一个字节的ascII码值