C++中 const int* ,int * cnost,以及指针 (pointer) 的理解

本文深入浅出地解析了C++中指针的概念及其与函数的关系,通过实例介绍了指针的不同类型及其使用场景,包括变量指针、常量指针等,并详细解释了使用权与所有权的区别。

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

问题:指针如何理解?
这里有一个对指针概念的解释,比较长,我们从另外一个角度来理解指针:将指针理解为一个函数

问题:如何理解函数与参数之间的关系?
这里有一个对形式参数,实际参数的解释,我的理解:关键是函数对实际参数的“使用权”与“所有权”

使用权所有权
只能使用,不能修改随心所欲,可以用,可以改

问题:参数让不让你改?
要回答这个问题,就牵涉到另外一个东东:常量

下面,我们在逐步回答前面的问题。

  • 常量与变量
    int a = 0; // a 是一个变量
    a = 90;   // 可以对 a 的值进行修改

    const int b = 1; // b 是一个常量
    //b = 90; // 不能对 b 的值进行修改
  • 函数的“使用权”与“所有权”
    使用权
void fun1(int a, const int b){
    a = 100; // a 是一个变量,可以修改
    //b = 100;// 不能对 b 的值进行修改
}
状态int aconst int b
调用fun1之前901
在fun1内901
在fun1内修改变量之后1001
调用fun1之后901

所有权
那,什么时候才能使得函数对变量用于“所有权”呢?
答:使用“引用传递”,或者指针。
下面先给出使用“引用传递”的例子。

void fun2(int& a){
    a = 200;
}
状态int a
调用fun2之前90
在fun2内90
在fun2内修改变量之后200
调用fun2之后200

这里的int& a表示什么意思呢?为什么这时就可以改变a的值呢?
&表示“取地址”的意思,也就是说,函数fun2知道实际参数a的地址了,
“跑得了和尚,跑不了庙”,你家在哪我都知道了,也就用于“所有权”了,
可以对实际参数a的值进行修改。

指针的例子呢?
下面先说一下对指针的理解,然后再给出例子。

  • 将指针理解为一个函数
int* pa = &a; // 指针 pa 是一个函数,能够获取 a 的地址

那,既然将指针理解为一个函数,这个“函数”对a 是拥有“使用权”还是“所有权”呢?
答:额,这个很复杂,要看 指针的类型了

    int* pa1 = &a; // 指针 pa 是一个函数,能够获取 a 的地址
    *pa1 = 300;
    cout <<"after pa1, value of a is: "<<a <<endl;

    const int* pa2 = &a;
    //*pa2 = 400; // pa2 是指向 常量整型的指针,不能通过 pa2 修改a值
    cout <<"after pa2, value of a is: "<<a <<endl;
指针int* pa1const int* pa2
对a的权限所有权使用权

问题:为什么pa2 不能修改?pa2是否有另外的声明方式?
答:1)pa2 是一种“ 常量整型的指针”,可以理解为公安局,他们记录你们家的地址,只能把你叫过来问话,但是没有所有权。
2)也可以这样声明pa2:int const * pa2。为什么可以这样?,这就要参考最后一个说明了。

  • 利用指针将函数对变量用于“所有权”
void fun3(int* pa1){
    *pa1 = 500;
}
状态a的值
调用fun3之前300
在fun3内300
在fun3内修改变量之后500
调用fun3之后500

这里可以看到,函数fun3的输入参数是 int* 类型的,也就是一个指针,
通过前面的分析我们知道,int* 类型的指针已经拥有对a的“所有权”了,
利用函数fun3,当然对a也拥有“所有权”了。

再看一个例子,

void fun4(const int* pa2){
    //*pa2 = 600; // 不能修改 a 的值
}

函数 fun4,对a的作用变成了和 pa2 一样了。
这里,虽然a是变量,但是函数fun2通过pa2使用a也只能使用,即使在函数内部也不能修改,是最低级的“使用权”。

问题:前面提到的指针都是变量,那有没有:常量指针呢?
答:有,既是这个指针看成函数的话,它只能接收某个特定的参数。
声明方式如下:

    int * const pa3 = &a;
    *pa3 = 700;
    cout <<"after pa3, value of a is: "<<a <<endl;
    //pa3 = &b; pa3 不能在指向其他的量了
状态a的值
调用pa3之前500
调用pa3之后700

这里pa3对a,且只能对a 拥有“所有权”,可以改变a的值,但是不能再拥有其他量了。
也就是说,pa3 此时只能“一夫一妻”

