一起来练习C++的指针

目录
  • 练习一:一级指针指向练习题
  • 练习二:二级指针指向练习题
    • A选项;
    • B选项;
    • C选项;
    • D选项;
    • E选项;
  • 总结

在C++中,const作用于指针时,可以看做是对指针权限的限制。这里我们先把指针的权限归为两种,分别为指向权限修改权限。(ps:以上是为了理解方便,实际并没有如此规定)

	int a = 10, b = 20;
	int* p = &a;
	p = &b;		// 改变指向的权限
	*p = 30;	// 修改内存的权限
	const int* cp = &a;	// 限制修改权限
	//*cp = 100;		// error:表达式必须是可修改的左值	修改
	cp = &b;			// ok.								指向
	int* const pa = &a;	// 限制指向权限
	*pa = 100;			// ok.								修改
	//pa = &b;			// error:表达式必须是可修改的左值  指向

指针的赋值一般遵守权限缩小式的赋值。例如,我有一本书,我有使用权限(我可以看,可以做笔记),借给你后你只有阅读权限(只能看,不能做笔记)。当然,如果我们关系好,我可以赋予你使用权(你拥有读写的权利)。同样的,指针的赋值也是如此。

	int a = 10;
	int* p = &a;	// int*  <==  int *
	int* q = p;				// int*  <== int*
	const int* cp = p;		// const int*  <==  int*	权限缩小,
	int* const pa = p;		// int*	 <== int*			注意:int* const pa;是“int*”类型
	//int* p1 = cp;	// error:int*  <== const int *     权限放大,
	int* p2 = pa;	// ok.    int*  <== int*

我们可以得出一级指针赋值的公式

int *       <==  int *		// int* 包含 int* 和 int* const类型
int const * <==  int *		// int const * <=等同=> const int *
// 以上公式反过来赋值就是错误的..

练习一:一级指针指向练习题

题目一:下列表达式语句赋值错误的是?

	int a = 10;
	const int* p = &a;
	int* q = p;
	int a = 10;
	int* const p = &a;
	int* q = p;
	int a = 10;
	int* const p = &a;
	int* const q = p;
	int a = 10;
	int* const p = &a;
	const int* q = p;

答案:(鼠标选中查看)

错误:A,正确:B、C、D

解析:

	int a = 10;
	const int* p1 = &a;
	int* q1 = p1;				// error:无法从const int * 转为 int *
	/* 分析:
		int* <= cosnt int*
	*/
	int* const p2 = &a;
	int* q2 = p2;
	/* 分析:
		int* <= int*
	*/
	int* const p3 = &a;
	int* const q3 = p3;
	/* 分析:
		int* <= int*
	*/
	int* const p4 = &a;
	const int* q4 = p4;
	/* 分析:
		cosnt int* <= int*
	*/

练习二:二级指针指向练习题

题目二:下列表达式语句错误的有。

// 选项A
int a = 10;
int* p1 = &a;
const int** q1 = &p1;
// 选项B
int a = 10;
int* p2 = &a;
int* const* q2 = &p2;
// 选项C
int a = 10;
int* p3 = &a;
int** const q3 = &p3;
// 选项D
int a = 10;
int* const p4 = &a;
int** q4 = &p4;
// 选项E
int a = 10;
const int* p5 = &a;
int* const* q5 = &p5;

答案:(鼠标选中查看)

错误:A、D、E,正确:B、C

A选项;

错误; 注:如果const修饰的是二级指针,我们需要对二级指针的逐层解引用进行分析。

	int* p1 = &a;
	const int** q1 = &p1;	//error  无法从“int * *”转换为“const int** ”

int* p1 = &a; p1的类型为int* 取地址为 int **const int** q1 = &p1; q1的类型为 const int **则指针赋值过程为 const int ** <= int* *

分析:

  • const作用于(**q1),修饰二级指针。表示不可通过q1对 a 的值进行修改。
  • *q1 解引用一次后,为一级指针,即 p1 。但是 p1 存在对 a 修改的风险,因此无法直接赋值。

修改方案:

  • 方案一:直接限定一级指针p1。保证p1不会修改a的值,即const int * p1= &a; const int** q1 = &p1;
  • 方案二:间接限定q1,使其指向时缩小权限,对解引用后的(*q1)修改的权限做出限制,如:const int * const * q1;
	// 方案一
	const int* p12 = &a;
	const int** q12 = &p12;

	// 方案二
	int* p11 = &a;
	const int* const* q11 = &p1;

B选项;

