指针

概念

指针是什么?

在计算机科学中,指针(Pointer)是编程语言中的一个对象,利用地址,它的值直接指向存在电脑存储器中另一个地方的值。由于通过地址能找到所需的变量单元,可以说,地址指向该变量单元。因此,将地址形象化的称为“指针”。意思是通过它能找到以它为地址的内存单元。

内存地址

  • 字节(byte):字节是内容的容量单位,英文称为byte,一个字节有8位,即1byte = 8bits
  • 字(word):4byte = 1字 半字:2Byte
  • 地址:系统为了便于区分每一个字节而对它们逐一进行的编号,称为内存地址。
    • 地址编号在同一块内存是连续的

​ 32Bit系统:内存的地址编号宽度一般是32Bit

​ 64Bit系统:内存的地址编号宽度一般是64Bit

基地址

  • 单字节数据:对于单字节数据而言,其地址就是其字节编号。例如:char

  • 多字节数据:对于多字节数据而言,其地址是其所有字节中编号最小的那个,称为基地址。例如:short、int、long、double…

地址相关操作–取地址符

  • 每个变量都是一块内存,都可以通过取址符& 获取其地址

  • 例如:

    int a = 100;
    printf("整型变量 a 的地址是: %p\n", &a);
    char c = 'x';
    printf("字符变量 c 的地址是: %p\n", &c);
    double f = 3.14;
    printf("浮点变量 f 的地址是: %p\n", &f);
    
  • 注意:

    • 虽然不同的变量占内存空间的大小是不同的,但是他们的地址的大小却是一样的
    • 不同的地址虽然形式上看起来是一样的,但由于他们代表的内存尺寸和类型都不同,因此它们在逻辑上是严格区分的。

指针基础

  • 指针的概念

    • 指针本身是一种数据类型 ,用指针类型(*)定义的变量称之为指针变量—简称指针

    • 地址。比如 &a 是一个地址,&a 指向变量 a

    • 专门用于存储地址的变量,又称指针变量。

  • 指针的定义

    int    *p1; // 用于存储 int 型数据的地址,p1 被称为 int 型指针,或称整型指针
    char   *p2; // 用于存储 char 型数据的地址,p2 被称为 char 型指针,或称字符指针
    double *p3; // 用于存储double型数据的地址,p3 被称为 double 型指针
    
  • 指针的赋值:赋给指针的地址,类型需跟指针的类型相匹配。

    int a = 100;
    p1 = &a; // 将一个整型地址,赋值给整型指针p1char c = 'x';
    p2 = &c; // 将一个字符地址,赋值给字符指针p2double f = 3.14;
    p3 = &f; // 将一个浮点地址,赋值给浮点指针p3
    

    那指针类型的意义是什么?

所以,指针的类型决定了指针向前或者向后走一步有多大(距离)

  • 指针的索引—解引用:通过指针,取得其指向的目标

    *p1 = 200; // 将 p1 指向的目标(即a)修改为200,等价于 a = 200;
    *p2 = 'y'; // 将 p2 指向的目标(即c)修改为'y',等价于 c = 'y';
    *p3 = 6.6; // 将 p3 指向的目标(即f)修改为6.6,等价于 f = 6.6;
    

    内存空间的变化



所以,指针的类型决定了对指针解引用的时候有多大的权限(能操作几个字节),比如:char的指针解引用就只能访问一个字节,所以*pc = 0就将空间中的44改为00,而int的指针解引用就能访问四个字节,所以*pi = 0就将内存中的 00332211改为00000000。(第3幅图应该圈住4个00)

  • 指针的尺寸–即指针变量占用空间大小

    • 指针尺寸指的是指针所占内存的字节数
    • 指针所占内存,取决于地址的长度,而地址的长度则取决于系统寻址范围,即字长
    • 结论:指针尺寸只跟系统的字长有关,跟具体的指针的类型无关

总结:指针就是变量,用来存放地址的变量。(存放在指针中的值都被当成地址处理)。

