文章目录

  • 一、指针是什么?
  • 二、指针和指针类型
  • 三、野指针
    • 3.1野指针成因
    • 3.2规避野指针的方法
  • 四、指针运算
    • 4.1指针+-整数
    • 4.2指针-指针
  • 五、指针和数组
  • 六、二级指针
  • 七、指针数组
  • 总结

一、指针是什么?

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

我们的内存是类型下图这样的空间:

我们在使用的过程中,把内存划分成一个个小的字节,每个字节称为一个内存单元。我们给每个内存单元进行编号,每个编号唯一对应一个内存单元。类似身份证,每人一个身份证,我有这个身份证就可以唯一确定你这个人。这里也是类似的,地址指向了一个确定的内存空间,所以地址也被形象的被称为指针。

举个例子:

int main()
{int a = 10;int* pa = &a;return 0;
}


int* pa=&a,也就是pa里面放的a的地址(指针),pa是(整形)指针变量,它是用来存放指针的

二、指针和指针类型

int main()
{int a = 0x11223344;//0x表示16进制//一位16进制对应四位2进制:2*2*2*2=16//我们这里a一共8位16进制,也就是正好把32位2进制占满了(8*4=32)int* pa = &a;*pa = 0;return 0;
}

我们按f10进行调试,监视一下,可以看到我们pa里面存放的就是a的地址

监视一下内存,可以看到,我们内存中经过代码pa = 0; 32位是全部变成了0
ps:下图中我们是16进制一共8位,对应2进制32位

经过
pa = 0;赋值后

那么问题来了,我们之前说过,32位下指针大小都是4字节,64位下指针大小都是8字节。
假如我们都在32位下,我能否用char*来接收这个int型的地址(指针)呢?

我们仅把上面代码中的int * 改成 char * 来测试一下

可以看到放进去是没有问题的,但是如果要对内容修改呢?

经过*pa = 0;赋值后

为什么我这里只改动了一个字节呢?因为我是char * 的指针,我只负责我指向地址开始往后的1个字节。同理,如果我是int * 的指针,我只负责我指向地址开始往后的4个字节。

指针类型的意义1:指针类型决定了指针解引用操作的时候,一次访问几个字节(访问内存的大小)

由于这个性质,我们来看看下面这个题目

int main()
{int a = 10;int* pa = &a;char* pa2 = &a;printf("%p\n", pa);printf("%p\n", pa2);printf("%p\n", pa+1);printf("%p\n", pa2+1);return 0;
}


可以看到,我们的pa虽然和pa2都是一个地址,但是当他们加1的时候,就受到了指针类型的管控。char * 指针+1只会跳1个字节,而int * 的指针+1则会跳4个字节。

指针类型的意义2:指针类型决定了指针±整数时的步长(跳过几个字节)

三、野指针

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

3.1野指针成因

1. 指针未初始化

int main()
{int* p;//没有初始化*p = 20;return 0;
}


如果你没有初始化p,那么指针p里面放的是随机的地址,你*p=20是想把随机地址里面的值改成20。但是我们这个随机地址是不属于我们当前程序的,就会造成非法访问,p就是野指针。

2. 指针越界访问

int main()
{int arr[10] = 0;int i = 0;int*p = arr;for (int i = 0;i <= 10;i++){//数组元素下标是0-9,你如果访问了10,这个是不属于我们的空间//这就是越界访问*p = i;p++;}return 0;
}


3. 指针指向的空间释放
这个知识点笔者会在后面的动态内存释放进行详细讲解,这里简单提一下

int* test()//指针作为函数返回值
{int a = 10;return &a;
}
int main()
{int* p = test();printf("%d",*p);return 0;
}

我们在main函数里调用test函数,test函数返回a的地址。
需要注意的是,一旦返回了a的地址,test函数结束,a的生命周期也结束了
也就是说,a所在的空间返还给了操作系统。那么你在main函数中通过p再访问a之前的地址,这就是非法访问了。

打个比方:你和你好朋友出去旅游,然后你们开了一间房。在使用期内,你们想在房间里干啥都行。但是房间到期后,你再访问,旅店要告你的。

3.2规避野指针的方法

  1. 指针初始化
  2. 小心指针越界
  3. 指针指向空间释放即使置NULL
  4. 指针使用之前检查有效性

示例如下:

#include <stdio.h>
int main()
{int *p = NULL;//不知该该指向哪里,可以先初始化为NULL//....int a = 10;p = &a;//明确地初始化if(p != NULL){*p = 20;}return 0;
}

四、指针运算

4.1指针±整数

#define N_VALUES 5
float values[N_VALUES];
float *vp;
//指针+-整数;指针的关系运算
for (vp = &values[0]; vp < &values[N_VALUES];)
{*vp++ = 0;
}

上面这段代码啥意思,请看下图的解释

我们在for循环中判断vp指向的地址是否小于&values[N_VALUES]
ps:地址本质是一个16进制数,可以用来比较

发现vp指向的地址小于&values[N_VALUES],我们就进行 *vp++,
需要注意的是,++的优先级是高于 * 的,所以我们本应先进行++再 *,
但我们这里是后置++也就是说vp在本轮还是指向数组0下标元素,然后 * 解引用赋值为0,

vp因为++了,所以指向了数组1下标元素

然后重复for循环,后面的都一样了,就是把数组元素赋为0,然后vp++直到vp指向3停止
(vp < &values[4])

4.2指针-指针

#include<stdio.h>
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };printf("%d\n", &arr[9] - &arr[2]);printf("%d\n", &arr[2] - &arr[9]);return 0;
}