正确; 注:如果const修饰的是一级指针,我们可以抛开二级指针的表象,但看一级指针的赋值操作是否正确。如本例。

	int* p2 = &a;
	int* const* q2 = &p2;
	/* 分析:
		int* const*  <==  int* *
		const修饰 *q2,即cosnt修饰一级指针
		cosnt*  <== *				// 去掉前面的 int* <= int*
		int const *  <== int *		// 添加一个任意类型,如int
			如所示,这是一个权限缩小的一级指针赋值,
	*/

C选项;

正确; 注:如果两边类型相同,则无需进行判断。如本例。

	int* p3 = &a;
	int** const q3 = &p3;
	cout << typeid(q3).name() << endl;	//输出 q3 类型   int * *
	/* 分析:
		int**const  <== int* *  即  int**  <== int* *
		两边类型相同,无需进行特殊判断,
	*/

ps:如果const修饰的参数右边无“*”号,则该cosnt不作用于类型。如:

	int n = 10;
	// 使用typeid(valtypr).name() 输出变量类型
	int const* p1 = &n;		// int const *
	int* const p2 = &n;		// int *			// 忽略const
	int* p = &n;
	int** q = &p;
	//int const** q1 = &p;
	int const* const* q1 = &p;	   // int const* const*
	int* const* q2 = &p;		   // int* const*
	int** const q3 = &p;		   // int**		// 忽略cosnt

D选项;

错误; 同B选项相同,对于const修饰的一级指针进行判即可。

	int* const p4 = &a;
	int** q4 = &p4;			//error  "int *const *"类型的值不能用于初始化"int **类型的实体

int* const p4 = &a; 类型为 int*,因为const的存在,取地址后类型为 int * const *int** q4 = &p4; 类型为 int**则指针赋值过程为 int** <== int* const*

分析:

  • const作用于(p4),修饰一级指针。则我们忽略没有const修饰的部分。即* <== const* ,//忽略 int 部分,该部分赋值时权限没有发生变化。int* <== int const * ,给指针确定一个类型,如“int” 类型
  • 如,我们可以看到,该表达式语句想从int const* 类型处,获得一个 int* 类型的赋值,也就是说这是权限放大式的赋值。错误原因:该赋值会使得 int* 类型指针对常量(int const* 所指向的值)产生修改的风险。

修改方案:

  • int* <== int const *型赋值改成 int const* <== int const *类型赋值即,int*const* q4 = &p4;

E选项;

错误; 注:如果赋值两边都有const时,各论各的分析,如下。

	const int* p5 = &a;
	int* const* q5 = &p5;	//error  无法从"const int **"转换为"int *const *"

分析:

  • 省略分析过程等赋值类型为 int* const* <== const int* *
  • 分情况分析:
    • 提取指针左边部分int* <== const int* ,错误
    • 提取指针右边部分cosnt * <== * 即 int const * <== int *,正确
  • 综上,错误 。

修改方案:

1.修改指针左边类型:int* <== const int* ⇒ int* <= int*

2.修改指针左边类型:int* <== const int* ⇒ const int* <= const int*

	//1 int* <= int*
	int* p51 = &a;					// const int* p5  ⇒   int* p51
	int* const* q51 = &p51;
	//2 const int*  <= const int*
	const int* p52 = &a;
	const int* const* q52 = &p52;	// int* const* q5 ⇒   const int* const* q52

方法总结:

对于二级指针的赋值操作判断,看const位置、主要有以下两种情况:

  • 如果 const修饰的是二级指针 如:

    • int const ** ,如选项A。我们需要考虑其解引用情况。cosnt修饰二级指针所指向的值为常量,但是由于一次解引用后的指针会存在修改常量的风险,因此我们需要限制该指针与常量之间进行过度的一级指针
    • 针对此类二级指针,我们只需记住合法的赋值为等式两边需同时有const ,const int* cosnt* <== int**或 左边等式有两个cosnt ,const int* cosnt* <== int** 。
  • 如果 const修饰的是一级指针其他 如:

1.const修饰的是一级指针int * const *,如选项B。单独剥离出含cosnt类型的一级指针类型进行分析

2.即修饰一级指针又修饰二级指针 如, int cosnt * cosnt *

3.无const修饰 如, int** 或 int ** cosnt,如选项C、选项D

  • 针对此类二级指针,通过一级指针的比较进行比较即可。

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注我们的更多内容!

(0)