到这里的问题是:

  1. 一个小的单位到底是多大?(1个字节)

  2. 如何编址?

    从上面的内存地址部分我们了解到,一个字节给一个对应的地址是比较合理的。

    对于32位的机器,假设有32根地址线,那么假设每根地址线在寻址的是产生一个电信号正电/负电(1或0)

    那么32根地址线产生的地址就会是:

    00000000 00000000 00000000 00000000

    00000000 00000000 00000000 00000001

    11111111 11111111 11111111 11111111

    这里就有2的32次方个地址,每个地址标识一个字节,那我们就可以给(232Byte == 232/1024KB == 232/1024/1024MB== 4GB)4G的空闲进行编址。

    同样的方法,那么64位机器,如果给64根地址线,那能编址多大空间?(远大于32位系统)

    所以:

    1. 32位的机器上,地址是32个0或者1组成二进制序列,那地址就得用4个字节的空间来存储,所以一个指针变量的大小就应是4字节

    2. 那如果在64位机器上,如果有64位地址线,那一个指针变量的大小是8字节,才能存放一个地址。

    总结:

    1. 指针是用来存放地址的,地址是唯一标识一块地址空间的。

    2. 指针的大小在32位平台是4字节,在64位平台是8字节。

野指针

概念

野指针就是指针指向的位置是不可知的(随机的,不正确的、没有明确限制的)

野指针成因

  1. 指针未初始化
int *p;//局部变量指针未初始化,默认为随机值
*p = 20;//将20存放到一块未知的空间,是很危险的。
  1. 指针越界访问
int arr[10] = {0};
int *p = arr;
int i = 0;
for(i = 0;i<=11;i++)
{//当指针指向的范围超出数组arr的范围时,p就是野指针*(p++) = i;
}
  1. 指针指向的空间释放
int* test()
{int a = 10;//a是局部变量,函数返回后该变量就会被释放return &a;
}
int main()
{int *p = test();//这时候获得的空间是一块未知空间,是没有权限操作该空间的(因为该空间可能被分配给其它的程序使用,强行操作是很危险的)*p = 20;return 0;
}

如何规避野指针?

  1. 指针初始化
int a = 10;
int *pa = &a;//初始化
int *p = NULL;//NULL为(void*)0,是所有程序都访问不到的区域
//当不知道怎么给指针初始化的时候,先给指针初始化为NULL,这样操作至少不会出现段错误。
int *pa = NULL;//NULL用来初始化指针的,给指针赋值
  1. 小心指针越界
  2. 指针指向空间释放后立即将其置为NULL
  3. 指针使用之前检查有效性

野指针的危害

  1. 使用野指针,相当于访问了非法的内存,常常导致段错误。(segmentation fault)
  2. 使用野指针,可能会破坏系统的关键数据,导致系统奔溃等严重后果。

指针运算

  • 指针加法意味着地址向上移动若干个目标
  • 指针减法意味着地址向下移动若干个目标
  • 示例:
int  a = 100;
int *p = &a; // 指针 p 指向整型变量 a
int *k1 = p + 2; // 向上移动 2 个目标(2个int型数据)
int *k2 = p - 3; // 向下移动 3 个目标(3个int型数据)

标准规定:

允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但不允许与指向第一个元素之前的那个内存位置的指针进行比较。

空指针

  1. 当无法确定一个地址所对应的内存的数据类型时,将该地址类型定义为void型,即通用型。

    void *p = malloc(8);//申请一块未知用途的空间
    
  2. void 型指针在解引用时,必须转化为某种具体的数据类型指针,否则无法解引用。

    double f = 3.14
    *p = f;//错误的
    *(double *)p = f; //正确,进行类型转换
    

很多情况下,我们不可避免地会遇到野指针,比如刚定义的指针无法立即为其分配一块恰当的内存,又或者指针所指向的内存被释放了等等。一般的做法就是将这些危险的野指针指向一块确定的内存,比如零地址内存(NULL)。

