【指针】一级指针二级指针知识点梳理
目录
一、普通指针(一级)
1.定义
2.指针变量和普通变量的异同点
3.使用:
4.指针字节大小
5.习题练习
总结
6.指针的好处
总结
7.指针与引用
8.指针与const
Q&A
二、二级指针
一、普通指针(一级)
前言:
大多数现代计算机都将内存分割为字节(byte),每个字节可以存储8位的信息。每个字节都有唯一的地址,用来和内存中其它字节相区别。
可执行程序由代码(原始C语言中与语句对应的机器指令)和数据(原始程序中的变量)两部分构成。程序中的每个变量占有一个或多个字节内存,把第一个字节的地址称为变量的地址,这就是指针的出处。
1.定义
- 指针变量的声明:和普通变量基本一样,唯一的不同是在指针变量名字前放星号*:int *p;
- 取地址运算符:为了找到变量的地址,可以用取地址运算符&,如果x是变量,&x就是x在内存中的地址。& 变量名(获取该变量的地址) ,针对所有变量都适用。
输出地址:printf(“%d”,&a); //%d 输出10进制 ,%p输出16进制,%0输出八进制
补充:scanf里scanf("%d",&a);用&输入值也是一样的道理,通过地址才能在scanf函数中形参中把值带过来。如果int i,*p=&i; scanf("%d",p);此时如果再写&p就是错误的。
- 间接寻址运算符:如果p是指针,*p就是p当前指向的对象。
- 指针变量初始化:
//1.可以在声明的同时对它初始化 int i; int *p=&i; //2.i的声明可以和p的初始化一起 int i,*p=&i;
2.指针变量和普通变量的异同点
- 相同点:
- 都能改变
- 都有地址
- 不同点:
- 指针变量可以解引用,普通变量不可以
3.使用:
通过解引用修改变量的值:
- 只要p指向i,*p就是i的别名,*p不仅拥有和i相同的值,而且*p的改变也会改变i的值。
- *+指针变量:解引用,间接访问符,*p就是对p解引用
例如:int* p=&a;
*p=100;
p指向a,p格子内是a的地址。
*p是对a解引用,得到a格子内的值,也就是a的值,令*p=100,则a=100
补充,对*的理解
- int *p=&a;中*不是间接寻址运算符,其作用是指明p类型是指针
- *p=100; *作为解引用,执行间接寻址
- *p=&a;是错误的,因为它把a的地址赋给了p所指向的对象,而不是p本身
- int p;//定义整型变量
- int* p; //定义整形地址变量
- &a; //整型地址变量
- p=&a;//整型地址变量p保存整形a地址值
- int*p=&a;//把上面两步合并
4.指针字节大小
- 与类型无关,由平台决定
- X86:32位操作系统;2^32 一个字节8位,这种情况下,指针4个字节
- x64:64位操作系统 2^68 这种情况下,指针8个字节
- 一字节=-128到127 256 2^8
- 1TB=1024GB;
- 1GB=1024MB;
- 1MB=1024KB;
- 1KB=1024B;
- 1B=8b;
补充:
(1)Int*p1,p2,p2;
p1:指针变量,p2,p3:整型变量
(2)typedef int* INT;
INT p1,p2,p3; //此时,三个均为指针变量,等价于Int*p1,*p2,*p2;
(3)#define INT int*
INT p1,p2,p3; //此时,仅第一个为指针变量,等价于Int*p1,p2,p2;
5.习题练习
eg1:
知识点:指针类型必须严格相等才能赋值(就是变量什么类型,指针什么类型)
char a;
short b;
int c;
unsigned long d;
double e;
float f;
(1)定义指针p1,保存a的地址
char* p1 = &a;
(2)定义指针p2,保存b的地址
short* p2 = &b;
(3)定义指针p3,保存c的地址
int* p2 = &c;
(4)定义指针p4,保存d的地址
unsigned long* p2 = &d;
(5)定义指针p5,保存e的地址
double* p5 = &e;
(6)定义指针p6,保存f的地址
float* p6 = &f;
eg2:
int a, b, c;
int* p1 = &a;
int* p2 = &b;
int* p3 = &c;
(1)通过p1,将a的值改为20,并输出;
*p1 = 20;
printf("%d", *p1);
(2)把p1的值赋给p2,通过p2修改a的值为30,并输出;
p2=p1; //注意p2=p1是使p2指向和p1同一块地址,*p2=*p1是把p1所指地址的值复制给p2。
*p2=30
printf("%d", *p2);
//知识点:任意数量的指针变量都可以指向同一个对象
(3)通过p1,p2,p3,将c的值改为a×b,并输出;
*p3 = *p2 * *p2;
printf("%d", *p3);
(4)定义指针p,指向a
int* p = &a;
(5)通过p1,将a的值改为10;
*p1=10;
(6)定义指针p,将p指向b,并通过p将b的值改为20
int*p = &b;
*p = 20;
(8)定义指针p,通过p输出a,b的值(需要多条语句)
int*p;
p = &a; printf("%d", *p);//输出a的值;
p = &b; printf("%d", *p);//输出b的值;
eg3:问输出结果为?
void main() {char a[]={1,1,1,1};int *p=(int*)a;printf("*p=%d\n",*p); }
解:
- (int*) :强转
- *p: 从p所指向的首地址开始,以它基类型(基类型:当前指针变量所指向的变量类型)所指的首地址为偏移量
- int 4字节:取4个地址
- 题中假设a[0]地址为Ox2000,那么就是以Ox2000开始偏移4个
- a[0]=a[1]=a[2]=a[3]=a[4]=00000001
- 偏移4个就是1000000010000000100000001
- %d 十进制整数形式
- %O 八进制
- %X 十六进制
- 输出结果为%d形式,也就是将1000000010000000100000001 用十进制转换输出
总结
指针的使用
- 指针类型必须严格相等才能赋值(就是变量什么类型,指针什么类型)
- 任意数量的指针变量都可以指向同一个对象。p2=p1是使p2指向和p1同一块地址,*p2=*p1是把p1所指向地址的值复制给p2。
- *p: 从p所指向的首地址开始,以它基类型(基类型:当前指针变量所指向的变量类型)所指的首地址为偏移量
6.指针的好处
(1)一般变量想要带回值,只能用return带回一个值,而指针可以通过形参带回多个值。
eg:
- eg1:
主函数调用MyScanf(),将实参x,y传给形参a,b,形参类型是指针类型,可以将1,0带回给主函数:x=1,y=0。
void MyScanf(int* a, int* b) {*a = 1;*b = 0; }int main() {int x ;int y ;MyScanf(& x, & y);return 0; }
- eg2:
主函数调用Operate(),将1,2以及x,y传递给形参。形参接收到后,在函数内部实现a+b与a*b,因为参数类型是指针,所以在主函数内x=3,y=2。
int Operate(int a,int b,int* sum,int* mul) {*sum=a+b;//主函数里把地址传过来,该函数内部对其解引用替换值*mul=a*b; }int main() {int x,y;Operate(1,2,&x,&y); }
(2)因为形参与实参的地址不一样,形参是局部变量,无法通过形参直接修改实参值,需要地址改变,而指针可以通过调用函数改变实参的值。
例: 经典不同参数的交换函数
void Swap1(int a,int b)
{int temp=a;a = b;b = temp;
}
void Swap2(int *a,int* b)
{int *temp=a;a = b;b = temp;
}void Swap3(int *a,int* b)
{int temp=*a;*a = *b;*b = temp;
}
void Swap4(int &a,int& b)
{int temp=a;a = b;b = temp;
}//如下Swap函数错误:对野指针进行解引用void Swap(int *a,int* b)
{int *temp=*a;*a = *b;*b = *temp;
}
- 参数传递
- (a+b)+abs(a-b)/2 =>得到a和b中大的数
- swap1:
- 传的是值,值传递是拷贝,并 未发生交换现象
- swap2:
- swap2内的交换了,但是主函数的不变
- 以水杯和水杯内的水果举例,a是水杯,是地址,*a是水果,是地址所指向的内容。在swap2中,传的是地址,交换的是指针的指向(主函数把水杯给swap2函数,swap2交换的是水杯,不是水杯内的水果)
- swap3:
- *a是值,是a所指向空间内的内容,a是指针
- swap3和主函数内的值均改变了
- swap4:
- 引用在主函数时是(x,y),不写&
- swap4和主函数内的值均改变了
- 总结:
- 当形参和实参是同一地址,修改里面的内容就要改变
- 是同一空间,修改的内容不会改变
总结
指针的好处:
1.可以通过形参带回多个值
2.可以通过形参改变实参的值
7.指针与引用
引用与指针有什么区别?
- 引用必须被初始化,指针不必。引用相当于是给变量起别名,别名确定后不会修改
int x=10; int y=20; int& z=x; z=y; //x=20
- 引用初始化以后不能被改变,指针可以改变所指的对象。
- 指针可以改变指向,引用不可以。
- 不存在指向空值的引用,但是存在指向空值的指针。
- 引用只能是一级的,指针可以多级。
- sizeof(指针)的大小是固定的,根据平台决定指针的大小,而sizeof(引用)是变量的大小
8.指针与const
用const保护参数
当看到fun(&x);语句时,通常会认为调用指针来修改参数的值,但是有时候fun也只是用来检查x的值而不是修改,因为传递指针可以提高效率——如果需要大量的存储空间,传递变量的值可能会浪费时间和空间。因此,在这种不需要修改指针值的情况下,可以用const进行保护,const应该放在形参的声明中,后面紧跟形参的类型说明:
void fun(const int *p){...}
值得注意的是,这样的声明会被错误理解为函数fun不能修改p,但是实际是不能修改指针类型执行的整数,但是并不阻止fun修改p本身。
Q&A
- Q:指针总和地址一样吗
- A:通常是,但不总是。在一些计算机上,指针可能是“偏移量”而不完全是地址。
- Q:如果指针可以指向程序中的数据,那么使指针只想程序中的代码,是否有可能?
- A:可能,函数指针。
二、二级指针
举例说明:
例:int a=10;
int *p=&a; //p指向a,(p格子内)保存a的地址
int **pp=&p; //pp指向p,(pp格子内)保存p的地址
“*”:解引用(格子箭头往前指)
pp:pp格子=200;
*pp:p的值=100 //1个“*”,箭头往前走1下。*pp是对p解引用得到p格子内的值,也就是100;
**pp:a的值=10 //2个“*”,箭头往前走2下。**pp是对a解引用得到a格子内的值,也就是10;
用法:
改变pp的指向,使其指向q:pp=&q;
通过二级指针pp修改a的值:**pp=200;
总结:
一级指针关联变量的地址,
二级指针关联一级指针的地址。
补充:
&&a错误
*&a=a; 对的,对a取地址又对其解引用
&*a错误,不能对a解引用
例题分析
eg1:
int a = 10;
int b = 20;
int* p;
p = &a;
*p = 30;
int** pp = &p;
(1)a,和**p的值为多少?
a=30; //p指向a,*p = 30修改了a的值;
**pp = 30;//解两次引用,箭头跳两次指向a,a格子内值为30
eg2:
int a = 10;
int b = 20;
int* p= &a;
*p=100;
int** pp = &p;
*pp = &b;
**pp =1000;
int ***ppp =&pp;
***ppp=1;
(2)a和b的值为多少?
*p=100; 将a的值改为100
*pp = &b; 将p的指向修改为b
**pp =1000;解2次引用,将b的值修改为1000
int ***ppp =&pp; 定义ppp指向pp
***ppp=1;将b的值修改为1
所以,a=100,b=1
补充:对野指针的理解
int *p=&a
printf(“%d”,*p); //可以
但是
p=NULL;
printf(“%d”,*p);//不行,不能对空进行解引用
但是,如下情况:
定义Fun(p)函数,在内部对p置空,调用Fun(p),再输出printf(“%d”,*p)
void Fun(int* p)
{
p = NULL;
}
int main()
{
int a = 10;
int* p = &a;
Fun(p);
printf("d\n", *p);
return 0;
}
//此时发现Fun函数并没有起作用,因为:A函数调用B函数,如果需要通过修改A中实参的值,则必须传指针,在B中再解引用
//fun函数未被执行,想要通过另一个修改其值,必须传指针,函数内部解引用(加*)
//如下所示,会导致错误
void Fun(int** p)
{
**p = NULL;
}
总结:
NULL空指针,当前为无效指针,相当于告诉用户该指针作废
野指针:没有访问权限,指针定义时未初始化,易产生野指针
【指针】一级指针二级指针知识点梳理相关推荐
- 关于C语言中的数组指针、指针数组以及二级指针
概念解释 数组指针:首先它是一个指针,它指向一个数组,即指向数组的指针:在32 位系统下永远是占4 个字节,至于它指向的数组占多少字节,不知道.数组指针指向的是数组中的一个具体元素,而不是整个数组,所 ...
- 计算机二级指针,C语言——二级指针
二级指针的概念 首先任何值都有地址,一级指针的值虽然是地址,但这个地址做为一个值亦需要空间来存放,是空间就具有地址,这就是存放地址这一值的空间所具有的地址,二级指针就是为了获取这个地址, 一级指针所关 ...
- C语言指针(函数指针数组,二级指针)
int (*p[num])( char*,int,int );int (*(*p)[num])(int a); 上一篇博客最后的两个例子的答案分别是:函数指针数组,函数指针数组的指针. 函数指针数组指 ...
- 【C语言指针】 指针+-整数、指针-指针、解引用、指针数组、二级指针、结构体声明、初始化、传参
目录 一.指针 1.指针是什么 1.1.一个单元1个字节 2.指针和指针类型 2.1.指针类型的意义 ① 解引用 ② + -整数 例:把每个整形里放1 每个字节里放1 总结: 3.野指针 3.1.野指 ...
- 【C 语言】二级指针作为输入 ( 自定义二级指针内存 | 为 二级指针 分配内存 - 存放 一维指针 | 为每个 一级指针 分配内存 | 释放二维指针内存 )
文章目录 一.二级指针 1.为 二维指针 分配内存 2.为每个 一维指针 分配内存 3.释放 二维指针 内存 二.完整代码示例 一.二级指针 声明二级指针 : // 声明二维指针char **p = ...
- C++一级与二级指针基础详解
指针可以指向一份普通类型的数据,例如 int.double.char 等,也可以指向一份指针类型的数据,例如 int *.double *.char * 等. 如果一个指针指向的是另外一个指针,我们就 ...
- 一级指针赋值与二级指针赋值
一级指针:指向一段内存 1.内存变量:a .*p1: 2.内存地址:&a. p1: 3.指针自身地址: &p1: 二级指针:指向指针p1的指针p2, 1.内存变量:a .*p1 .** ...
- C指针8:二级指针(意思就是指向指针的指针)
指针可以指向一份普通类型的数据,例如 int.double.char 等,以下简称一级指针: 也可以指向一份指针类型的数据,例如 int *.double *.char * 等.以下简称二级指针:即如 ...
- 【C 语言】二级指针作为输入 ( 指针数组 | 将 二级指针 作为函数输入 | 抽象函数业务逻辑 )
文章目录 一.打印 指针数组 中指针指向的字符串 二.字符串排序 三.代码示例 一.打印 指针数组 中指针指向的字符串 打印 指针数组 中指针指向的字符串 : 指针退化问题 : 传入二级指针 , 同时 ...
- 男女会籍注册(指针数组+链表+二级指针)
编写以下函数实现: (1)doregister(n, arr), 从控制台输入n个会员信息,按性别存储两个链表(名字升序排序),每个链表的头节点存入指针数组arr中: (2)unregister(ar ...
最新文章
- sy-repid 和 sy-cprog的区别
- 批处理命令 / rd 和 del
- bootstrap评分插件 Bootstrap Star Rating Examples
- 梁迪:我为MVP骄傲,《微软最有价值专家奖励计划介绍》附专题视频
- 原生js浏览器兼容性问题
- [7.14更新日志]CRP编译缓存和海外构建让编译健步如飞!
- PHP的strpos函数辨析
- java实现deflate算法
- C#TcpClien网络通信之内存泄漏
- VIA声卡升级驱动后没有控制台怎么办?
- 一个网工的十年奋斗史 - 工作篇
- 2015年移动Web/HybridApp开发技能列表
- mysql周报内容范文_mysql 数据分析如何实现日报、周报、月报和年报?
- 如何在GitHub上传并更新项目
- 北方工业大学编译原理——四、五章单元测试内容复习
- python打包成可执行文件_将Python文件打包成.EXE可执行文件的方法
- 武汉移动137和武汉电信189手机业务比较
- Gitlab下新建project并上传本地project整理
- 高压线性 LED恒流驱动器 PWM调光 内置MOS1A 外置MOS 2A
- java io中断_Java的Interrupt与线程中断
热门文章
- 【3】Ubuntu18.04下载与安装,以及中文输入法的安装
- 年度绩效考核演示PPT模板
- ar8171 linux网卡驱动,ar8171 8175网卡驱动(ar8171网卡驱动下载)V1.0.1 官方最新版
- linux ar -x64,Linux ar 命令 command not found ar 命令详解 ar 命令未找到 ar 命令安装 - CommandNotFound ⚡️ 坑否...
- android自动生成dimens适配文件
- 第六天----数据结构笔记
- html实现轮播图--小圆圈呈中间大两边小的样式
- Prim(各种功能)
- 标准方程法(正规方程法)
- 【Flutter】如何完成一个透明沉浸式状态栏