在C/C++中,字符数组和字符指针都可用于处理字符串,但它们在本质、内存管理、可变性等方面有显著区别:
📌 核心区别总结
|
特性 |
字符数组 (e.g., |
字符指针 (e.g., |
|
本质 |
连续的内存块,存储字符本身 |
变量,存储一个内存地址 |
|
内存分配 |
编译时静态分配 (栈或全局区) |
指针变量本身在栈上,指向的内存可动态分配 |
|
内存所有权 |
拥有自己的存储空间 |
指向的内存可能属于数组、字面量或堆 |
|
sizeof 结果 |
数组总字节数 (包括 |
指针变量的大小 (通常4或8字节) |
|
内容可变性 |
✅ 元素可修改 |
⚠️ 取决于指向的内存 (字面量不可改) |
|
地址可变性 |
❌ 数组名是常量地址 (不可重新赋值) |
✅ 指针可指向不同地址 |
|
初始化 |
可用字符串字面量初始化 (内容复制) |
可直接指向字符串字面量 (共享内存) |
🔍 详细解析与示例
1. 内存分配与本质
字符数组: 声明时分配固定大小的连续内存。
char str[10] = "Hello"; // 栈上分配10字节,内容为'H','e','l','l','o','\0',?,?,?
字符指针: 仅分配一个指针变量 (大小固定),指向的内存需另行分配。
char *ptr = "World"; // ptr在栈上,指向只读区的字符串字面量
char *ptr2 = malloc(10); // 指向堆内存
2. 初始化行为
数组初始化: 字符串字面量内容被复制到数组空间。
char arr[] = "ABC"; // arr拥有独立副本,可修改 arr[0]='X'
指针初始化: 直接指向字面量的内存 (通常只读)。
char *ptr = "ABC"; // ptr指向只读内存,修改 ptr[0] 导致未定义行为(崩溃)!
3. sizeof 运算符
数组: 返回整个数组的字节数。
char arr[20];
printf("%zu", sizeof(arr)); // 输出 20
指针: 返回指针本身的大小 (与系统相关)
char *ptr;
printf("%zu", sizeof(ptr)); // 输出 4 (32位) 或 8 (64位)
4. 可变性
数组内容可修改:
char arr[] = "Hello";
arr[0] = 'X'; // 合法: "Xello"
指针指向的内容:
char *ptr1 = "Hello"; // ❌ 不可修改 (字面量在只读段)
char ptr2[] = "World"; // ✅ 可修改: ptr2[0]='Y'
char *ptr3 = malloc(10);
strcpy(ptr3, "Hi"); // ✅ 堆内存可修改
5. 赋值与地址可变性
数组名是常量指针 (不可重新赋值):
char arr[10];
arr = "New"; // ❌ 编译错误!数组名不可作为左值
指针可重新指向:
char *ptr = "Old";
ptr = "New"; // ✅ 合法 (注意新指向的可能是只读内存)
ptr = arr; // ✅ 指向数组arr
⚠️ 关键注意事项
- 字符串字面量的只读性
用指针直接指向字面量时 (char *p = "literal"),绝不可修改内容。这是未定义行为,可能导致程序崩溃。 - 动态内存的指针需手动释放
若指针指向堆内存 (malloc,calloc),必须在使用后free(ptr),否则内存泄漏。 - 数组退化为指针
当数组传递给函数时,它会退化为指向首元素的指针,函数内部无法通过sizeof获知数组大小。
💡 何时使用哪种?
- 字符数组:
-
- 需要存储固定大小的字符串。
- 需要在栈上快速分配且可修改内容。
- 函数内部的小型临时字符串。
- 字符指针:
-
- 需动态分配字符串长度(如运行时确定)。
- 需指向不同字符串(重新赋值)。
- 作为函数参数传递字符串(避免复制大数组)。
- 指向只读字符串(如常量提示信息)。
🌰 代码示例对比
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
// 字符数组 (栈内存,可修改)
char arr[] = "Array";
arr[0] = 'a'; // ✅ 合法
printf("Array: %s\n", arr); // "array"
// 字符指针指向字面量 (只读!)
char *ptr1 = "Literal";
// ptr1[0] = 'l'; ❌ 运行时错误!
// 字符指针指向堆内存 (可修改)
char *ptr2 = malloc(10);
strcpy(ptr2, "Heap");
ptr2[0] = 'h'; // ✅ 合法
printf("Heap: %s\n", ptr2); // "heap"
free(ptr2); // 必须释放!
// 指针可重新指向,数组名不可
ptr1 = arr; // ✅ ptr1 现在指向arr
// arr = ptr2; ❌ 编译错误!
return 0;
}
Array: array
Heap: heap
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
// 1, 基本介绍
char str[10] = "hello"; // 栈上分配10字节,内容为'H','e','l','l','o','\0',?,?,?
char *ptr = "world"; // ptr在栈上,指向只读区的字符串字面量,不可修改 (字面量在只读段)
char *ptr2 = malloc(10); // 指向堆内存
// 2,内容是否可修改
char arr[] = "abc"; // arr拥有独立副本,可修改 arr[0]='X'
char *ptr3 = "abc"; // ptr3在栈上,指向只读内存,修改 ptr[0] 导致未定义行为(崩溃)!
char *ptr4 = malloc(10); // 指向堆内存
printf("arr %s\n", arr);
printf("ptr3 %s\n", ptr3);
// ptr3 不可修改
// ptr3[0] = 'd'; // Segmentation fault (core dumped)
// printf("ptr3 %s\n", ptr3);
// strcpy(ptr3, "hi"); // Segmentation fault (core dumped)
// printf("ptr3 %s\n", ptr3);
// arr可修改
arr[0] = 'd';
printf("arr %s\n", arr);
// 堆内存可修改
strcpy(ptr4, "hi");
printf("ptr4 %s\n", ptr4);
ptr4[0]= 'i';
printf("ptr4 %s\n", ptr4);
// 3,赋值与地址可变性
// arr = "new"; // 编译错误,数组名不可作为左值。
// arr = ptr3; // 编译错误,数组名不可作为左值,也不能重新指向指针
ptr3 = "new"; // ✅ 合法(注意新指向的可能是只读内存)
printf("ptr3 %s\n", ptr3);
ptr3 = arr; // ✅ 指向数组arr
printf("ptr3 %s\n", ptr3);
return 0;
}
arr abc
ptr3 abc
arr dbc
ptr4 hi
ptr4 ii
ptr3 new
ptr3 dbc
✅ 总结
|
场景 |
推荐使用 |
|
固定大小、需修改的字符串 |
字符数组 ( |
|
动态长度或需重指向的字符串 |
字符指针 ( |
|
只读字符串常量 |
|
理解这些区别能避免内存错误,并写出更健壮的C/C++代码

1748

被折叠的 条评论
为什么被折叠?



