字符数组和字符指针的区别

在C/C++中,字符数组和字符指针都可用于处理字符串,但它们在本质、内存管理、可变性等方面有显著区别:

📌 核心区别总结

特性

字符数组 (e.g., char str[])

字符指针 (e.g., char *ptr)

本质

连续的内存块,存储字符本身

变量,存储一个内存地址

内存分配

编译时静态分配 (栈或全局区)

指针变量本身在栈上,指向的内存可动态分配

内存所有权

拥有自己的存储空间

指向的内存可能属于数组、字面量或堆

sizeof 结果

数组总字节数 (包括'\0')

指针变量的大小 (通常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

⚠️ 关键注意事项

  1. 字符串字面量的只读性
    用指针直接指向字面量时 (char *p = "literal"),绝不可修改内容。这是未定义行为,可能导致程序崩溃。
  2. 动态内存的指针需手动释放
    若指针指向堆内存 (malloc, calloc),必须在使用后 free(ptr),否则内存泄漏。
  3. 数组退化为指针
    当数组传递给函数时,它会退化为指向首元素的指针,函数内部无法通过 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

✅ 总结

场景

推荐使用

固定大小、需修改的字符串

字符数组 (char[])

动态长度或需重指向的字符串

字符指针 (char*)

只读字符串常量

const char* (避免误修改)

理解这些区别能避免内存错误,并写出更健壮的C/C++代码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值