<think>我们正在讨论C语言string.h库中的常用函数。根据引用内容,string.h定义了多种操作字符数组的函数。下面我将整理常用函数及其说明和注意事项。
引用[1]提到string.h包含一个变量类型、一个宏和各种函数。引用[2][3]列举了常用函数如strcpy, memcpy等。
我将按功能分类介绍,并给出示例代码(C语言)和注意事项。
注意:由于用户之前的问题是关于C++特有操作,但当前问题转向C语言string.h,因此我们聚焦于C语言的字符串处理函数。
一、字符串复制
1. strcpy: 复制字符串
原型:char *strcpy(char *dest, const char *src);
功能:将src指向的字符串(包括结束符'\0')复制到dest。返回dest。
注意:dest必须有足够的空间,且src和dest内存区域不能重叠(重叠应用memmove)。
2. strncpy: 复制指定长度的字符串
原型:char *strncpy(char *dest, const char *src, size_t n);
功能:复制src的前n个字符到dest。如果src长度小于n,则用'\0'填充剩余部分;如果大于等于n,则不会自动添加'\0'。
注意:需要手动添加结束符的情况。
二、字符串连接
1. strcat: 连接字符串
原型:char *strcat(char *dest, const char *src);
功能:将src字符串追加到dest结尾(覆盖dest的结束符),并添加新的结束符。返回dest。
注意:dest必须有足够的空间。
2. strncat: 连接指定长度的字符串
原型:char *strncat(char *dest, const char *src, size_t n);
功能:将src的前n个字符追加到dest,并在末尾添加'\0'。
注意:如果src长度大于n,则只复制前n个字符;否则复制整个src(包括结束符)并填充到n个字符?实际上,strncat总是会在结果字符串末尾添加一个结束符,且最多复制n个字符(不会复制src的结束符,然后添加自己的结束符)。所以,实际连接后字符串长度为strlen(dest)+n+1(因为添加了一个结束符)。
三、字符串比较
1. strcmp: 比较字符串
原型:int strcmp(const char *s1, const char *s2);
功能:按字典序比较s1和s2。返回0表示相等;大于0表示s1大于s2;小于0表示s1小于s2。
2. strncmp: 比较指定长度的字符串
原型:int strncmp(const char *s1, const char *s2, size_t n);
功能:比较前n个字符。
四、字符串查找
1. strchr: 查找字符第一次出现的位置
原型:char *strchr(const char *s, int c);
功能:返回s中第一次出现字符c的指针,未找到返回NULL。
2. strrchr: 查找字符最后一次出现的位置
原型:char *strrchr(const char *s, int c);
功能:返回s中最后一次出现字符c的指针。
3. strstr: 查找子串
原型:char *strstr(const char *haystack, const char *needle);
功能:返回haystack中第一次出现needle子串的位置,未找到返回NULL。
五、内存操作函数(虽然操作内存,但常与字符串相关)
1. memset: 内存设置
原型:void *memset(void *s, int c, size_t n);
功能:将s指向的内存区域的前n个字节设置为c(按字节填充)。常用于初始化。
2. memcpy: 内存复制
原型:void *memcpy(void *dest, const void *src, size_t n);
功能:从src复制n个字节到dest。不处理重叠(重叠时用memmove)。
3. memmove: 可处理重叠的内存复制
原型:void *memmove(void *dest, const void *src, size_t n);
功能:同memcpy,但保证重叠时正确复制。
4. memcmp: 内存比较
原型:int memcmp(const void *s1, const void *s2, size_t n);
功能:比较s1和s2的前n个字节(按字节比较,不考虑字符结束符)。
六、其他
1. strlen: 字符串长度
原型:size_t strlen(const char *s);
功能:返回s的长度(不包括结束符'\0')。
2. strtok: 字符串分割
原型:char *strtok(char *str, const char *delim);
功能:将字符串str按delim分割(首次调用传入str,后续传入NULL)。注意:该函数会修改原字符串。
3. strerror: 返回错误描述字符串
原型:char *strerror(int errnum);
功能:返回错误码errnum对应的描述字符串。
下面给出部分函数的示例代码:
```c
#include <stdio.h>
#include <string.h>
int main() {
// 1. strlen 示例
char str[] = "Hello";
printf("Length of '%s' is %zu\n", str, strlen(str)); // 5
// 2. strcpy 示例
char dest[20];
strcpy(dest, str); // dest = "Hello"
printf("After strcpy: %s\n", dest);
// 3. strcat 示例
strcat(dest, " World!"); // dest = "Hello World!"
printf("After strcat: %s\n", dest);
// 4. strcmp 示例
int cmp = strcmp("apple", "banana");
printf("Comparison result: %d\n", cmp); // 负数,因为"apple" < "banana"
// 5. strchr 示例
char *ptr = strchr(str, 'e'); // 指向第一个'e'
if (ptr) {
printf("Found 'e' at position: %ld\n", ptr - str); // 1
}
// 6. memset 示例
char arr[10];
memset(arr, 'A', 9);
arr[9] = '\0';
printf("After memset: %s\n", arr); // "AAAAAAAAA"
// 7. memcpy 示例
char src[] = "Copy this";
char dest2[20];
memcpy(dest2, src, strlen(src)+1); // 复制包括结束符
printf("After memcpy: %s\n", dest2);
return 0;
}
```
注意事项:
1. 使用strcpy、strcat等函数时,必须确保目标缓冲区足够大,否则会导致缓冲区溢出(安全问题)。
2. 使用strncpy、strncat等带n的函数时,注意n的含义以及是否自动添加结束符(strncpy不会在复制n个字符后添加结束符,而strncat会)。
3. 避免使用已废弃的函数(如strcpy_s等是C11可选的安全版本,但非所有编译器支持)。
4. 使用strtok时,注意其会修改原字符串,且不可重入(多线程环境下使用strtok_r)。
相关问题:
1. 为什么说strcpy函数不安全?如何避免?
2. 如何手动实现strlen函数?
3. memcpy和memmove的区别是什么?在什么情况下必须使用memmove?
4. 如何使用strtok分割带空格的字符串?
</think>### C语言 `string.h` 常用函数详解
`string.h` 是 C 语言标准库中用于字符串操作的核心头文件,提供字符串处理和内存操作函数。以下是主要函数分类及说明:
---
#### 一、**字符串操作函数**
1. **`strlen` - 字符串长度**
```c
size_t strlen(const char *str);
```
- 计算字符串长度(不含结束符 `\0`)
- 示例:`strlen("Hello")` 返回 `5`
- ⚠️ 传入 `NULL` 会导致未定义行为
2. **`strcpy` / `strncpy` - 字符串复制**
```c
char *strcpy(char *dest, const char *src); // 完全复制
char *strncpy(char *dest, const char *src, size_t n); // 指定长度复制
```
- `strcpy` 将 `src` 复制到 `dest`(包括 `\0`)
- `strncpy` 复制前 `n` 字节:
- 若 `src` 长度 < `n`,剩余部分填 `\0`
- 若 `src` 长度 ≥ `n`,**不会自动添加 `\0`**
- ⚠️ 必须确保 `dest` 有足够空间
3. **`strcat` / `strncat` - 字符串拼接**
```c
char *strcat(char *dest, const char *src); // 完全拼接
char *strncat(char *dest, const char *src, size_t n); // 指定长度拼接
```
- 将 `src` 追加到 `dest` 末尾(覆盖 `dest` 的 `\0`)
- `strncat` 总是会在结果末尾添加 `\0`
- ⚠️ `dest` 缓冲区溢出风险高
4. **`strcmp` / `strncmp` - 字符串比较**
```c
int strcmp(const char *s1, const char *s2); // 完全比较
int strncmp(const char *s1, const char *s2, size_t n); // 指定长度比较
```
- 返回值:
- `<0`:`s1` < `s2`
- `=0`:相等
- `>0`:`s1` > `s2`
- 示例:`strcmp("apple", "banana")` 返回负数
5. **`strstr` - 查找子串**
```c
char *strstr(const char *haystack, const char *needle);
```
- 在 `haystack` 中查找 `needle` 首次出现位置
- 示例:`strstr("Hello World", "Wo")` 返回 `"World"` 的指针
- ⚠️ 未找到时返回 `NULL`
6. **`strchr` / `strrchr` - 查找字符**
```c
char *strchr(const char *s, int c); // 首次出现
char *strrchr(const char *s, int c); // 最后一次出现
```
- 示例:`strchr("Hello", 'l')` 返回第一个 `'l'` 的地址
---
#### 二、**内存操作函数**
1. **`memset` - 内存填充**
```c
void *memset(void *s, int c, size_t n);
```
- 将 `s` 的前 `n` 字节设置为 `c`(按字节填充)
- 示例:初始化数组归零
```c
int arr[10];
memset(arr, 0, sizeof(arr)); // 全部置0
```
2. **`memcpy` / `memmove` - 内存复制**
```c
void *memcpy(void *dest, const void *src, size_t n); // 不处理重叠
void *memmove(void *dest, const void *src, size_t n); // 处理重叠
```
- 关键区别:
- `memcpy` 要求源和目标内存不重叠
- `memmove` 允许重叠(如数组内移动数据)
- 示例:数组元素左移
```c
int arr[5] = {1,2,3,4,5};
memmove(arr, arr+1, 4*sizeof(int)); // {2,3,4,5,5}
```
3. **`memcmp` - 内存比较**
```c
int memcmp(const void *s1, const void *s2, size_t n);
```
- 比较前 `n` 字节(可比较任意二进制数据)
- 返回值同 `strcmp`
---
#### 三、**其他实用函数**
1. **`strtok` - 字符串分割**
```c
char *strtok(char *str, const char *delim);
```
- 按 `delim` 分割字符串(首次调用需传字符串,后续传 `NULL`)
- ⚠️ 修改原字符串,线程不安全(可用 `strtok_r` 替代)
- 示例:分割逗号分隔字符串
```c
char str[] = "A,B,C";
char *token = strtok(str, ",");
while (token != NULL) {
printf("%s\n", token);
token = strtok(NULL, ",");
}
// 输出:A → B → C
```
2. **`strerror` - 错误描述**
```c
char *strerror(int errnum);
```
- 将错误码转换为可读字符串
- 示例:`perror(strerror(errno))`
---
### ️ 关键注意事项
1. **缓冲区溢出风险**
- `strcpy`, `strcat` 等函数不检查目标缓冲区大小
- 安全替代方案:
```c
strncpy(dest, src, dest_size-1); // 预留空间给 \0
dest[dest_size-1] = '\0'; // 手动添加结束符
```
2. **结束符 `\0` 处理**
- `strncpy` 在复制满 `n` 字节时**不会添加 `\0`**
- `strncat` 总是添加 `\0`(需额外预留 1 字节)
3. **重叠内存操作**
- 源和目标重叠时,必须用 `memmove` 代替 `memcpy`
- 错误示例:
```c
char s[] = "hello";
memcpy(s, s+1, 4); // 未定义行为,应用 memmove
```
4. **空指针风险**
- 所有函数均不检查 `NULL` 指针(传入 `NULL` 导致崩溃)
5. **二进制安全**
- `mem*` 系列函数可操作任意数据(如图像、结构体)
- `str*` 系列遇到 `\0` 停止
---
### 函数总结表
| 函数 | 用途 | 安全风险 | 关键特性 |
|--------------|---------------------|---------------|------------------------|
| `strlen` | 字符串长度 | 传入 `NULL` | 不计数 `\0` |
| `str(n)cpy` | 字符串复制 | 缓冲区溢出 | `strncpy` 可能缺 `\0` |
| `str(n)cat` | 字符串拼接 | 缓冲区溢出 | `strncat` 保证 `\0` |
| `str(n)cmp` | 字符串比较 | 无 | 二进制不安全 |
| `memcpy` | 内存复制 | 内存重叠 | 高性能,不处理重叠 |
| `memmove` | 内存复制(安全) | 无 | 处理内存重叠 |
| `memset` | 内存填充 | 无 | 按字节初始化 |
| `strtok` | 字符串分割 | 修改原字符串 | 非线程安全 |
---