目录

一、普通指针(一级)

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.指针变量和普通变量的异同点

  • 相同点:
  1. 都能改变
  2. 都有地址
  • 不同点:
  1. 指针变量可以解引用,普通变量不可以

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 用十进制转换输出

总结

 指针的使用

  1. 指针类型必须严格相等才能赋值(就是变量什么类型,指针什么类型)
  2. 任意数量的指针变量都可以指向同一个对象。p2=p1是使p2指向和p1同一块地址,*p2=*p1是把p1所指向地址的值复制给p2。
  3. *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.指针与引用

引用与指针有什么区别?

  1. 引用必须被初始化,指针不必。引用相当于是给变量起别名,别名确定后不会修改

    int x=10;
    int y=20;
    int& z=x;
    z=y; //x=20
  2. 引用初始化以后不能被改变,指针可以改变所指的对象。
  3. 指针可以改变指向,引用不可以。
  4. 不存在指向空值的引用,但是存在指向空值的指针。
  5. 引用只能是一级的,指针可以多级。
  6. 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空指针,当前为无效指针,相当于告诉用户该指针作废

野指针:没有访问权限,指针定义时未初始化,易产生野指针

【指针】一级指针二级指针知识点梳理相关推荐

  1. 关于C语言中的数组指针、指针数组以及二级指针

    概念解释 数组指针:首先它是一个指针,它指向一个数组,即指向数组的指针:在32 位系统下永远是占4 个字节,至于它指向的数组占多少字节,不知道.数组指针指向的是数组中的一个具体元素,而不是整个数组,所 ...

  2. 计算机二级指针,C语言——二级指针

    二级指针的概念 首先任何值都有地址,一级指针的值虽然是地址,但这个地址做为一个值亦需要空间来存放,是空间就具有地址,这就是存放地址这一值的空间所具有的地址,二级指针就是为了获取这个地址, 一级指针所关 ...

  3. C语言指针(函数指针数组,二级指针)

    int (*p[num])( char*,int,int );int (*(*p)[num])(int a); 上一篇博客最后的两个例子的答案分别是:函数指针数组,函数指针数组的指针. 函数指针数组指 ...

  4. 【C语言指针】 指针+-整数、指针-指针、解引用、指针数组、二级指针、结构体声明、初始化、传参

    目录 一.指针 1.指针是什么 1.1.一个单元1个字节 2.指针和指针类型 2.1.指针类型的意义 ① 解引用 ② + -整数 例:把每个整形里放1 每个字节里放1 总结: 3.野指针 3.1.野指 ...

  5. 【C 语言】二级指针作为输入 ( 自定义二级指针内存 | 为 二级指针 分配内存 - 存放 一维指针 | 为每个 一级指针 分配内存 | 释放二维指针内存 )

    文章目录 一.二级指针 1.为 二维指针 分配内存 2.为每个 一维指针 分配内存 3.释放 二维指针 内存 二.完整代码示例 一.二级指针 声明二级指针 : // 声明二维指针char **p = ...

  6. C++一级与二级指针基础详解

    指针可以指向一份普通类型的数据,例如 int.double.char 等,也可以指向一份指针类型的数据,例如 int *.double *.char * 等. 如果一个指针指向的是另外一个指针,我们就 ...

  7. 一级指针赋值与二级指针赋值

    一级指针:指向一段内存 1.内存变量:a .*p1: 2.内存地址:&a. p1: 3.指针自身地址: &p1: 二级指针:指向指针p1的指针p2, 1.内存变量:a .*p1 .** ...

  8. C指针8:二级指针(意思就是指向指针的指针)

    指针可以指向一份普通类型的数据,例如 int.double.char 等,以下简称一级指针: 也可以指向一份指针类型的数据,例如 int *.double *.char * 等.以下简称二级指针:即如 ...

  9. 【C 语言】二级指针作为输入 ( 指针数组 | 将 二级指针 作为函数输入 | 抽象函数业务逻辑 )

    文章目录 一.打印 指针数组 中指针指向的字符串 二.字符串排序 三.代码示例 一.打印 指针数组 中指针指向的字符串 打印 指针数组 中指针指向的字符串 : 指针退化问题 : 传入二级指针 , 同时 ...

  10. 男女会籍注册(指针数组+链表+二级指针)

    编写以下函数实现: (1)doregister(n, arr), 从控制台输入n个会员信息,按性别存储两个链表(名字升序排序),每个链表的头节点存入指针数组arr中: (2)unregister(ar ...

最新文章

  1. sy-repid 和 sy-cprog的区别
  2. 批处理命令 / rd 和 del
  3. bootstrap评分插件 Bootstrap Star Rating Examples
  4. 梁迪:我为MVP骄傲,《微软最有价值专家奖励计划介绍》附专题视频
  5. 原生js浏览器兼容性问题
  6. [7.14更新日志]CRP编译缓存和海外构建让编译健步如飞!
  7. PHP的strpos函数辨析
  8. java实现deflate算法
  9. C#TcpClien网络通信之内存泄漏
  10. VIA声卡升级驱动后没有控制台怎么办?
  11. 一个网工的十年奋斗史 - 工作篇
  12. 2015年移动Web/HybridApp开发技能列表
  13. mysql周报内容范文_mysql 数据分析如何实现日报、周报、月报和年报?
  14. 如何在GitHub上传并更新项目
  15. 北方工业大学编译原理——四、五章单元测试内容复习
  16. python打包成可执行文件_将Python文件打包成.EXE可执行文件的方法
  17. 武汉移动137和武汉电信189手机业务比较
  18. Gitlab下新建project并上传本地project整理
  19. 高压线性 LED恒流驱动器 PWM调光 内置MOS1A 外置MOS 2A
  20. java io中断_Java的Interrupt与线程中断

热门文章

  1. 【3】Ubuntu18.04下载与安装,以及中文输入法的安装
  2. 年度绩效考核演示PPT模板
  3. ar8171 linux网卡驱动,ar8171 8175网卡驱动(ar8171网卡驱动下载)V1.0.1 官方最新版
  4. linux ar -x64,Linux ar 命令 command not found ar 命令详解 ar 命令未找到 ar 命令安装 - CommandNotFound ⚡️ 坑否...
  5. android自动生成dimens适配文件
  6. 第六天----数据结构笔记
  7. html实现轮播图--小圆圈呈中间大两边小的样式
  8. Prim(各种功能)
  9. 标准方程法(正规方程法)
  10. 【Flutter】如何完成一个透明沉浸式状态栏