数组和指针的区别

  1. 数组

    • 当你声明一个字符数组并用字符串字面量初始化时,例如 char array[] = "zhanghonghua";,数组会存储整个字符串,包括结尾的空字符 '\0'。数组的大小是根据初始化的字符串自动确定的。
  2. 指针

    • 当你声明一个字符指针并用字符串字面量初始化时,例如 char *ptr = "zhanghonghua";,指针存储的是字符串首字符的地址。指针本身不存储整个字符串,它只是指向存储字符串的内存位置。

这里有几个关键点需要注意:

  • 数组名在大多数表达式中会被解释为指向数组首元素的指针,但在函数参数传递时,数组名会退化为指向数组首元素的指针
  • 字符串字面量通常存储在程序的只读数据段中,因此通过指针引用它们是安全的,但尝试修改它们将导致编译时或运行时错误。
  • 字符数组可以存储可修改的字符串,因为它们通常在栈上通过动态内存分配在堆上分配,这些内存区域是可写的。(下面有详细解释)

下面是一个简单的例子,展示了数组和指针在存储字符串时的不同:

// 使用数组存储字符串
char array[] = "Hello, World!";
array[0] = 'h'; // 合法操作,修改第一个字符为小写的 'h'

// 使用指针存储字符串的首地址
char *ptr = "Hello, World!";
// ptr[0] = 'h'; // 非法操作,因为字符串字面量存储在只读内存区域

在这个例子中,修改数组 array 是合法的,但尝试修改通过指针 ptr 引用的字符串将导致编译错误,因为字符串字面量是不可修改的。

当然,让我进一步解释一下字符数组和它们是如何存储可修改字符串的。

1. **字符数组在栈上分配**:
   当你在函数内部定义一个字符数组时,这个数组是在栈上分配的。栈是用于存储局部变量的内存区域,它在函数调用时自动分配,在函数返回时自动释放。栈上分配的内存是可写的。

   void function() {
       char charArray[50] = "initial string"; // 在栈上分配的字符数组
       charArray[0] = 'A'; // 可以修改数组中的字符
   }

   在这个例子中,`charArray` 是一个局部变量,它在函数的栈帧上分配。你可以修改这个数组中的字符,因为栈内存是可写的。

2. **字符数组通过动态内存分配在堆上**:
   使用 `new` 或 `malloc` 可以在堆上分配内存。堆是用于动态内存分配的内存区域,它需要程序员手动管理(分配和释放)。堆上分配的内存也是可写的。

   char* dynamicCharArray = new char[50]; // 在堆上使用 new 分配的字符数组
   strcpy(dynamicCharArray, "another string"); // 可以复制字符串到这个数组
   dynamicCharArray[0] = 'A'; // 可以修改数组中的字符
   delete[] dynamicCharArray; // 释放堆上分配的内存

   在这个例子中,`dynamicCharArray` 是一个指针,指向在堆上分配的内存。你可以使用 `strcpy` 函数复制一个字符串到这个数组,也可以直接修改数组中的字符。使用完毕后,需要使用 `delete[]` 来释放分配的内存,防止内存泄漏。

3. **为什么数组可以存储可修改的字符串**:
   - 字符数组存储的是实际的字符数据,而不是字符数据的引用。因此,当你修改数组中的字符时,你是在修改实际存储在内存中的字符。
   - 由于栈和堆上的内存都是可写的,字符数组可以存储可修改的字符串。

4. **与指针的区别**:
   - 指针本身不存储字符串,它只存储一个地址,指向存储字符串的内存位置。如果指针指向的是字符串字面量,那么这个字符串是存储在只读内存区域的,不能被修改。
   - 当你使用字符数组时,你实际上是在操作一个存储实际字符数据的内存区域,因此可以修改它。

总结来说

字符数组可以存储可修改的字符串,因为它们直接在可写的内存区域(栈或堆)上存储字符数据。

而指针只是存储地址,是否可修改取决于它指向的内存区域的可写性。

这里进一步明确一下:

  1. 栈(Stack)

    • 栈是用于存储函数调用时的局部变量和返回地址等信息的内存区域。
    • 栈上分配的内存是可写的。当你在函数内部定义一个局部变量时,这个变量存储在栈上,你可以修改它的值。
  2. 堆(Heap)

    • 堆是用于动态内存分配的内存区域,程序员可以通过 malloccallocrealloc 或 C++ 中的 new 和 new[] 等操作在堆上分配内存。
    • 堆上分配的内存同样是可写的。程序员需要负责管理这部分内存,使用完毕后应通过 free 或 delete 释放。
  3. 字符串常量(String Literals)

    • 字符串常量(如 "Hello, World!")通常存储在只读数据段中,如程序的文本段(.text)或只读数据段(.rodata)。
    • 这些内存区域是不可写的,因为它们包含了程序运行时需要的固定数据,修改这些数据可能会导致程序行为不稳定或崩溃。

下面是一个简单的例子,展示如何在栈和堆上存储可修改的字符串,以及字符串常量的区别:

 
#include <cstring>

int main() {
    // 在栈上存储可修改的字符串
    char stackString[20] = "initial";
    stackString[0] = 'I'; // 修改第一个字符为大写的 'I'

    // 在堆上存储可修改的字符串
    char* heapString = new char[20];
    strcpy(heapString, "initial");
    heapString[0] = 'I'; // 修改第一个字符为大写的 'I'
    delete[] heapString; // 释放堆内存

    // 字符串常量是不可修改的
    const char* literal = "Hello, World!";
    // literal[0] = 'h'; // 这将导致编译错误,因为字符串常量是只读的

    return 0;
}

在这个例子中:

  • stackString 是在栈上分配的字符数组,可以修改。
  • heapString 是在堆上分配的字符数组,也可以修改。
  • literal 是一个指向字符串常量的指针,指向的字符串是不可修改的。尝试修改它将导致编译错误。

下面链接是整理的各个存储区的生存周期和是否支持数据的读写

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值