C语言字符串函数
C语言字符串函数全面详解(优化版)
我将基于您的讲义进行优化,为每个函数增加示例代码和大致实现原理,使内容更加全面和实用。
1. 字符串基础概念
字符串表示
在C语言中,字符串是以\0(空字符)结尾的字符数组。
// 三种常见的字符串表示方式
char str1[] = "Hello"; // 自动添加\0
char str2[6] = {'H','e','l','l','o','\0'}; // 手动添加\0
char *str3 = "Hello"; // 字符串字面量(常量区,不可修改)
// 验证字符串以\0结尾
char test[] = "hello";
printf("长度: %zu, 实际占用: %zu\n", strlen(test), sizeof(test));
// 输出: 长度: 5, 实际占用: 6 (包含\0)
实现原理:C语言通过检测空字符\0来确定字符串的结束位置,所有字符串函数都依赖这个约定。
2. 字符串长度函数
strlen
#include <string.h>
size_t strlen(const char *str);
功能:计算字符串长度(不包括\0)
示例:
char str[] = "Hello";
printf("长度: %zu\n", strlen(str)); // 输出: 5
// 错误用法:缺少\0的情况
char bad[] = {'a', 'b', 'c'}; // 没有\0结束符
printf("%zu\n", strlen(bad)); // 未定义行为,可能输出随机值
实现原理:
// strlen的模拟实现
size_t my_strlen(const char *str) {
size_t count = 0;
while (*str != '\0') { // 遍历直到遇到\0
count++;
str++;
}
return count;
}
// 另一种实现:指针减法
size_t my_strlen2(const char *str) {
const char *start = str;
while (*str != '\0') str++;
return str - start;
}
核心思想是遍历字符串直到遇到\0,统计字符数量。
3. 字符串复制函数
strcpy
char *strcpy(char *dest, const char *src);
功能:将src复制到dest(包括\0)
示例:
char dest[10];
strcpy(dest, "Hello"); // 安全
printf("复制结果: %s\n", dest); // 输出: Hello
// 危险用法:缓冲区溢出
// strcpy(dest, "This is too long"); // 可能导致程序崩溃
实现原理:
// strcpy的模拟实现
char *my_strcpy(char *dest, const char *src) {
char *ret = dest; // 保存目标字符串起始位置
while ((*dest++ = *src++)) { // 逐个字符复制,包括\0
; // 空循环体
}
return ret; // 返回目标字符串指针
}
strncpy
char *strncpy(char *dest, const char *src, size_t n);
功能:安全复制,最多复制n个字符
示例:
char dest[10];
strncpy(dest, "Hello World", 9);
dest[9] = '\0'; // 必须手动添加结束符
printf("安全复制: %s\n", dest); // 输出: Hello Wor
// 处理短于n的字符串
char dest2[10] = "*********";
strncpy(dest2, "Hi", 5);
printf("结果: %s\n", dest2); // 输出: Hi\0\0\0*** (需要手动添加\0)
实现原理:
char *my_strncpy(char *dest, const char *src, size_t n) {
size_t i;
for (i = 0; i < n && src[i] != '\0'; i++) {
dest[i] = src[i]; // 复制字符
}
for (; i < n; i++) {
dest[i] = '\0'; // 剩余空间填\0
}
return dest;
}
4. 字符串连接函数
strcat
char *strcat(char *dest, const char *src);
功能:将src连接到dest末尾
示例:
char path[50] = "/home/";
strcat(path, "user");
printf("路径: %s\n", path); // 输出: /home/user
char greeting[20] = "Hello";
strcat(greeting, " World");
printf("问候: %s\n", greeting); // 输出: Hello World
实现原理:
char *my_strcat(char *dest, const char *src) {
char *ret = dest;
// 找到dest的结尾
while (*dest != '\0') {
dest++;
}
// 追加src内容
while ((*dest++ = *src++)) {
; // 空循环体
}
return ret;
}
strncat
char *strncat(char *dest, const char *src, size_t n);
功能:安全连接,最多连接n个字符
示例:
char buf[20] = "Hello";
strncat(buf, " World!!!", 6); // 只连接" World"
printf("结果: %s\n", buf); // 输出: Hello World
// 自动添加\0是strncat的优点
char small[10] = "Hi";
strncat(small, " there everyone", 7); // 只连接" there"
printf("安全连接: %s\n", small); // 输出: Hi there
5. 字符串比较函数
strcmp
int strcmp(const char *str1, const char *str2);
返回值:
<0:str1 < str2=0:str1 = str2>0:str1 > str2
示例:
printf("比较结果: %d\n", strcmp("apple", "banana")); // 负数
printf("比较结果: %d\n", strcmp("apple", "apple")); // 0
printf("比较结果: %d\n", strcmp("banana", "apple")); // 正数
// 实际应用
if (strcmp(username, "admin") == 0) {
printf("管理员登录\n");
}
实现原理:
int my_strcmp(const char *str1, const char *str2) {
while (*str1 && (*str1 == *str2)) {
str1++;
str2++;
}
return *(unsigned char *)str1 - *(unsigned char *)str2;
}
strncmp
int strncmp(const char *str1, const char *str2, size_t n);
功能:比较前n个字符
示例:
// 比较前3个字符
if (strncmp("apple", "application", 3) == 0) {
printf("前3字符相同\n"); // 会执行
}
// 文件扩展名检查
char filename[] = "document.txt";
if (strncmp(filename + strlen(filename) - 4, ".txt", 4) == 0) {
printf("这是文本文件\n");
}
6. 字符串查找函数
strchr
char *strchr(const char *str, int c);
功能:查找字符c第一次出现的位置
示例:
char *p = strchr("hello", 'l');
if (p != NULL) {
printf("找到字符'l',位置: %ld\n", p - "hello"); // 输出: 2
}
// 提取文件扩展名
char filename[] = "photo.jpg";
char *dot = strchr(filename, '.');
if (dot != NULL) {
printf("扩展名: %s\n", dot + 1); // 输出: jpg
}
strstr
char *strstr(const char *haystack, const char *needle);
功能:查找子串
示例:
char *p = strstr("hello world", "world");
if (p != NULL) {
printf("找到子串: %s\n", p); // 输出: world
}
// 检查电子邮件格式
char email[] = "user@example.com";
if (strstr(email, "@") != NULL) {
printf("有效的电子邮件格式\n");
}
实现原理:
char *my_strstr(const char *haystack, const char *needle) {
if (*needle == '\0') return (char *)haystack;
for (const char *p = haystack; *p != '\0'; p++) {
const char *h = p, *n = needle;
while (*h && *n && *h == *n) {
h++;
n++;
}
if (*n == '\0') return (char *)p; // 找到子串
}
return NULL; // 未找到
}
7. 字符串分割函数
strtok
char *strtok(char *str, const char *delim);
功能:根据分隔符分割字符串
示例:
char data[] = "name=john;age=25;city=beijing";
char *token = strtok(data, ";=");
while (token != NULL) {
printf("字段: %s\n", token);
token = strtok(NULL, ";=");
}
// 输出:
// 字段: name
// 字段: john
// 字段: age
// 字段: 25
// 字段: city
// 字段: beijing
// CSV文件解析
char csv[] = "Alice,25,Engineer,Boston";
char *field = strtok(csv, ",");
while (field != NULL) {
printf("值: %s\n", field);
field = strtok(NULL, ",");
}
注意事项:
- 会修改原字符串,将分隔符替换为
\0 - 非线程安全,使用
strtok_r用于多线程
8. 内存操作函数(常用于字符串)
memcpy
void *memcpy(void *dest, const void *src, size_t n);
功能:内存复制(不处理重叠)
示例:
char src[] = "hello";
char dest[10];
memcpy(dest, src, 6); // 复制5字符+\0
printf("复制结果: %s\n", dest); // 输出: hello
// 复制结构体数组
struct Person { char name[20]; int age; };
struct Person people1[5], people2[5];
// ...初始化people1...
memcpy(people2, people1, sizeof(people1)); // 高效复制
memset
void *memset(void *ptr, int value, size_t n);
功能:内存设置
示例:
char buffer[100];
memset(buffer, 0, 100); // 清零初始化
memset(buffer, 'A', 10); // 前10字节设为'A'
buffer[10] = '\0';
printf("结果: %s\n", buffer); // 输出: AAAAAAAAAA
// 初始化结构体
struct Data { int id; char name[50]; };
struct Data item;
memset(&item, 0, sizeof(item)); // 全部初始化为0
9. 字符串转换函数
字符分类函数(ctype.h)
#include <ctype.h>
// 检查字符类型
char c = 'A';
if (isalpha(c)) printf("%c 是字母\n", c);
if (isdigit(c)) printf("%c 是数字\n", c);
if (isalnum(c)) printf("%c 是字母或数字\n", c);
// 输入验证
char input[100];
fgets(input, sizeof(input), stdin);
if (isdigit(input[0])) {
printf("第一个字符是数字\n");
}
大小写转换
// 单个字符转换
printf("小写: %c\n", tolower('A')); // 输出: a
printf("大写: %c\n", toupper('a')); // 输出: A
// 整个字符串转换
char text[] = "Hello World";
for (int i = 0; text[i] != '\0'; i++) {
text[i] = tolower(text[i]);
}
printf("全小写: %s\n", text); // 输出: hello world
10. 数值转换函数
字符串转数值
#include <stdlib.h>
// 基本转换
int num1 = atoi("123"); // 字符串转整数
double num2 = atof("3.14"); // 字符串转浮点数
long num3 = atol("1000000"); // 字符串转长整数
// 安全版本(可检测错误)
char *endptr;
long value = strtol("123abc", &endptr, 10);
if (*endptr == '\0') {
printf("转换成功: %ld\n", value);
} else {
printf("转换失败,无效字符: %s\n", endptr);
}
数值转字符串
#include <stdio.h>
// 使用sprintf
char buffer[20];
int num = 255;
sprintf(buffer, "%d", num);
printf("字符串: %s\n", buffer); // 输出: 255
// 安全版本snprintf
char safe_buf[10];
int written = snprintf(safe_buf, sizeof(safe_buf), "数值: %d", 12345);
if (written >= sizeof(safe_buf)) {
printf("缓冲区不足,需要%d字节\n", written + 1);
}
printf("安全格式化: %s\n", safe_buf);
11. 安全字符串处理技巧
自定义安全函数
// 安全的字符串复制
char* safe_strcpy(char *dest, const char *src, size_t dest_size) {
if (dest_size == 0) return dest;
strncpy(dest, src, dest_size - 1);
dest[dest_size - 1] = '\0'; // 确保字符串终止
return dest;
}
// 安全的字符串连接
char* safe_strcat(char *dest, const char *src, size_t dest_size) {
size_t dest_len = strlen(dest);
if (dest_len >= dest_size) return dest; // 没有空间了
size_t avail = dest_size - dest_len - 1; // 保留空间给\0
strncat(dest, src, avail);
return dest;
}
// 使用示例
char path[20] = "/home/";
safe_strcat(path, "verylongusername", sizeof(path));
printf("安全路径: %s\n", path); // 不会溢出
12. 综合应用示例
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
void comprehensive_string_demo() {
printf("=== C语言字符串函数综合演示 ===\n");
// 1. 基本操作
char text[100] = "Hello World";
printf("1. 原始: %s\n", text);
printf(" 长度: %zu\n", strlen(text));
// 2. 查找和替换
char *found = strstr(text, "World");
if (found) {
strncpy(found, "C Language", 11);
}
printf("2. 替换后: %s\n", text);
// 3. 分割字符串
char data[] = "apple,banana,cherry";
printf("3. 分割结果:\n");
char *token = strtok(data, ",");
while (token) {
printf(" - %s\n", token);
token = strtok(NULL, ",");
}
// 4. 数值转换
char num_str[] = "123.45";
double num = atof(num_str);
printf("4. 数值转换: %s -> %.2f\n", num_str, num);
// 5. 大小写转换
char mixed[] = "HeLLo WoRLd";
for (int i = 0; mixed[i]; i++) {
mixed[i] = tolower(mixed[i]);
}
printf("5. 小写转换: %s\n", mixed);
// 6. 内存操作应用
char source[] = "Memory operations";
char destination[50];
memcpy(destination, source, strlen(source) + 1); // +1包含\0
printf("6. 内存复制: %s\n", destination);
}
int main() {
comprehensive_string_demo();
return 0;
}
13. 常见错误和最佳实践
要避免的错误
// 错误1: 缓冲区溢出
char small[5];
// strcpy(small, "This is too long"); // 危险!会导致缓冲区溢出
// 正确做法:使用安全函数
strncpy(small, "Hello", sizeof(small));
small[sizeof(small) - 1] = '\0';
// 错误2: 使用未初始化的指针
char *ptr;
// strcpy(ptr, "hello"); // 未定义行为,可能崩溃
// 正确做法:分配内存
ptr = malloc(20 * sizeof(char));
if (ptr != NULL) {
strcpy(ptr, "hello");
free(ptr); // 记得释放
}
// 错误3: 修改字符串字面量
char *p = "constant";
// p[0] = 'C'; // 尝试修改只读内存,会崩溃
// 正确做法:使用字符数组
char safe[] = "modifiable";
safe[0] = 'M'; // 可以修改
最佳实践总结
- 总是检查缓冲区大小,使用
sizeof操作符获取数组大小 - 优先使用安全函数(带
n的版本,如strncpy、strncat) - 手动添加字符串结束符,特别是在使用
strncpy后 - 验证函数返回值,特别是动态内存分配和文件操作
- 使用
const保护不应修改的字符串 - 避免使用不安全的函数,如
sprintf,改用snprintf
通过理解每个函数的实现原理和正确用法,您可以编写出更加健壮、安全的C语言程序。本指南涵盖了C语言中最重要的字符串处理函数,掌握这些函数对于系统级编程至关重要。

浙公网安备 33010602011771号