“指向字符串常量的字符指针不可更改字符串的内容” 敲黑板,划重点了!!!

本文介绍了C/C++程序中不同类型的内存分配区域,包括栈区、堆区、全局区、文字常量区和程序代码区,并通过具体示例展示了各种类型变量的存储位置及使用限制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、预备知识—程序的内存分配
一个由C/C++编译的程序占用的内存分为以下几个部分
1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其
操作方式类似于数据结构中的栈。
2、堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回
收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。
3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的
全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另
一块区域。 - 程序结束后由系统释放。
4、文字常量区 —常量字符串就是放在这里的。 程序结束后由系统释放
5、程序代码区—存放函数体的二进制代码。

二 代码解释区域

#include <iostream>
using namespace std;
 
int main() 
{
    string str = "hello1";    // 字符串变量
 
    // 字符数组,先在常量区存储"hello2",然后在栈区申请内存空间,
    // 将"hello2"复制过去,str指向栈区的"hello2"
    char arr[] = "hello2";
    char *p = "hello3";    // 注意p指向常量字符串,在常量区存储"hello3",p直接指向常量区的"hello3"
 
    str[0] = 'X';    // 可成功修改
    arr[0] = 'X';    // 可成功修改
    p[0] = 'X';     // 编译器不能发现该错误,但运行时错误,p指向常量区内容,该区域内容不能被修改
 
    str = "hello1 world";    // 可成功修改
    arr = "hello2 world";    // 错误,arr是数组名,相当于指针常量,指向不能改变
    p = "hello3 world";    // 可成功修改,p指向另一个字符串常量
 
    system("pause");
    return 0;
}
 
三、要点说明:
1、在一个双引号""内的字符序列或者转义字符序列称为字符串常量。这些字符串常量存储在常量存储区。其内容不可被更改。(常量存储区:这是一块比较特殊的存储区,他们里面存放的是常量,不允许修改。与静态存储区有区别)

2、字符常量可以赋值给字符变量,如"char b='a';",但不能把一个字符串常量赋给一个字符变量,同时也不能对字符串常量赋值。
 

