📝 字符串的三种表示形式
1. 字符数组
char buf[128] = "hello"; // 在栈上分配的可修改内存
2. 字符指针
char* str_ptr = buf; // 指向字符数组的指针
3. 字符串常量
const char* str_literal = "hello,world"; // 只读的字符串常量
重要说明:虽然都使用 char* 类型,但字符串常量实际上是 const char* 类型。
🔒 字符串常量详解
特性说明
const char* str = "hello,world"; // 正确:指向字符串常量
// str[0] = 'H'; // ❌ 错误!字符串常量是只读的
为什么是const char*?
- 字符串常量存储在只读内存区(常量区)
- 修改会导致未定义行为
为什么不需要长度信息?
- C风格字符串以
\0(空字符)作为结束标记 - 遇到
\0即认为字符串结束
🔄 字符串基本操作
1. 字符串遍历
#include <stdio.h>
void show_string(const char* str) {
printf("遍历字符串: ");
for (int i = 0; str[i] != '\0'; i++) {
printf("%c ", str[i]);
}
printf("\n");
}
// 测试示例
int main() {
show_string("Hello");
return 0;
}
输出:
遍历字符串: H e l l o
2. 字符串长度计算
#include <stdio.h>
int get_length(const char* str) {
int count = 0;
while (str[count] != '\0') {
count++;
}
return count;
}
// 测试示例
int main() {
char str1[128] = {'h', 'e', '\0', 'n', 'i', 'c', 'e'}; // 长度为2
char str2[128] = {'\0', 'h', 'e', 'l', 'l', 'o'}; // 长度为0
printf("str1长度: %d\n", get_length(str1)); // 输出: 2
printf("str2长度: %d\n", get_length(str2)); // 输出: 0
printf("'Hello'长度: %d\n", get_length("Hello")); // 输出: 5
return 0;
}
3. 字符串复制
#include <stdio.h>
void string_copy(const char* src, char* dst) {
int i = 0;
while (1) {
dst[i] = src[i];
if (src[i] == '\0') break;
i++;
}
}
// 测试示例
int main() {
char src[] = "hello";
char dst[128]; // 确保目标缓冲区足够大
string_copy(src, dst);
printf("复制结果: %s\n", dst);
// ❌ 这不是字符串复制!
char buf[128] = "hello";
char* p = buf; // 这只是指针赋值,不是内容复制
return 0;
}
复制注意事项:
- 目标缓冲区必须足够大
- 必须复制结束符
\0 - 指针赋值 ≠ 字符串复制
4. 字符串比较
#include <stdio.h>
#include <string.h>
int string_compare(const char* str1, const char* str2) {
int i = 0;
while (str1[i] != '\0' && str2[i] != '\0') {
if (str1[i] < str2[i]) return -1;
if (str1[i] > str2[i]) return 1;
i++;
}
if (str1[i] == '\0' && str2[i] == '\0') return 0;
if (str1[i] == '\0') return -1;
return 1;
}
// 测试示例
int main() {
char input[128];
printf("请输入 'yes' 或 'no': ");
scanf("%s", input);
// ✅ 正确:使用strcmp或自定义比较函数
if (string_compare(input, "yes") == 0) {
printf("OK\n");
} else {
printf("Cancel\n");
}
// ❌ 错误:比较的是地址,不是内容
// if (input == "yes") { ... }
return 0;
}
比较规则:
- 逐个字符比较ASCII值
- 所有字符相同且同时结束 → 相等
- 发现不同字符 → 立即返回比较结果
- 一个字符串是另一个的前缀 → 较短的小于较长的
🗑️ 字符串删除操作
基础删除方法(高成本)
#include <stdio.h>
#include <string.h>
// 从指定位置删除一个字符(需要移动后续所有字符)
void erase_char(char text[], int index) {
int len = strlen(text);
if (index < 0 || index >= len) return;
for (int i = index; i < len; i++) {
text[i] = text[i + 1]; // 后续字符前移
}
}
// 测试示例
int main() {
char text[] = "hello";
printf("原始: %s\n", text);
erase_char(text, 1); // 删除 'e'
printf("删除后: %s\n", text); // 输出: hllo
// 删除末尾字符(低成本)
text[strlen(text) - 1] = '\0';
printf("删除末尾后: %s\n", text); // 输出: hll
return 0;
}
例如:从一个字符串中间删除一个字符
char str[10] = "hello"