相关推荐

  • c++动态内存管理与智能指针的相关知识点

    目录 引言 一.介绍 二.shared_ptr类 make_shared函数 shared_ptr的拷贝和引用 shared_ptr自动销毁所管理的对象… 使用动态生存期的资源的类 应用举例:Blob类 定义Blob类 StrBlob的拷贝.赋值和销毁 三.直接管理内存 使用new分配内存 使用new动态分配和初始化对象 动态分配const对象 内存耗尽 使用delete释放内存 基本介绍 举例 四.shared_ptr和new结合使用 new直接初始化share_ptr 初始化时传入可调用对象

  • c++函数名指针和函数指针

    目录 前言 例 1 例 2 例 3 前言 我们先来看一下函数指针式如何定义的,假如我们有一个函数int fun(int){…};那么他对应的函数指针写法就应该是int (*p)(int);然后再对他进行赋值,即p=fun;之后你就可以在接下来的地方按p作为函数名来调用它用起来完全和fun一样.(注意这里的p指针并不是只能接受fun这个函数名,任何返回值是int,参数只有一个int的函数都可以把函数名赋给p) 首先说一下C/C++在创建一个变量的时候比如int a;相应的在内存就会分配一个4个字节

  • 一起来了解一下C++中的指针

    目录 1 指针的基本概念 2 指针变量的定义和使用 3 指针所占内存空间 4 空指针和野指针 5 const修饰指针 6 指针和数组 7 指针和函数 8 指针.数组.函数 总结 1 指针的基本概念 作用:可以通过指针间接访问内存. 内存编号是从0开始记录的,一般用十六进制数字表示.可以利用指针变量保存地址. 2 指针变量的定义和使用 指针变量定义语法: 数据类型* 变量名; &可以取地址;*可以取地址存放的值 示例: #include<iostream> using namespace

  • C++的指针,引用和STL详解

    目录 指针.引用 指针 引用 STL STL中六大组件 常用容器用法介绍 vec.front(),vec.back()    返回vector的首尾元素 重载运算符 总结 对象的定义:对象是指一块能存储数据并具有某种类型的内存空间 一个对象a,它有值和地址:运行程序时,计算机会为该对象分配存储空间,来存储该对象的值,通过该对象的地址,来访问存储空间中的值. 指针.引用 指针 类型名 * 指针变量名: 每个变量都被存放在从某个内存地址(以字节为单位)开始的若干个字节中:"指针",也称作&

  • C++引用和指针的区别你知道吗

    目录 引用 1.引用概念 2.格式 3.引用特性 4.常引用 1.const引用 5.使用场景 1.引用作为参数 2. 引用作为做返回值 6.引用和指针的区别 7.引用和指针的不同点: 总结 引用 1.引用概念 引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间. 比如:李逵,在家称为"铁牛",江湖上人称"黑旋风 2.格式 类型& 引用变量名(对象名) = 引用实体: 例: void TestRe

  • C++ 中引用与指针的区别实例详解

    C++ 中引用与指针的区别实例详解 引用是从C++才引入的,在C中不存在.为了搞清楚引用的概念,得先搞明白变量的定义及引用与变量的区别,变量的要素一共有两个:名称与空间. 引用不是变量,它仅仅是变量的别名,没有自己独立的空间,它只符合变量的"名称"这个要素,而"空间"这个要素并不满足.换句话说,引用需要与它所引用的变量共享同一个内存空间,对引用所做的改变实际上是对所引用的变量做出修改.并且引用在定义的时候就必须被初始化.     参数传递的类型及相关要点: 1 按值

  • IOS开发之路--C语言指针

    概览 指针是C语言的精髓,但是很多初学者往往对于指针的概念并不深刻,以至于学完之后随着时间的推移越来越模糊,感觉指针难以掌握,本文通过简单的例子试图将指针解释清楚,今天的重点有几个方面: 什么是指针 数组和指针 函数指针 什么是指针 存放变量地址的变量我们称之为"指针变量",简单的说变量p中存储的是变量a的地址,那么p就可以称为是指针变量,或者说p指向a.当我们访问a变量的时候其实是程序先根据a取得a对应的地址,再到这个地址对应的存储空间中拿到a的值,这种方式我们称之为"直接

  • Swift中的指针操作和使用详细介绍

    Apple期望在Swift中指针能够尽量减少登场几率,因此在Swift中指针被映射为了一个泛型类型,并且还比较抽象.这在一定程度上造成了在Swift中指针使用的困难,特别是对那些并不熟悉指针,也没有多少指针操作经验的开发者(包括我自己也是)来说,在Swift中使用指针确实是一个挑战.在这篇文章里,我希望能从最基本的使用开始,总结一下在Swift中使用指针的一些常见方式和场景.这篇文章假定你至少知道指针是什么,如果对指针本身的概念不太清楚的话,可以先看看这篇五分钟C指针教程(或者它的中文版本),应

  • Go语言中的指针运算实例分析

    本文实例分析了Go语言中的指针运算方法.分享给大家供大家参考.具体分析如下: Go语言的语法上是不支持指针运算的,所有指针都在可控的一个范围内使用,没有C语言的*void然后随意转换指针类型这样的东西.最近在思考Go如何操作共享内存,共享内存就需要把指针转成不同类型或者对指针进行运算再获取数据. 这里对Go语言内置的unsafe模块做了一个实验,发现通过unsafe模块,Go语言一样可以做指针运算,只是比C的方式繁琐一些,但是理解上是一样的. 下面是实验代码: 复制代码 代码如下: packag

  • C语言指针详解及用法示例

    新手在C语言的学习过程中遇到的最头疼的知识点应该就是指针了,指针在C语言中有非常大的用处.下面我就带着问题来写下我对于指针的一些理解. 指针是什么? 指针本身是一个变量,它存储的是数据在内存中的地址而不是数据本身的值.它的定义如下: int a=10,*p; p=&a int a=10; int *p=&a; 首先我们可以理解 int* 这个是要定义一个指针p,然后因为这个指针存储的是地址所以要对a取地址(&)将值赋给指针p,也就是说这个指针p指向a. 很多新手都会对这两种定义方法

  • C++中回调函数及函数指针的实例详解

    C++中回调函数及函数指针的实例详解 如何获取到类中函数指针 实现代码: //A类与B类的定义 class A { public: void Test() { cout << "A::Test()" << endl; } }; class B : public A { public: void Test() { cout << "B::Test()" << endl; } }; //定义类的成员函数指针 typedef

  • C++ 中boost::share_ptr智能指针的使用方法

    C++ 中boost::share_ptr智能指针的使用方法 最近项目中使用boost库的智能指针,感觉智能指针还是蛮强大的,在此贴出自己学习过程中编写的测试代码,以供其他想了解boost智能指针的朋友参考,有讲得不正确之处欢迎指出讨论.当然,使用boost智能指针首先要编译boost库,具体方法可以网上查询,在此不再赘述. 智能指针能够使C++的开发简单化,主要是它能够自动管理内存的释放,而且能够做更多的事情,即使用智能指针,则可以再代码中new了之后不用delete,智能指针自己会帮助你管理

  • Go语言的方法接受者类型用值类型还是指针类型?

    概述 很多人(特别是新手)在写 Go 语言代码时经常会问一个问题,那就是一个方法的接受者类型到底应该是值类型还是指针类型呢,Go 的 wiki 上对这点做了很好的解释,我来翻译一下. 何时使用值类型 1.如果接受者是一个 map,func 或者 chan,使用值类型(因为它们本身就是引用类型). 2.如果接受者是一个 slice,并且方法不执行 reslice 操作,也不重新分配内存给 slice,使用值类型. 3.如果接受者是一个小的数组或者原生的值类型结构体类型(比如 time.Time 类

  • C++常量详解一(常量指针与常量引用的初始化)

    1.常量 1.1.常量的初始化: const对象一旦创建后其值就不能再改变,所以const对象必须初始化.这里我们要注意一点,像const int *p和const int &r都并不是const对象.因为const int *p只是表示不能通过p改变p所指的对象的值,p的值是可以变的,所以p可以不用初始化.至于r ,引用本身就不是对象,所以r也并不是const对象,r之所以一定初始化,是因为引用必须初始化.对于以上内容,你也可以理解为底层const 修饰的并不是const对象,还要注意像con

  • 简单谈谈C++中指针与引用的区别

    指针与引用是C++中两个很重要的概念,它们功能看过去很相似,就是都是间接引用某个对象,那么我们应该在什么时候使用指针,什么时候使用引用呢,下面请允许我慢慢道来: 1.永远不要使用一个指向空值的引用.一个引用必须始终指向某个对象,所以当你确定使用一个变量指向某个对象时,但是这个对象在某些时间可能指向控制,这时候你就必须把变量声明为指针类型,而不是引用!当你确定这个变量始终指向某个对象是,这时候你就可以把变量声明为引用类型. char *str=0; //设置指针为空值 char &s=*str;

随机推荐