这个实例非常适合作为第1002个例子,因为它巧妙地结合了C语言中几个核心且重要的概念:指针、数组、循环和字符串处理,理解了它,你对C语言的指针操作能力将会有一个质的飞跃。

(图片来源网络,侵删)
C语言实例 1002:使用指针反转字符串
实例目标
编写一个C语言程序,实现一个自定义函数 str_rev,该函数接收一个字符串作为参数,并使用指针操作原地(in-place,即不创建新的字符串)反转这个字符串,在 main 函数中调用该函数并打印反转前后的结果。
核心知识点解析
要实现这个功能,我们需要深入理解以下几个C语言概念:
- 字符串在C语言中的表示:在C语言中,字符串本质上是一个以空字符
'\0'结尾的字符数组。char str[] = "hello";实际上在内存中存储的是'h', 'e', 'l', 'l', 'o', '\0'。 - 指针与数组的关系:数组名
str会“衰变”为其首元素的地址。char *p = str;和char *p = &str[0];是等价的,我们可以通过指针来遍历和访问数组中的元素。 - 指针的算术运算:对指针进行加减运算,会使指针移动到相应类型大小的内存位置。
p++会让char类型的指针p向后移动1个字节,指向下一个字符。 - 原地反转算法:
- 使用两个指针,一个指向字符串的开头(
left),一个指向字符串的末尾(right)。 - 交换
left和right指针所指向的字符。 left指针向后移动一位 (left++),right指针向前移动一位 (right--)。- 重复步骤2和3,直到
left指针不再小于right指针,此时反转完成。
- 使用两个指针,一个指向字符串的开头(
代码实现
下面是完整的、带有详细注释的C语言代码。
#include <stdio.h>
/**
* @brief 使用指针原地反转字符串
* @param str 要反转的字符数组(字符串)
* 注意:这里使用 char* 类型,因为我们要修改字符串内容。
* 如果不想修改原字符串,应该传递 const char*,但本例需要修改。
*/
void str_rev(char *str) {
// 1. 边界条件检查
// str 是 NULL 指针,或者 str 指向的字符串为空(第一个字符就是 '\0'),则无需操作。
if (str == NULL || *str == '\0') {
return;
}
// 2. 初始化两个指针
// left_ptr 指向字符串的开头
char *left_ptr = str;
// right_ptr 指向字符串的末尾(即 '\0' 的前一个字符)
// 我们需要先找到字符串的末尾
char *right_ptr = str;
while (*right_ptr != '\0') {
right_ptr++;
}
// 循环结束后,right_ptr 指向 '\0',所以要回退一步
right_ptr--;
// 3. 交换字符,直到指针相遇或交叉
while (left_ptr < right_ptr) {
// 交换 left_ptr 和 right_ptr 所指向的字符
// 使用一个临时变量 temp 来完成交换
char temp = *left_ptr;
*left_ptr = *right_ptr;
*right_ptr = temp;
// 移动指针,准备下一次交换
left_ptr++;
right_ptr--;
}
}
/**
* @brief 辅助函数:打印字符串
* @param str 要打印的字符串
*/
void print_str(const char *str) {
// 使用 const char* 表示函数不会修改传入的字符串内容,这是一种良好的编程习惯
if (str == NULL) {
printf("(NULL)\n");
return;
}
while (*str != '\0') {
putchar(*str);
str++;
}
printf("\n");
}
int main() {
// 定义一个字符数组来存储字符串
char my_string[] = "Hello, C Pointers!";
printf("--- C语言实例 1002: 使用指针反转字符串 ---\n\n");
// 打印反转前的字符串
printf("反转前的字符串是: ");
print_str(my_string);
// 调用自定义的反转函数
str_rev(my_string);
// 打印反转后的字符串
// 注意:my_string 数组中的内容已经被修改
printf("反转后的字符串是: ");
print_str(my_string);
// --- 更多测试用例 ---
printf("\n--- 更多测试用例 ---\n");
// 测试空字符串
char empty_str[] = "";
printf("测试空字符串 \"\": ");
str_rev(empty_str);
print_str(empty_str);
// 测试单字符字符串
char single_char_str[] = "A";
printf("测试单字符 \"A\": ");
str_rev(single_char_str);
print_str(single_char_str);
// 测试回文字符串
char palindrome_str[] = "madam";
printf("测试回文字符串 \"madam\": ");
str_rev(palindrome_str);
print_str(palindrome_str);
return 0;
}
代码步骤详解
#include <stdio.h>:包含标准输入输出库,以便使用printf和putchar等函数。- *`str_rev(char str)` 函数**:
- 参数:
char *str是一个字符指针,它接收main函数中字符数组my_string的首地址。 - 边界检查:
if (str == NULL || *str == '\0')是一个健壮性考虑,如果传入空指针或空字符串,函数直接返回,避免无效操作。 - 寻找末尾指针:
while (*right_ptr != '\0') { right_ptr++; }循环会一直执行,直到right_ptr指向字符串的结束符'\0'。right_ptr--让它指向最后一个有效字符。 - 核心交换循环:
while (left_ptr < right_ptr)是循环的条件,当左指针在右指针的左边时,继续交换。 - 交换操作:
char temp = *left_ptr;:取出左指针指向的字符存入临时变量。*left_ptr = *right_ptr;:将右指针指向的字符赋值给左指针指向的位置。*right_ptr = temp;:将临时变量中的字符(原来的左字符)赋值给右指针指向的位置。
- 指针移动:
left_ptr++;和right_ptr--;让两个指针向中间靠拢。
- 参数:
- *`print_str(const char str)` 函数**:
- 这是一个辅助函数,用于打印字符串,使用
const char *是一个好的实践,它告诉调用者和编译器这个函数承诺不会修改字符串内容,增加了代码的安全性。
- 这是一个辅助函数,用于打印字符串,使用
main()函数:char my_string[] = "Hello, C Pointers!";:定义并初始化一个字符数组。- 调用和打印:清晰地展示了在调用
str_rev前后字符串的变化。 - 测试用例:提供了空字符串、单字符字符串和回文字符串的测试,验证了函数的鲁棒性。
运行结果
将以上代码编译并运行,你将得到如下输出:

(图片来源网络,侵删)
--- C语言实例 1002: 使用指针反转字符串 ---
反转前的字符串是: Hello, C Pointers!
反转后的字符串是: !sretniP ,C olleH
--- 更多测试用例 ---
测试空字符串 "":
测试单字符 "A": A
测试回文字符串 "madam": madam
总结与扩展
这个实例完美地展示了C语言指针的强大和灵活,通过直接操作内存地址,我们高效地完成了字符串反转任务。
可以思考的扩展问题:
-
str_rev函数能接受字符串字面量(如str_rev("hello");)作为参数吗?- 不能,在大多数C实现中,字符串字面量存储在只读的内存区域(如常量区/代码段),尝试修改它会导致未定义行为,通常是程序崩溃(段错误),这就是为什么我们的
main函数中使用的是char my_string[],它被分配在可读写的栈内存上。
- 不能,在大多数C实现中,字符串字面量存储在只读的内存区域(如常量区/代码段),尝试修改它会导致未定义行为,通常是程序崩溃(段错误),这就是为什么我们的
-
如何实现一个不区分大小写的字符串比较函数?
- 这也是一个经典的指针应用,可以创建两个指针分别遍历两个字符串,在比较每个字符时,使用
tolower()或toupper()函数将字符转换为统一的大小写后再比较。
- 这也是一个经典的指针应用,可以创建两个指针分别遍历两个字符串,在比较每个字符时,使用
通过掌握这类实例,你将能够更自信地使用C语言去解决更复杂的底层问题。