原字符串

删除 'e' , 后面的字符需要前移 - 结果:"hllo"
从字符串中间删除字符,这个操作是一个成本比较高的操作,每次都要移动很多数据。
高效删除方法:复制替代法
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
// 删除字符串中所有指定字符(高效版本)
void erase_all_chars(char text[], char char_to_delete) {
int len = strlen(text);
int new_index = 0;
// 原地删除,不需要额外内存
for (int i = 0; i < len; i++) {
if (text[i] != char_to_delete) {
text[new_index] = text[i];
new_index++;
}
}
text[new_index] = '\0'; // 添加新的结束符
}
// 需要额外内存的版本(适用于复杂删除规则)
void erase_all_chars_with_malloc(char text[], char char_to_delete) {
int len = strlen(text);
char* temp = (char*)malloc(len + 1); // 临时缓冲区
int new_index = 0;
for (int i = 0; i < len; i++) {
if (text[i] != char_to_delete) {
temp[new_index] = text[i];
new_index++;
}
}
temp[new_index] = '\0';
strcpy(text, temp); // 复制回原数组
free(temp); // 释放临时内存
}
// 测试示例
int main() {
char text[] = "China is a great country with a long history";
printf("原始: %s\n", text);
erase_all_chars(text, 'a'); // 删除所有 'a'
printf("删除所有'a'后: %s\n", text);
return 0;
}
输出:
原始: China is a great country with a long history
删除所有'a'后: Chin is gret country with long history
➕ 字符串插入操作
基础插入方法(高成本)
#include <stdio.h>
#include <string.h>
// 在指定位置插入字符(需要移动后续所有字符)
void insert_char(char text[], int index, char char_to_insert) {
int len = strlen(text);
// 检查边界和缓冲区大小
if (index < 0 || index > len) return;
// 从后向前移动字符,为新字符腾出空间
for (int i = len; i >= index; i--) {
text[i + 1] = text[i];
}
text[index] = char_to_insert; // 插入新字符
}
// 测试示例
int main() {
char text[128] = "hello"; // 确保缓冲区足够大
printf("原始: %s\n", text);
insert_char(text, 2, 'X'); // 在位置2插入'X'
printf("插入后: %s\n", text); // 输出: heXllo
return 0;
}
高效插入方法:复制构建法
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
// 高效插入字符(使用临时缓冲区)
void efficient_insert(char text[], int index, char char_to_insert) {
int len = strlen(text);
char* temp = (char*)malloc(len + 2); // 为新字符串分配空间
// 复制前半部分
for (int i = 0; i < index; i++) {
temp[i] = text[i];
}
// 插入新字符
temp[index] = char_to_insert;
// 复制后半部分
for (int i = index; i <= len; i++) {
temp[i + 1] = text[i];
}
strcpy(text, temp); // 复制回原数组
free(temp); // 释放临时内存
}
// 测试示例
int main() {
char text[128] = "hello world";
printf("原始: %s\n", text);
efficient_insert(text, 5, ',');
printf("插入后: %s\n", text); // 输出: hello, world
return 0;
}
💡 性能优化总结
操作成本分析
| 操作 | 时间复杂度 | 说明 |
|---|---|---|
| 访问单个字符 | O(1) | 直接通过索引访问 |
| 遍历字符串 | O(n) | 需要扫描整个字符串 |
| 删除末尾字符 | O(1) | 只需修改结束符 |
| 删除中间字符 | O(n) | 需要移动后续所有字符 |
| 插入字符 | O(n) | 需要移动后续所有字符 |
最佳实践建议
- 避免频繁的中间插入/删除:考虑使用链表等其他数据结构
- 批量操作使用复制法:一次性处理比多次移动更高效
- 确保缓冲区足够大:特别是进行插入操作时
-
使用标准库函数:如
strcpy,strcat,strcmp等,它们经过高度优化