在学习完成了C语言的的指针这一大难点后,我们将继续学习C语言里面的库函数,其中字符函数也是比较重要的一类。
零 . 字符函数:
下面列出了头文件 ctype.h 中定义的函数。
这些函数用于测试字符是否属于某种类型,这些函数接受 int 作为参数,它的值必须是 EOF 或表示为一个无符号字符。
如果参数 c 满足描述的条件,则这些函数返回非零(true)。如果参数 c 不满足描述的条件,则这些函数返回
这些函数所需要的头文件是ctype.h
,在这里我们只需要练习一个函数就ok了,其他的函数是非常类似的。借助下面的练习完成。
1.1. 练习1:将字符串中的小写字母改成大写字母,其他不变
方法一:利用ASCII码来改变
代码如下:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<ctype.h>
//练习1:将字符串中的小写字母改成大写字母,其他不变
int main()
{char str[] = "hElLo WOrld";char c;int i = 0;while (str[i]){char c = str[i];if (islower(c))c -= 32;putchar(c);i++;}return 0;
}
我们来看这段代码:1. 其中islower就是判断字母是小写,如果是小写,那么减去32(小写字母(ASCII 97–122)减去32 → 大写字母(ASCII 65–90),如果是大写就不进去if内部。
2. putchar输出转换后的字符(或原大写字符)到标准输出。
3. 循环条件:str[i]
检查当前字符是否为非零(即非 '\0'
),遇到结束符时终止
4. char c
声明字符变量,用于存储单个字符,将 str[i]
的值(字符)复制到变量 c
中,二者内存独立,修改 c
不影响原字符串
方法二:利用字符转换函数
代码如下:
//练习1:将字符串中的小写字母改成大写字母,其他不变
int main()
{char str[] = "hElLo WOrld";char c;int i = 0;while (str[i]){char c = str[i];c = toupper(c);putchar(c);i++;}return 0;
}
此时就不需要判断了,如果加了判断也是ok的,这样就能把字符进行大写化
toupper
就是:to upper,往上-大写。
tolower
就是:to lower,往下-小写。
一. 重要的字符函数:
1. strlen函数
1. 1.strlen 函数的使用:
strlen是库函数,本质是通过计算字符串里面的\0前面的字符的个数,他是可以用来计算字符串,但是不能用来计算字符数组的(一般字符数组里面不含\0)
同时返回值是一个无符号数,需要打印的话使用:%zd;传入字符串的首地址就ok
1.2 . strlen函数的模拟:
代码如下:
size_t my_strlen(char* c)
{int count = 0;while (*c){count++;c++;}return count;
}int main()
{char* c = "helso";int ret = my_strlen(c);printf("%zd", ret);
}
还可以做一下稍微的改进:
size_t my_strlen(char* c)
{int count = 0;while (*c++){count++;}return count;
}int main()
{char* c = "helsole";size_t ret = my_strlen(c);printf("%zd", ret);
}
先来看这个第二个代码的while循环:
*c++
包含两步操作:-
*c
**:解引用指针c
,获取当前字符的值。 -
c++
:指针后移(指向下一个字符),副作用发生在解引用之后。
-
可以详细看这张图标来完成。我们还可以继续尝试递归的方式还有
#include<assert.h>
size_t my_strlen(const char* str)
{assert(str);if (*str == '\0')return 0;elsereturn 1 + my_strlen(str + 1);
}int main()
{char a[] = "heijos";size_t ret = my_strlen(a);printf("%zd", ret);
}
2.strcpy函数
2.1 strcpy的使用:
C 库函数 **char *strcpy(char dest, const char src) 把 src 所指向的字符串复制到 dest。
该函数返回一个指向最终的目标字符串 dest 的指针。
cpy顾名思义就是靠拷贝函数:将原字符串拷进目标字符串(src–>dest),需要注意的是:
需要注意的是如果目标数组 dest 不够大,而源字符串的长度又太长,可能会造成缓冲溢出的情况,同时原来的字符串也是以‘\0’结尾。(\0也会被复制)
#include<string.h>
int main()
{char* c = "copy to d";char d[10] = { 0 };strcpy(d, c);printf("%s", d);
}
网址给出的案例
最后结果:
strcpy代码模拟:
我们尝试模拟,代码如下:
char* my_strcpy(char* dest, const char* str)
{int tmp = dest;while (*str){*dest = *str;dest++;str++;}return dest;
}#include<string.h>
int main()
{char* c = "copy to d";char d[10] = { 0 };my_strcpy(d, c);printf("%s", d);
}
这段代码就完成了复制;我们还可以做一下改进:
char* my_strcpy(char* dest, const char* str)
{int tmp = dest;while (*dest++ = *str++);return dest;
}#include<string.h>
int main()
{char* c = "copy to d";char d[10] = { 0 };my_strcpy(d, c);printf("%s", d);
}
以下分步拆解首次循环逻辑(假设 str
初始指向字符串 "copy to d"
的首字符 'c'
):
步骤 | 操作 | 指针状态 | **dest 赋值结果** |
---|---|---|---|
1. while (*str++) 检查 | 先取 *str 的值('c' ,非零 → 条件为真) → **再执行 str++ ** | str 指向 'o' | 未执行赋值 |
2. 进入循环体 | 执行 *dest = *str | str 仍指向 'o' | dest[0] = 'o' |
3. dest++ | 目标指针后移 | dest 指向下一个位置 | - |
关键问题:
- 首字符
'c'
被完全跳过:条件判断时检查了*str
('c'
),但str++
在进入循环体前已执行,导致循环内*str
实际指向的是第二个字符'o'
。 - **
dest[0]
未被覆盖:因初始化为0
(char d[10] = {0}
),且未被赋值 → 首字节仍是'\0'
,导致printf
输出空字符串。
你的问题直指C语言中后置自增运算符(*str++
)的核心陷阱**:它会在判断条件后立刻移动指针,导致循环内操作错位。以下是详细分析:
🔍 问题核心:while (*str++)
的执行顺序
以下分步拆解第二段代码的首次循环逻辑(假设 str
初始指向字符串 "copy to d"
的首字符 'c'
)
步骤 | 操作 | 指针状态 | **dest 赋值结果** |
---|---|---|---|
1. while (*str++) 检查 | 先取 *str 的值('c' ,非零 → 条件为真) → **再执行 str++ ** | str 指向 'o' | 未执行赋值 |
2. 进入循环体 | 执行 *dest = *str | str 仍指向 'o' | dest[0] = 'o' |
3. dest++ | 目标指针后移 | dest 指向下一个位置 | - |
关键问题:
- 首字符
'c'
被完全跳过:条件判断时检查了*str
('c'
),但str++
在进入循环体前已执行,导致循环内*str
实际指向的是第二个字符'o'
。 - **
dest[0]
未被覆盖**:因初始化为0
(char d[10] = {0}
),且未被赋值 → 首字节仍是'\0'
,导致printf
输出空字符串。
⚠️ 对比:三种自增写法的差异
以下对比你提供的三版代码中 while
条件的行为:
代码版本 | **while 条件** | 行为 | 是否复制首字符 |
---|---|---|---|
第一段 | while (*str) | 检查当前字符非 '\0' ,不自增指针 → 循环内复制当前字符 | ✅ 是 |
第二段 | while (*str++) | 检查后立刻自增 → 循环内复制的是下一个字符 | ❌ 否(跳过首字符) |
第三段 | while (*dest++ = *str++) | 先复制当前字符(包括 '\0' ),再自增 → 完整复制 | ✅ 是 |
💡 核心区别:后置自增(
i++
)在表达式中“先返回值,再自增”,导致条件判断和循环内操作使用的指针位置不同步
3.strcat函数
3.1 strcat函数的使用:
C 库函数 **char *strcat(char dest, const char src) 把 src 所指向的字符串追加到 dest 所指向的字符串的结尾
该函数返回一个指向最终的目标字符串 dest 的指针。
两个字符串要求以\0结尾。
正确代码如下
int main()
{char c[22] = "i like";char* b = " you";strcat(c, b);printf("%s", c);
}
避免出现目标空间不够,会出先错误。
3.2 strcat的模拟实现
通过观察我们做出以下代码
char* my_strcat(char* dest, char* str)
{char *tmp = dest;while (*dest++);dest--;while ((*dest++ = *str++));return tmp;
}int main()
{char c[22] = "i like";char* b = " you";my_strcat(c, b);printf("%s", c);
}
为什么有dest–
在 C 语言的字符串操作中,while (*dest++)
这种循环是定位目标字符串末尾的常见写法,但它的执行逻辑会导致 dest
指针最终指向 '\0'
之后的位置,而非 '\0'
本身。以下是详细分析:
🔍 **while (*dest++)
的执行机制**
假设目标字符串 dest
为 "i like"
(存储在数组 c[22]
中),其内存布局如下('\0'
在索引 6 处):
索引 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | … |
---|---|---|---|---|---|---|---|---|---|
字符 | i | l | i | k | e | \0 | ? | … |
循环的执行步骤如下:
- **检查条件
*dest
**:- 若
*dest
非'\0'
,条件为真,进入循环体(但此循环无显式循环体,仅执行指针自增)。
- 若
- **执行自增
dest++
**:- 后置自增:先返回
dest
的当前值,再将dest
指向下一位置。
- 后置自增:先返回
- **重复直到遇到
'\0'
**:- 当
dest
指向c[6]
('\0'
)时:- 条件
*dest
为0
(假),但自增仍会执行 →dest
移动到c[7]
。
因此dest–是必须要有的。
- 条件
- 当
4.strcmp函数
4.1strcmp的使用
顾名思义就是比较两个字符串的大小:
我们先说以下cmp的比叫方式:
字符串通过标准库函数 strcmp()
实现比较,核心逻辑为:
- 逐字符ASCII值对比:从首字符开始,逐个比较字符的ASCII值
- 终止条件:
- 遇到不相同的字符 → 返回两字符ASCII差值(正数/负数)。
- 遇到
\0
结束符 → 若长度不同,短字符串较小;否则相等(返回0)
![[Pasted image 20250613170755.png]]
使用说明,代码如下:
int main()
{char c[22] = "i like";char* b = " you";int ret = strcpmy(c, b);if (ret = 0)printf("一样大");if (ret < 0)printf("c < b");else {printf("c > b");}
}
4.2.strcmp的模拟实现:
第一次尝试而写的错误代码,没有考虑到\0
,就是str的结束
int my_strcmp(const char *str1,const char * str2 )
{while (*str1 - *str2 == 0){str1++;str2++;}return *str1 - *str2;
}int main()
{char c[22] = "i like";char* b = " you";int ret = my_strcmp(c, b);if (ret == 0)printf("一样大");if (ret < 0)printf("c < b");else {printf("c > b");}
}
改进过后的代码就有:
int my_strcmp(const char *str1,const char * str2 )
{while (*str1 != 0 && *str1 - *str2 == 0){//只有未到达/0时才继续str1++;str2++;}return *str1 - *str2;//当到达0时一定比没到到达的小,这样比较出来了
}int main()
{char c[22] = "i like";char* b = " you";int ret = my_strcmp(b,c);if (ret == 0)printf("一样大");if (ret < 0)printf("c < b");else {printf("c > b");}
}