从零开始C语言精讲篇5:指针
文章目录
- 一、指针是什么?
- 二、指针和指针类型
- 三、野指针
- 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规避野指针的方法
- 指针初始化
- 小心指针越界
- 指针指向空间释放即使置NULL
- 指针使用之前检查有效性
示例如下:
#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:指针相关推荐
- 从零开始C语言精讲篇1:初识C语言
文章目录 前言 一.什么是C语言(了解) 二.第一个C语言程序 三.数据类型 四.变量.常量 4.1变量 4.1.1定义变量的方法 4.1.2变量的分类 4.1.3变量的使用 4.1.4变量的作用域和 ...
- 宾夕法尼亚大学计算机专业研究生,宾夕法尼亚大学计算机工程研究生排名最新消息精讲篇...
原标题:宾夕法尼亚大学计算机工程研究生排名最新消息精讲篇 宾大建立之初,大学是一所慈善学院.本杰明?富兰克林作为院校设立人,认为新的知识来自对现有资源最广泛的认识和最有创新的运用.这一思想指导着他的研 ...
- 《C语言精讲第八课》——数组,超级详细且容易理解
<C语言精讲第八课>--数组,超级详细且容易理解 文章目录 <C语言精讲第八课>--数组,超级详细且容易理解 前言
- c语言国二资料,国二C语言精讲资料.doc
国二C语言精讲资料.doc 第一章C语言基础知识 该章内容:本章是整个C语言最基础的部分:表达式是C语言的核心,本章会学习到不同的表达式. 学习方法:对关键的知识点一定要多背,把最基础的习题多做几遍, ...
- 【云原生】4.3 DevOps 精讲篇——Sonar Qube
CSDN话题挑战赛第2期 参赛话题:学习笔记 目录 一.前言 二.SonarQube 安装 1.介绍 2.安装 三.Sonar Qube基本使用 1.安装中文插件 2.Maven 代码检测 3.Son ...
- c语言27除以4,2014计算机等级二级C语言精讲习题及答案 4
单项选择题 26.若有函数 Viod fun(double a[], int *n) {---} 以下叙述中正确的是 A. 调用fun函数时只有数组执行按值传送,其他实参和形参之间执行按地址传送 B. ...
- 【云原生】4.2 DevOps 精讲篇
哈喽~ 大家好呀,这篇就来详细介绍 DevOps 以及需要安装什么软件.
- 【C语言精讲】——创建数组、使用数组(一维数组、二维数组)
目录 一.一维数组的创建和初始化 1.1数组的创建 1.2数组的初始化 数组字符串的创建 strlen和sizeof的区别 1.3一维数组的使用 计算数组元素的个数 用指针访问数组(代替下标引用操作符 ...
- 赵海英《C语言精讲》,《数据结构》
考研学习记录: 1-8章已学习完成,剩下的两周完成其他章节学习. 数据结构强化学习计划安排: 在这里插入图片描述 char 字符型 占1byte 即8位,一个char型数据(例如:a.#.!之类的)用 ...
最新文章
- python多久能学会爬虫-python一般学多久
- Servlet架构初解析
- sap 预制凭证与暂存凭证的区别
- 使用SQL Server Analysis Services Tabular Model建立分析模型
- handsontable pro 授权码 key 生成器(JS函数)(仅供学习交流)
- [学习笔记] [数据分析] 01.Python入门
- 2022数维杯问题D:三重拉尼娜事件下极端气候灾害的损失评估和应对策略研究-思路分析
- 用ESP8266连接 0.96寸 OLED屏幕
- 反射Modifier
- 如何做好自媒体矩阵,0成本获取流量必备
- python 做网页_听过最近Python过气了?
- python数据组织存在维度吗_用Python 爬取蔡徐坤新浪微博 10 万转发数据,从数据的维度看看存在多少假流量...
- bde连接oracle失败,BDE联接出错,求助
- 物理/逻辑CPU、Core、Thread等概念
- 深圳-数据岗位面试不完全记录(回忆版)
- 卡西欧计算机亮度,卡西欧怎么调3秒灯
- Corn fields(玉米田)状压dp入门第一题 洛谷P1879 poj3254
- 用VSCODE看linux内核代码
- 乱七八糟的普元(GoCom)网站
- 关于城市旅游的HTML网页设计 HTML+CSS上海博物馆网站 dreamweaver作业静态HTML网页设计 html网页制作期末大作业