Linux规定:进程的虚拟内存从0x0000 00000x0804 8000为不可访问的内存,这段内存的权限为0,也就是任何进程访问这段内存就一定会遇到权限不足无法运行的错误而被系统强制关闭,以免程序拿到一个野指针,造成更大的损失。

NULL实际上就是一个宏:#define NULL (void *)0

// 1,刚定义的指针,让其指向零地址以确保安全:
char *p1 = NULL;
int  *p2 = NULL;// 2,被释放了内存的指针,让其指向零地址以确保安全:
char *p3 = malloc(100); //让 p3 指向一块大小为100个字节的内存
free(p3);               //释放这块内存,此时 p3 相当于指向了一块非法内存
p3 = NULL;              //让 p3 指向零地址

二级指针

指针变量也是变量,是变量就有地址,用来存放指针变量地址的变量就是二级指针变量。以此类推,也可以得到三级指针、四级指针…

int a = 10;
int *pa = &a;
int **ppa = &pa;
//a的地址存放在pa中,pa的地址存放在ppa中;
//pa是一级指针,ppa是二级指针。

二级指针的运算

  1. *ppa通过对ppa中的地址进行解引用,这样找到的是pa,*ppa其实访问的就是pa。

  2. **ppa先通过*ppa找到pa。然后对pa进行解引用操作:*pa,那找到的是a。

    **ppa = 30;
    等价于*pa = 30;
    等价于 a = 30;
    

指针与数组的关系

  1. 数组名可以作为一个指针来使用,数组名是一个指针常量 ,不能改变只能用。

    char  a[10];
    char *p;
    a = p;//错误的,a只是一个指针常量
    能够参与运算
    p = a;//允许
    
  2. 指针和数组的访问方式可以互换

  • 数组名当成一个指针 用指针的运算访问成员 ; *a[n] == (a+n)
  • 指针用数组的下标访问方式 访问数组成员;*(p+0) == p[0] == a[0]
  • 数组指针—指向一个数组的指针;*char (p)[20];
  • 指针数组—存放若干个相同类型指针的数组;char *p[20];//定义了一个数组 有20个成员 20个char *类型成员

指针常量与常量指针

指针常量:int * const p;//关键字const修饰的是p,所以p是一个常量

int a = 10;
int b = 20;
int *const p = &a;//定义时就需要初始化,不能int *const p = NULL;   p = &a;这是错误的,因为这时候的p已经没办法改变了
p = &b;//这是错误的,p是常量,没办法改变的
*p = 20;//这是正确的

常量指针:int const *p; 或者 const int *p;//这时候的const修饰的是*,p是一个变量,*p是一个常量.(这种表达我不知道对不对,但这样理解是没问题的)

int a = 10;
int b = 20;
int const *p = &a//可以int const *p = NULL; p = &a;
*p = 20;//这是错误的,*p是一个常量,不可改变
p = &b;//这是正确的

>>
结构体及其对齐规则
系统IO与标准IO
C语言实现24点游戏算法