<think>我们正在讨论C/C++中的指向匿名字符串常量指针。匿名字符串常量指的是在代码中直接出现的字符串字面,例如"HelloWorld",它们没有显式的变量名,但编译器会为它们在内存中分配存储空间(通常是只读区域)。指向这样的字符串常量指针具有一些特殊性质。根据引用[1]和引用[4]的内容,我们知道:-字符串常量可以用来初始化指向字符指针(如`char*s="CLanguage";`),此时指针`s`指向的是字符串常量的首地址。-但是,字符串常量本身是只读的,所以试图通过指针修改字符串常量内容会导致未定义行为(通常是程序崩溃)。现在,我们需要分析指向匿名字符串常量指针的行为。以下是一些关键点:1.**存储位置**:匿名字符串常量通常存储在程序的只读数据段(例如.rodata段)中,因此试图修改它们会导致段错误(segmentationfault)。2.**指针类型**:为了强调字符串常量不可修改性,C++11标准要求使用`constchar*`来指向字符串常量。在C中,虽然允许使用`char*`,但修改字符串常量仍然是未定义行为。3.**生命周期**:匿名字符串常量具有静态存储期,它们在程序的整个生命周期内都存在。因此,指向它们的指针在程序运行期间始终有效。4.**指针运算**:可以通过指针运算来遍历字符串,直到遇到空字符`'\0'`为止(引用[2])。5.**作为函数参数**:指向字符串常量指针可以作为函数的形参,并且可以用字符串数组名(即字符数组的首地址)来赋(引用[1]中的示例)。下面我们通过一些代码示例来说明:###示例1:基本用法```c++#include<iostream>intmain(){constchar*p="Hello";//p指向匿名字符串常量//通过指针访问std::cout<<p<<std::endl;//输出:Hellostd::cout<<*p<<std::endl;//输出:H//指针运算p++;//现在指向'e'std::cout<<p<<std::endl;//输出:ello//注意:不能修改//*p='a';//错误!尝试修改只读内存,会导致未定义行为return0;}```###示例2:与字符数组对比```c++#include<iostream>intmain(){//字符数组(在栈上分配,可修改)chararr[]="World";arr[0]='w';//合法//指向字符串常量指针constchar*p="Hello";//p[0]='h';//非法,编译错误或运行时错误return0;}```###示例3:作为函数参数如引用[1]中的示例,我们可以将指向字符串常量指针作为函数的形参,并用字符串数组名(即字符数组的首地址)来赋。因为数组名在表达式中会退化为指向其首元素的指针。```c++#include<iostream>#include<string>usingnamespacestd;voiddisplay(conststring*str1){cout<<"字符串指针指向字符串数组"<<endl;cout<<*(str1+2);}intmain(){stringstr1[3]={"hjh","ihuy","jhb"};conststring*s=str1;//用字符串数组名给指向字符串常量指针display(s);return0;}```注意:在这个例子中,虽然函数`display`的形参是指向`conststring`的指针,但实参是一个字符串数组(元素类型为`string`)的首地址。这里`conststring*`表示指针指向的是常量字符串(即不能通过该指针修改`string`对象),而数组中的每个元素都是可修改的`string`对象,但通过这个指针我们承诺不会修改它们。###注意事项-**类型安全**:在C++中,字符串字面的类型是`constchar[N]`(N是包括空字符的长度),因此将其赋给`constchar*`是安全的。赋给`char*`在C++11之前是允许的(但已废弃),在C++11中需要强制类型转换(但强烈不建议,因为试图修改会导致未定义行为)。-**多字符串字面的合并**:编译器可能会将相同的字符串字面合并存储在同一地址,以节省空间。-**跨平台差异**:不同平台和编译器可能有不同的实现,但行为在标准中有明确规定。###相关问题1.在C++中,为什么允许将字符串字面给`constchar*`,而不允许赋给`char*`(在C++11标准之后)?2.如何安全地使用指向字符串常量指针作为函数参数?3.在C语言中,为什么允许将字符串字面给`char*`,而修改它却是未定义行为?4.如何区分字符串常量字符数组在内存中的位置?5.在C++中,使用`std::string`与C风格字符串字符指针)相比有什么优势?[^1]:引用[1]中说明了指向字符串常量指针可以指向字符串数组,并作为函数参数传递。[^2]:引用[2]中说明了字符串长度的计算方式,以及C风格字符串以空字符结尾的特性。[^4]:引用[4]中介绍了字符指针指向字符串的几种方法,包括用字符串常量初始化指针。</think>## C/C++ 指向匿名字符串常量指针行为分析 在C/C++中,匿名字符串常量(也称为字符串字面量)是直接出现在代码中的字符串(如 `"Hello"`),它们没有显式变量名。理解指向这些常量指针行为对内存安全和代码正确性至关重要。 ### 核心特性与行为分析 1. **内存存储位置** - 匿名字符串常量存储在程序的**只读数据段(.rodata)** - 生命周期贯穿整个程序运行期 ```c const char* p = "Anonymous"; // "Anonymous"存储在只读区 ``` 2. **指针的只读性** - 通过指针修改内容会导致未定义行为(通常是段错误): ```c char* p = "Constant"; // 危险!C++11起已禁止 p[0] = 'K'; // 运行时崩溃:尝试修改只读内存 ``` - 正确做法:始终使用 `const` 修饰符[^4] ```cpp const char* safe_ptr = "Immutable"; // 安全声明 ``` 3. **指针与类型兼容** - 字符串数组名可赋指向常量指针(数组退化为指针): ```cpp char arr[] = "Array"; const char* ptr = arr; // 合法:承诺不修改内容[^1] ``` - 但反向操作(常量指针赋给非常量指针)危险: ```cpp const char* c_ptr = "Constant"; char* danger_ptr = const_cast<char*>(c_ptr); // 危险! ``` 4. **内存复用优化** 编译器可能合并相同字符串常量: ```cpp const char* p1 = "ABC"; const char* p2 = "ABC"; // p1 和 p2 可能指向同一内存地址 ``` ### 关键问题解决方案 1. **安全传递到函数** 使用 `const` 修饰形参,兼容多种输入: ```cpp void print_str(const char* str) { // 安全接口设计[^1] cout << str; } int main() { print_str("Literal"); // 匿名常量 char arr[] = "Array"; print_str(arr); // 字符数组 } ``` 2. **修改内容的需求** 需要修改时应使用字符数组副本: ```cpp const char* orig = "Hello"; char buffer[20]; strcpy(buffer, orig); // 复制到可修改内存[^5] buffer[0] = 'J'; // 安全修改 ``` 3. **类型区分技巧** - 匿名常量:`typeid("literal").name()` 返回 `char const*` - 字符数组:`typeid(arr).name()` 返回 `char [N]` ### 内存布局示例 ``` 程序内存布局: ┌─────────────┐ │ 代码区 │ ├─────────────┤ │ 只读数据段 │ ← "Anonymous"存储位置 ├─────────────┤ │ 全局变量区 │ ├─────────────┤ │ 堆 │ ├─────────────┤ │ 栈 │ ← char arr[] 存储位置 └─────────────┘ ``` ### 最佳实践 1. 始终用 `const char*` 指向字符串常量 2. 需要修改时使用字符数组或 `std::string`[^3] 3. 避免 C 风格字符串,优先使用 `std::string_view`(C++17) 4. 慎用 `const_cast`,确保目标内存真正可写 > ⚠️ **重要提示**:在 C++11 及以后,`char* p = "literal"` 已被废弃,直接使用会导致编译错误。必须使用 `const char*`。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值