指针-指针,得到的数字的绝对值,是指针之间元素的个数

注:肯定有一些杠精会问:“啊,你这个是两个相同类型的指针啊,如一个char类型指针-int类型呢?”

int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };char brr[10] = { 0 };printf("%d\n", &arr[9] - &brr[2]);return 0;
}

像上面这种代码,你自己想想有多亏贼?
首先你两个地址都是随机的,你怎么知道两个随机地址之间隔了多远?
还有一个问题就是,你两个不同类型指针,到时候划分元素个数是按1字节来化还是4字节来化?

指针-指针的前提:两个指针指向同一空间(比如指向同一数组)

具体应用实例:求字符串长度
法一:使用库函数strlen

//求字符串长度
//法一:使用库函数strlen
#include<stdio.h>
#include<string.h>
int main()
{char arr[] = "abcdef";int len = strlen(arr);printf("%d\n", len);return 0;
}

法二:使用指针

int my_strlen(char* p)
{int count = 0;while (*p != '\0')//字符串末尾都是默认有一个\0{count++;p++;}return count;
}
int main()
{char arr[] = "abcdef";int len = my_strlen(arr);printf("%d\n", len);return 0;
}

法三:使用指针-指针

int my_strlen(char* p)
{char* tmp = p;//标记arr[0]的位置while (*p != '\0')//字符串末尾都是默认有一个\0{p++;}//走完while,p指向arr的末尾元素int count = p - tmp;return count;
}
int main()
{char arr[] = "abcdef";int len = my_strlen(arr);printf("%d\n", len);return 0;
}

五、指针和数组

区别:
数组是一块连续的空间,里面放的是相同类型的元素。
数组大小和元素类型,元素个数有关系,比如int arr[10],该数组大小=4*10

指针是一个变量,里面存放地址
指针变量的大小是4/8byte,这个取决于是32位系统还是64位

联系:
数组每个内存单元都有自己的内存地址,而我们指针可以存放这些地址

重点!!!:数组名是什么?

#include <stdio.h>
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,0 };printf("%p\n", arr);printf("%p\n", &arr[0]);return 0;
}


可见数组名和数组首元素的地址是一样的
这里的一样,不仅仅是数值上,是各种意义上!
ps:数组名确实是首元素地址,但是有两个例外
1.sizeof(数组名),这里的数组名表示的是整个数组
2.&数组名,这里的数组名也是整个数组,&数组名表示整个数组的地址

#include <stdio.h>
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,0 };printf("%p\n", arr);printf("%p\n", &arr[0]);printf("%p\n", &arr);printf("---------我是分界线----------\n");printf("%p\n", arr+1);//地址+4,跳过1个int型printf("%p\n", &arr[0]+1);//地址+4,跳过1个int型printf("%p\n", &arr+1);//地址+40,跳过一个数组int arr[40]return 0;
}

既然可以把数组名当成地址存放到一个指针中,我们使用指针来访问一个数组元素就成为可能。


#include <stdio.h>
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,0 };int* p = arr;int i = 0;int sz = sizeof(arr) / sizeof(arr[0]);for (i = 0;i < sz;i++){*(p + i) = i;printf("%d ", *(p + i));}return 0;
}

六、二级指针

指针变量也是变量,是变量就有地址,那指针变量的地址存放在哪里? 这就是 二级指针 。

int main()
{int a = 10;int* pa = &a;//pa里面放的是a的地址int** ppa = &pa;//ppa里面放的是pa的地址return 0;
}

示意图如下:

对于二级指针,我们也是可以解引用的
*ppa 通过对ppa中的地址进行解引用,这样找到的是 pa , *ppa 其实访问的就是 pa

int b = 20;
*ppa = &b;//等价于 pa = &b;

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

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

当然了,你也可以无限套娃,就会有三级指针、四级指针。。。

七、指针数组

指针数组是指针还是数组?
这个问题你就想,好男孩,好男孩的本质是男孩啊
指针数组本质还是数组

举个例子:

int main()
{int arr1[5];//整形数组,存放整形的数组就是整形数组char arr2[3];//字符数组,存放字符的数组就是字符数组//指针数组,存放指针的数组就是指针数组int* parr[5];//整形指针数组char* pbrr[4];//字符指针数组return 0;
}

示意图如下

应用实例:

int main()
{int a = 123;int b = 213;int c = 312;int* arr[3] = { &a,&b,&c };for (int i = 0;i < 3;i++){printf("%d\n", *arr[i]);//arr[i]是一个元素地址,*arr[i]对该地址解引用,得到地址指向的元素}return 0;
}


总结

本文介绍了指针和指针类型(着重掌握)、野指针及其成因、指针运算、指针数组等相关知识。作为C语言的大头,指针这块知识必须要拿下,最后祝读者学业有成,奥利给!

从零开始C语言精讲篇5:指针相关推荐

  1. 从零开始C语言精讲篇1:初识C语言

    文章目录 前言 一.什么是C语言(了解) 二.第一个C语言程序 三.数据类型 四.变量.常量 4.1变量 4.1.1定义变量的方法 4.1.2变量的分类 4.1.3变量的使用 4.1.4变量的作用域和 ...

  2. 宾夕法尼亚大学计算机专业研究生,宾夕法尼亚大学计算机工程研究生排名最新消息精讲篇...

    原标题:宾夕法尼亚大学计算机工程研究生排名最新消息精讲篇 宾大建立之初,大学是一所慈善学院.本杰明?富兰克林作为院校设立人,认为新的知识来自对现有资源最广泛的认识和最有创新的运用.这一思想指导着他的研 ...

  3. 《C语言精讲第八课》——数组,超级详细且容易理解

    <C语言精讲第八课>--数组,超级详细且容易理解 文章目录 <C语言精讲第八课>--数组,超级详细且容易理解 前言

  4. c语言国二资料,国二C语言精讲资料.doc

    国二C语言精讲资料.doc 第一章C语言基础知识 该章内容:本章是整个C语言最基础的部分:表达式是C语言的核心,本章会学习到不同的表达式. 学习方法:对关键的知识点一定要多背,把最基础的习题多做几遍, ...

  5. 【云原生】4.3 DevOps 精讲篇——Sonar Qube

    CSDN话题挑战赛第2期 参赛话题:学习笔记 目录 一.前言 二.SonarQube 安装 1.介绍 2.安装 三.Sonar Qube基本使用 1.安装中文插件 2.Maven 代码检测 3.Son ...

  6. c语言27除以4,2014计算机等级二级C语言精讲习题及答案 4

    单项选择题 26.若有函数 Viod fun(double a[], int *n) {---} 以下叙述中正确的是 A. 调用fun函数时只有数组执行按值传送,其他实参和形参之间执行按地址传送 B. ...

  7. 【云原生】4.2 DevOps 精讲篇

    哈喽~ 大家好呀,这篇就来详细介绍 DevOps 以及需要安装什么软件.

  8. 【C语言精讲】——创建数组、使用数组(一维数组、二维数组)

    目录 一.一维数组的创建和初始化 1.1数组的创建 1.2数组的初始化 数组字符串的创建 strlen和sizeof的区别 1.3一维数组的使用 计算数组元素的个数 用指针访问数组(代替下标引用操作符 ...

  9. 赵海英《C语言精讲》,《数据结构》

    考研学习记录: 1-8章已学习完成,剩下的两周完成其他章节学习. 数据结构强化学习计划安排: 在这里插入图片描述 char 字符型 占1byte 即8位,一个char型数据(例如:a.#.!之类的)用 ...

最新文章

  1. python多久能学会爬虫-python一般学多久
  2. Servlet架构初解析
  3. sap 预制凭证与暂存凭证的区别
  4. 使用SQL Server Analysis Services Tabular Model建立分析模型
  5. handsontable pro 授权码 key 生成器(JS函数)(仅供学习交流)
  6. [学习笔记] [数据分析] 01.Python入门
  7. 2022数维杯问题D:三重拉尼娜事件下极端气候灾害的损失评估和应对策略研究-思路分析
  8. 用ESP8266连接 0.96寸 OLED屏幕
  9. 反射Modifier
  10. 如何做好自媒体矩阵,0成本获取流量必备
  11. python 做网页_听过最近Python过气了?
  12. python数据组织存在维度吗_用Python 爬取蔡徐坤新浪微博 10 万转发数据,从数据的维度看看存在多少假流量...
  13. bde连接oracle失败,BDE联接出错,求助
  14. 物理/逻辑CPU、Core、Thread等概念
  15. 深圳-数据岗位面试不完全记录(回忆版)
  16. 卡西欧计算机亮度,卡西欧怎么调3秒灯
  17. Corn fields(玉米田)状压dp入门第一题 洛谷P1879 poj3254
  18. 用VSCODE看linux内核代码
  19. 乱七八糟的普元(GoCom)网站
  20. 关于城市旅游的HTML网页设计 HTML+CSS上海博物馆网站 dreamweaver作业静态HTML网页设计 html网页制作期末大作业

热门文章

  1. 圆孔衍射及夫琅禾费衍射Matlab仿真
  2. 基于java+SpringBoot+vue+B/S的酒店管理系统设计与实现【源码+论文+演示视频+包运行成功】
  3. mysql5.7 终端无法输入中文数据库不能输入中文的解决方法
  4. 2012诺亚方舟中国号船票正式销售!
  5. Navicat 图表 控件 多选
  6. Spring实战源码使用IDEA编译启动
  7. 菜々香@おひるね中 汉化补丁
  8. 对《货币战争》一书的小小结
  9. Linux 中的zip命令使用
  10. Maven使用教程(IDEA版)