指针、野指针、指针常量、常量指针相关推荐

  1. c/c++教程 - 1.9 指针 空指针 野指针 const修饰指针 指针常量 常量指针 指针和数组 指针和函数

    十一.指针 (1)指针的定义和使用 指针的作用:可以通过指针间接访问内存. 参考视频:https://www.bilibili.com/video/BV1et411b73Z?from=search&a ...

  2. C++:const的使用(普通常量、指针、引用)

    const声明的是常量,常量基本上只能读不能写.其实x也是能写的,但他只是在x初始化的时候完成了写操作. int main() {const int x=4;//x是const int型.在初始化时写 ...

  3. 简单介绍C++中常量与指针

    在C++学习使用过程中,每个人都不可避免地使用指针,而且都或多或少的接触过常量指针或指针常量,但是对这两个的概念还是很容易搞糊涂的,所以这篇文章主要给大家介绍了关于C++中常量与指针的相关资料,需要的 ...

  4. 常量和指针(Pointers and Constants)

    常量和指针(Pointers and Constants) --const 修饰的指针解惑 一般遇到用const修饰的常量涉及到指针就会比较麻烦,容易把头搞晕,有个简单的技巧就是从右向左看,下面我举例 ...

  5. 常量指针与指向常量的指针

    这两个概念经常很容易混淆,下面简单分析一下 1.常量指针: int * const p   : const p 就是一个常量,然后再看*,可以看出是一个常量类型的指针,即int类型的常量指针.不能修改 ...

  6. [C++] 指向常量的指针 VS 指针类型的常量

    指向常量的指针 VS 指针类型的常量 const 修饰指针时的位置不同,作用也不相同. 1. 指向常量的指针 不能通过指向常量的指针改变所指对象的值,但指针本身可以改变,可以指向另外的对象. 例: i ...

  7. C++的常量、指针、引用

    一.C语言的常量是可以通过地址进行修改的:而C++的常量是不可修改的,且在定义的时候必须初始化! 可引用常量的地址,但只是临时地址: const int a = 10 ; int *b = (int* ...

  8. c/c++ 函数、常量、指针和数组的关系梳理

    压力才有动力,15年中旬就要准备实习,学习复习学习复习学习复习学习复习--无限循环中,好记性不如烂笔头--从数组开始,为主干. c 的array由一系列的类型相同的元素构成,数组声明包括数组元素个数和 ...

  9. [C++基础]018_常量指针和指向常量的指针

    先来看一下什么是常量指针,什么是指向常量的指针吧! 1. 常量指针定义 1 int * const ptr = new int(); 2. 指向常量的指针 1 const int* ptr; 上面已经 ...

  10. 【C++ 语言】引用 ( 引用简介 | 指针常量 | 常量指针 | 常引用 | 引用参数 | 引用 指针 对比 )

    文章目录 I . 引用概念 II . 引用声明 III . 引用 地址 内存 分析 IV . 常引用 V . 引用作为参数 VI . 引用 与 指针 对比 I . 引用概念 C++ 对 C 扩充 : ...

最新文章

  1. 成为黑客之前的语言准备清单
  2. dataframe 转rdd java,在pyspark中将RDD转换为Dataframe
  3. MatConvnet工具箱文档翻译理解(1)
  4. 批处理:修改COM端口号
  5. 年末技术总结,你也参加吧!
  6. linux下svn常用命令集锦
  7. 万维网文档在服务器端动态,信息网络应用基础作业2.docx
  8. python学习------面向对象进阶
  9. Spring(Data-Rest)
  10. android原生系统手写,可自定义 自带中文手写输入法_索尼 Xperia SP_手机Android频道-中关村在线...
  11. 基于spring cloud + nacos + gateway + ssm+的学生管理系统
  12. 算法的复杂度度量--时间复杂度以及空间复杂度
  13. 微信公众号内置浏览器缓存清理
  14. WordPress付费资源素材下载主题 总裁CeoMax主题
  15. 什么是UTF-8编码
  16. iOS马甲包开发招式及规避4.3方法合集
  17. 来啊,一起来智障啊:国外大火游戏人类一败涂地究竟有多好玩?
  18. Python | 人脸识别系统 — 人脸比对 代码部分
  19. python3文件的编码类型是_Python3.x环境创建Python脚本文件时,需要将文件编码格式设置为...
  20. 三菱PLC GX Work2学习笔记

热门文章

  1. 安卓 下载pdf到手机存储
  2. Swift - iOS应用的国际化与本地化
  3. jQuery滑动效果
  4. android sdl 插件,在Eclipse中配置SDL2.0 for Android
  5. I'm up to my ears
  6. ROS2机器人坐标工具→tf2静态广播←Python
  7. 单例模式及单例类的两种实现
  8. Excel基础(02)单元格格式
  9. XSSFWorkbook 设置单元格样式_6.6 使用单元格样式
  10. framework层的event_log分析