是不是很混乱?那为什么混乱?是什么造成的?
答:1)是的。
2)函数对变量的“所有权”与“使用权”不好理解。
3)因为指针的引入。
那为什么还要引入指针呢?
答:为了更多的灵活性,比如起到 fun4 的作用。
代价就是,不好理解。
这里的坑还有很多,这里只是抛砖引玉。

C++内部的机制是如何的呢?

  • 放大招:读取规则,从右到左开始读
    借用 这里的两张图理解,从右到左。

    int * const ptr

 const int * ptr
int const * ptr

整体代码如下:

// csdn_code.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <iostream>
#include <opencv2/opencv.hpp>

using namespace cv;
using namespace std;

void fun1(int a, const int b);
void fun2(int& a);
void fun3(int* pa);
void fun4(const int* pa2);


void fun1(int a, const int b){
    a = 100; // a 是一个变量,可以修改
    //b = 100;// 不能对 b 的值进行修改
}

void fun2(int& a){
    a = 200;
}

void fun3(int* pa1){
    *pa1 = 500;
}

void fun4(const int* pa2){
    //*pa2 = 600; // 不能修改 a 的值
}

int main()
{
    int a = 0; // a 是一个变量
    a = 90;   // 可以对 a 的值进行修改

    const int b = 1; // b 是一个常量
    //b = 90; // 不能对 b 的值进行修改

    cout <<"value of a is: "<<a <<endl;

    fun1(a,b);
    cout <<"after fun1, value of a is: "<<a <<endl;

    fun2(a);
    cout <<"after fun2, value of a is: "<<a <<endl;

    int* pa1 = &a; // 指针 pa 是一个函数,能够获取 a 的地址
    *pa1 = 300;
    cout <<"after pa1, value of a is: "<<a <<endl;

    const int* pa2 = &a;
    //*pa2 = 400; // pa2 是指向 常量整型的指针,不能通过 pa2 修改a值
    cout <<"after pa2, value of a is: "<<a <<endl;

    fun3(pa1);
    cout <<"after fun3, value of a is: "<<a <<endl;

    fun4(pa2);
    cout <<"after fun4, value of a is: "<<a <<endl;


    int * const pa3 = &a;
    *pa3 = 700;
    cout <<"after pa3, value of a is: "<<a <<endl;
    //pa3 = &b; pa3 不能在指向其他的量了

    // 将指针看做是一个函数,是否可以改变指针所指向的值
    waitKey();
    system("pause");
    return 0;
}
<think>我们正在讨论C语言和C++中关于指针类型`int*`和`const int*`之间的隐式转换规则。 问题:C语言和C++中,`int*`和`const int*`之间是否支持隐式类型转换? 根据C和C++的类型系统,我们需要分别考虑两个方向: 1. 从`int*`到`const int*`的转换(添加const) 2. 从`const int*`到`int*`的转换(去除const) 我们将分别讨论C语言和C++中的情况。 一、C语言中的转换规则: 在C语言中,指针类型之间的隐式转换规则如下: - 允许将`int*`隐式转换为`const int*`。因为这是添加const限定符,通常被认为是安全的(不会破坏原数据的const性)。 - 不允许将`const int*`隐式转换为`int*`。因为去除const限定符可能会导致通过新指针修改原本被声明为const的数据,这是不安全的。 但是,C语言允许使用显式类型转换(强制类型转换)来实现`const int*`到`int*`的转换,但这样做是危险的,因为如果原数据确实是常量,那么修改它是未定义行为。 示例代码(C语言): ```c int main() { int a = 10; const int b = 20; // 合法:隐式转换,int*const int* const int* p1 = &a; // 非法:隐式转换,const int*int*,编译器会报错 // int* p2 = &b; // 错误 // 显式转换:强制类型转换 int* p3 = (int*)&b; // 语法上允许,但通过p3修改b是未定义行为(因为b是const) // *p3 = 30; // 可能导致未定义行为 return 0; } ``` 二、C++中的转换规则: 在C++中,规则与C语言基本一致,但C++的类型检查更为严格。 - 同样允许将`int*`隐式转换为`const int*`(即添加底层const)。 - 不允许将`const int*`隐式转换为`int*`(即去除底层const)。 此外,C++提供了四种命名的强制类型转换操作符(static_cast, const_cast, reinterpret_cast, dynamic_cast)来更清晰地表达转换意图。其中,const_cast专门用于添加或移除const(和volatile)限定符。 示例代码(C++): ```cpp int main() { int a = 10; const int b = 20; // 合法:隐式转换,int*const int* const int* p1 = &a; // 非法:隐式转换,const int*int*,编译器报错 // int* p2 = &b; // 错误 // 使用const_cast进行显式转换(去除constint* p3 = const_cast<int*>(&a); // 安全,因为a不是const *p3 = 30; // 允许,因为a原本就是非const // 但是,对真正的常量进行const_cast去除const并修改是未定义行为 int* p4 = const_cast<int*>(&b); // 语法允许,但... // *p4 = 40; // 未定义行为,因为b是常量 return 0; } ``` 总结: 1. 在C和C++中,从`int*`到`const int*`的隐式转换是允许的。 2. 在C和C++中,从`const int*`到`int*`的隐式转换是不允许的,必须使用显式转换(在C中用强制转换,在C++中推荐用const_cast,但要注意安全)。 注意:在C++中,如果尝试将指向常量的指针隐式转换为非const指针,编译器会直接报错。而在C语言中,同样会报错,但C语言允许使用强制转换绕过,而C++中则必须使用const_cast(或C风格的强制转换)来显式转换。 引用说明: 根据引用[4]和[5]中提到的隐式类型转换规则,指针的隐式转换包括派生类指针到基类指针的转换、空指针的转换等,但并没有提到可以隐式去除const限定符。而显式类型转换则用于需要强制转换的情况。因此,我们的结论符合这些引用内容。 最后,需要强调的是,强制去除const限定符并修改原本定义为常量的对象是未定义行为,应该避免。 如果用户需要进一步了解,可以提出相关问题。 §§相关问题§§ 1. C++中四种强制类型转换(static_cast, const_cast, dynamic_cast, reinterpret_cast)的区别和应用场景? 2. 在C++中,如何安全地使用const_cast? 3. C语言和C++在类型转换方面还有哪些主要区别?</think>### C语言和C++中`int*`与`const int*`的隐式转换规则 在C语言和C++中,`int*`(指向非常量整数的指针)与`const int*`(指向常量整数的指针)之间的隐式转换规则如下: #### 1. **从`int*`到`const int*`的隐式转换(安全)** - **允许隐式转换**:在C和C++中,将非常量指针隐式转换为常量指针是安全的,编译器会自动完成。 - **原因**: 这种转换是"添加const限定符"(`const`-adding),不会破坏数据的完整性。通过`const int*`访问数据时,只能读取不能修改,而原`int*`仍可修改数据,因此是类型安全的。 - **示例**: ```c int a = 10; int* p1 = &a; // 非常量指针 const int* p2 = p1; // 隐式转换:int*const int*(合法) ``` - `p2`可以读取`a`的值,但无法通过`p2`修改`a`。 #### 2. **从`const int*`到`int*`的隐式转换(不安全)** - **禁止隐式转换**:在C和C++中,将常量指针隐式转换为非常量指针**不允许**的,会导致编译错误。 - **原因**: 这种转换是"去除const限定符"(`const`-removing),可能破坏数据的常量性。如果允许隐式转换,程序可能通过新指针修改本应只读的数据,导致未定义行为。 - **示例**: ```c const int b = 20; const int* p3 = &b; int* p4 = p3; // 错误:const int*int*(隐式转换非法) ``` - 编译器会报错:`invalid conversion from 'const int*' to 'int*'`。 #### 3. **显式强制转换(危险但允许)** - **显式转换**:在C和C++中,可以通过强制类型转换实现`const int*`到`int*`的转换,但这是**不安全的**。 - **风险**: 如果原数据是常量(如`const int b`),通过强制转换后的指针修改数据会导致**未定义行为**(程序崩溃或数据损坏)。 - **示例**: ```c const int b = 20; const int* p3 = &b; int* p4 = (int*)p3; // C风格强制转换(语法允许,但危险) // *p4 = 30; // 未定义行为!尝试修改常量数据 ``` - 在C++中,更推荐使用`const_cast`显式表达意图: ```cpp int* p5 = const_cast<int*>(p3); // C++风格(仍危险) ``` --- ### 关键总结 | 转换方向 | C语言 | C++ | 安全性 | 说明 | |------------------|-------|-------|--------------|--------------------------| | `int*` → `const int*` | ✅ 隐式 | ✅ 隐式 | 安全 | 添加const限定符 | | `const int*` → `int*` | ❌ 隐式 | ❌ 隐式 | 不安全 | 需显式转换,但可能导致UB | > **未定义行为(UB)**:当通过强制转换后的`int*`修改**原始常量数据**时,行为未定义。例如: > ```cpp > const int x = 100; > int* p = (int*)&x; // 显式强制转换 > *p = 200; // UB!x是常量,实际可能触发段错误 > ``` --- ### 设计原理 - **类型安全优先**:禁止隐式去除`const`是为了防止意外修改只读数据,符合C/C++的"常量正确性"原则[^4][^5]。 - **显式转换警示**:强制要求显式转换(如`const_cast`)提醒开发者注意风险,需自行确保数据可修改[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值