在 C 语言中,字符指针(char*) 是处理字符串的核心工具之一。
它不仅能灵活指向单个字符,还能高效操作字符串常量。今天,我们就从基础用法到深层原理,全面探究字符指针的奥秘。
一、字符指针的基础:指向单个字符
字符指针最朴素的用法,是指向单个字符变量,通过指针间接访问或修改字符的值。
看这段代码:
cpp
#include <stdio.h>
int main()
{
char ch = 'w'; // 定义字符变量ch,值为'w'
char *pc = &ch; // 字符指针pc指向ch的地址
*pc = 'W'; // 通过解引用修改ch的值
printf("ch = %c\n", ch); // 输出:ch = W
return 0;
}
关键逻辑:
char *pc
声明了一个字符指针变量pc
。&ch
获取字符变量ch
的内存地址 ,并赋值给pc
,此时pc
就 "指向" 了ch
。*pc
是解引用操作 ,表示 "访问pc
指向地址的内容",因此*pc = 'W'
等价于直接修改ch
的值。
二、字符指针指向字符串常量:"地址" 而非 "内容"
字符指针更常见的场景,是指向字符串常量 。很多初学者会误解 "把整个字符串存到指针里",但本质是:指针存储的是字符串首字符的地址。
看这个例子:
cpp
#include <stdio.h>
int main()
{
const char* pstr = "hello bit."; // 字符指针pstr指向字符串首字符'h'的地址
printf("%s\n", pstr); // 输出:hello bit.
return 0;
}
深层理解:
"hello bit."
是字符串常量 ,C/C++ 会将其存储在只读数据区(防止程序意外修改常量内容)。const char* pstr
中的const
表示 "指针指向的内容是常量"------ 即不能通过pstr
修改字符串(比如*pstr = 'H'
会触发编译错误)。pstr
本身只存了首字符'h'
的内存地址 ,而非整个字符串的内容。printf("%s", pstr)
能打印完整字符串,是因为 C 语言约定:字符串以'\0'
(空字符)结尾,printf
会从首地址开始,直到遇到'\0'
才停止。
三、字符数组 vs 字符指针:内存模型的本质区别
为了彻底弄清字符指针与字符串的关系,我们对比字符数组 和指向常量字符串的字符指针------ 这也是面试高频考点。
看这段经典代码:
cpp
#include <stdio.h>
int main()
{
// 场景1:字符数组
char str1[] = "hello bit.";
char str2[] = "hello bit.";
// 场景2:指向常量字符串的字符指针
const char *str3 = "hello bit.";
const char *str4 = "hello bit.";
// 比较str1和str2
if (str1 == str2)
printf("str1 and str2 are same\n");
else
printf("str1 and str2 are not same\n");
// 比较str3和str4
if (str3 == str4)
printf("str3 and str4 are same\n");
else
printf("str3 and str4 are not same\n");
return 0;
}
运行结果:
str1 and str2 are not same
str3 and str4 are same
深度分析:
1. 字符数组 str1
和 str2
char str1[] = "hello bit.";
:程序会在栈内存 中为str1
分配一块空间,然后把字符串"hello bit."
的内容拷贝到这块空间里。str2
同理:也是在栈上单独分配一块新内存,再拷贝字符串内容。- 因此,
str1
和str2
是两块完全独立的内存 (地址不同),所以str1 == str2
为假(==
比较的是地址,而非内容)。
2. 字符指针 str3
和 str4
"hello bit."
是常量字符串 ,存储在只读数据区 。编译器为了优化内存,会让多个指针共享同一个常量字符串的内存(只要字符串内容相同)。str3
和str4
都存储了这个常量字符串首字符'h'
的地址 ,因此str3
和str4
指向的是同一块内存 ,所以str3 == str4
为真。
四、字符指针的实用场景
除了基础用法,字符指针在实际开发中还有诸多妙用:
1. 函数参数传递字符串(高效性)
通过字符指针传递字符串,无需拷贝整个字符串(只传地址),效率更高:
cpp
#include <stdio.h>
// 打印字符串的函数(接收字符指针)
void printString(const char *str)
{
printf("String: %s\n", str);
}
int main()
{
const char *msg = "Hello, Function!";
printString(msg); // 传递的是"地址",而非拷贝整个字符串
return 0;
}
2. 动态分配字符串(灵活性)
结合 malloc
动态分配内存,可灵活控制字符串长度:
cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
// 动态分配能存10个字符的空间(包含结束符'\0')
char *dynamicStr = (char *)malloc(10 * sizeof(char));
if (dynamicStr != NULL) // 检查内存是否分配成功
{
strcpy(dynamicStr, "Dynamic"); // 拷贝字符串到动态内存
printf("Dynamic String: %s\n", dynamicStr);
free(dynamicStr); // 用完后释放内存,避免泄漏
}
return 0;
}
五、总结
字符指针是 C 语言操作字符串的核心工具,核心要把握三点:
- 字符指针既可以指向单个字符 ,也可以指向字符串常量的首地址。
- 字符串常量存储在只读数据区,相同的常量字符串可能被编译器优化为 "共享内存"。
- 字符数组是 "存储字符串内容的内存块",而字符指针是 "指向字符串地址的工具"------ 二者在内存分配、比较逻辑上有本质区别。
掌握字符指针,不仅能更高效地操作字符串,也是理解 C 语言内存模型的关键一步。