const char* 、char*和char[]的区别
一、char* (字符指针)
定义:指向字符数据的指针
特点:
- 可以指向字符串字面量或字符数组
- 指针本身可修改(可以指向不同地址)
- 指向的内容可能可修改(取决于指向的内存类型)
内存分配:
- 通常指向堆、栈或静态存储区的内存
char* str1 = "Hello"; // 指向字符串字面量(只读区)
char buffer[] = "World";
char* str2 = buffer; // 指向可修改的数组str2[0] = 'w'; // 允许:修改指向的内容
str1 = str2; // 允许:修改指针本身
// str1[0] = 'h'; // 危险:尝试修改只读内存(未定义行为)
二、const char* (指向常量字符的指针)
定义:指向不可修改字符数据的指针
特点:
- 指针本身可修改(可以指向不同地址)
- 指向的内容不可修改
- 提高代码安全性和表达意图
典型用途:
- 字符串字面量
- 函数参数(表示函数不会修改传入的字符串)
const char* str = "Immutable"; // 推荐的安全声明// str[0] = 'i'; // 编译错误:内容不可修改
char buffer[] = "Mutable";
str = buffer; // 允许:指针可重新赋值
三、char[] (字符数组)
定义:固定大小的字符数组
特点:
- 数组名是常量指针(不可重新赋值)
- 内容可修改
- 大小在声明时确定(静态或自动储存期)
内存分配:
- 通常分配在栈或全局数据区
char arr1[] = "Stack"; // 栈上分配(可修改)
char arr2[10] = "Array"; // 显式指定大小arr1[0] = 's'; // 允许:修改内容
// arr1 = "New"; // 错误:数组名不可重新赋值// 正确的内容修改方式
strcpy(arr1, "NewString"); // 需要确保目标空间足够
关键区别总结
特性 | char* | const char* | char[] |
指针是否可修改 | ✓ (可指向新地址 | ✓ (可指向新地址) | ✗ (固定地址) |
内容是否可修改 | 取决于指向的内存 | ✗ (永远不可修改) | ✓ (总是可修改) |
内存管理 | 需要手动管理 | 自动/静态 | 自动/静态 |
典型用途 | 动态字符串 | 字符串字面量 | 固定大小缓冲区 |
sizeof 返回值 | 指针大小(4/8字节) | 指针大小 | 数组实际大小 |
使用场景建议:
1、const char*
- 处理字符串字面量(如"Hello")
- 函数参数(表示输入字符串不会修改)
void printString(const char* str) {printf("%s", str);// 保证不会修改str的内容
}
2、char *:
- 动态内存分配的字符串
- 需要重新指向不同的字符串的场景
char* dynamicStr = malloc(20);
strcpy(dynamicStr, "Dynamic");
// ...使用...
free(dynamicStr);
3、char[]:
- 固定大小的缓冲区
- 需要局部修改的字符串
重要注意事项:
1、字符串字面量的危险:
char* str = "Literal"; // 指向只读内存
str[0] = 'l'; // 未定义行为(可能崩溃)
2、正确的初始化:
// 正确
const char* safe = "Hello";
char mutable[] = "World";// 危险(可能指向无效内存)
char* danger;
// strcpy(danger, "Risky"); // 未初始化指针!
3、数组与指针的sizeof差异:
char arr[100];
char* ptr = arr;printf("%zu\n", sizeof(arr)); // 100(数组总大小)
printf("%zu\n", sizeof(ptr)); // 4或8(指针大小)
4、函数参数等价性:
void func(char str[]); // 实际等价于...
void func(char* str); // ...这个声明
最佳实践:
优先使用
const char*
处理只读字符串对可修改的缓冲区使用
char[]
用
char*
配合malloc/free
处理动态字符串避免修改字符串字面量
使用
strncpy
代替strcpy
防止缓冲区溢出