【明解C语言】之指针初阶详解
目录
一、指针是什么
二、指针和指针类型
1. 指针+-整数
2. 指针的解引用
3.练习
三、野指针
1.野指针成因
2.规避野指针的有效方法
四、指针运算
1.指针+-整数
2.指针-指针
3.指针的关系运算
五、指针和数组
1.测试通过数组下标和指针访问数组元素 的地址:
2.尝试用指针遍历数组并打印数组元素:
3.指针访问数组具体的某个元素
六、二级指针
七、指针数组
八、结语
一、指针是什么
理解指针的两个点:
1. 指针是个变量,用来存放内存单元的地址(编号)
2. 存放地址的变量叫指针变量
什么是内存?
内存是一块大的空间,把内存划分成了一个一个的内存单元,一个格子就是一个内存单元,取一个字节为基础的内存单元。
为了管理这些内存单元,又给每个内存单元进行了编号。内存单元的编号也叫内存单元的地址,通过地址可以很好的找到内存单元,也就是说,这个地址指向了该内存单元,所以地址也形象地称为指针。
什么是指针变量?
通过&(取地址操作符)将变量的地址取出,再把地址存放到另一个变量中,
这个变量,就是指针变量。
代码示例:
#include<stdio.h>int main()
{int a = 10;//在内存种开辟一块空间//将a的地址存放到怕中int* pa = &a;//拿到的是a的4个字节中第一个字节的地址*pa = 20;//通过pa访问a的值并将a的值改为20return 0;
}
一颗*说明pa是指针变量
int说明pa指向的对象a是int类型的
⚠️
注意:指针就是变量,用来存放地址的变量(存放在指针中的值,都被当成地址处理)
现在有问题:
一个小的单元到底是多大?(一个字节)
如何编址?
对于32位的机器,假设有32分地址线,那么,假设每根地址线在寻址的时候产生一个电信号,正电/负电,也就是1或者0。
那么32根地址线产生的地址就会是:
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000001
......
11111111 11111111 11111111 11111111
这里就有2的32次方个地址。
每个地址标识一个字节,那我们就可以给
(2^32Byte == 2^32/1024KB==2^32/1024/1024MB==2^32/1024/1024/1024GB == 4GB)
4G的空闲进行编址。
那64位的机器,64根地址线产生的地址会有多大空间?
可以自己尝试计算一下!
⚠️
注意:
1️⃣指针是用来存放地址的,地址是唯一标识一块地址空间的。
2️⃣指针的大小在32位平台是4个字节,在64位平台是8个字节。
二、指针和指针类型
1. 指针+-整数
#include<stdio.h>int main()
{int arr[10] = { 0 };//数组名是数组首元素的地址int * p = arr;char * pc = arr;printf("%p\n", p);//打印p的地址printf("%p\n", p + 1);//打印p+1的地址printf("%p\n", pc);//打印pc的地址printf("%p\n", pc + 1);//打印pc+1的地址return 0;
}
字符指针加1,相当于跳过了1个字符。
整形指针加1,相当于跳过了1个整型。
因为变量p和变量PC的类型不同,才会产生加4和加1的区别。
总结:指针的类型决定了指针向前或向后走一步能有多大,能走多远(距离)
2. 指针的解引用
1️⃣char类型的指针
int main()
{//a是16进制数字int a = 0x11223344;//4个字节char * pc = &a;*pc = 0;return 0;
}
2️⃣ int 类型的指针
int main()
{//a是16进制数字int a = 0x11223344;//4个字节int * pa = &a;*pa = 0;//将a的值改为0return 0;
}
总结:指针类型决定了指针解引用的权限有多大(能操作几个字节)
例如:char*的指针解引用就只能访问一个字节,而int*的指针解引用可以访问4个字节。
3.练习
int *指针的写法
#include<stdio.h>int main()
{int arr[10] = { 0 };int* p = arr;int i = 0;for (i = 0; i < 10; i++){//指针变量+1指向数组后一个元素*(p + i) = 1;//将数组所有元素改为1}return 0;
}
如果是整形的指针,一个整型的操作完之后跳过一个整形。(也就是一个整形)
char *指针的写法
#include<stdio.h>int main()
{int arr[10] = { 0 };char *p = arr;int i = 0;for (i = 0; i < 10; i++){*(p + i) = 1;}return 0;
}
如果是char类型的指针,一个字节操作完之后跳过一个字节,而且操作的时候是一个字节,一个字节初始化的
三、野指针
概念:野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)
1.野指针成因
- 指针未初始化
#include<stdio.h>int main()
{//这里的p就是一个野指针int* p;//p是一个局部的指针变量,局部变量不初始化的话,默认的是随机值//申请一块空间才可以去访问,而p并没有申请一块空间*p = 20;//非法访问内存return 0;
}
- 指针越界访问
#include<stdio.h>int main()
{int arr[10] = { 0 };int* p = arr;int i = 0;for (i = 0; i <= 10; i++){//当指针指向的范围超出了数组arr的范围时,就是越界访问,p就是野指针*(p + 1) = i;}return 0;
}
- 指针指向空间释放
#include<stdio.h>int* test()
{int a = 10;return &a;
}int main()
{int* p = test();*p = 20;return 0;
}
test()函数创建临时变量a并返回a的地址,指针变量p接收,a变量生命周期结束,变量销毁。(所谓的销毁,就是把申请的空间还给操作系统了)a变量已经销毁了,再去访问它的时候就访问不到了,也就是非法访问。
2.规避野指针的有效方法
- 指针初始化
#include<stdio.h>int main()
{//如果不知道p应该初始化为什么地址的时候,直接初始化为NULLint* p = NULL;//明确知道初始化的值int a = 10;int* ptr = &a;return 0;
}
- 小心指针越界
C语言本身是不会检查数据的越界行为的,所以我们自己要保证它不会越界。
- 指针指向空间释放及时置NULL
当一个指针指向的空间已经被释放了,那么应该及时的把指针变量置成空指针。当指针是一个空指针的时候,就不会去使用它了。
当指针指向的空间已经不属于我了,我不能再访问了,就要把一个指针变量及时的置为空指针来防止野指针的出现。
- 指针使用前检查有效性
检查指针有效性的办法:
#include<stdio.h>int main()
{int* p = NULL;//判断p是不是空指针if (p != NULL){*p = 10;}return 0;
}
四、指针运算
1.指针+-整数
#include<stdio.h>#define N_VALUES 5//定义符号int main()
{float values[N_VALUES];//值是5float* vp;//指针+-整数:指针的关系运算for (vp = &values[0]; vp < &values[N_VALUES];){//语法支持可以省略调整部分//vp<5*vp++ = 0;}return 0;
}
打印数字元素
#include<stdio.h>int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int* p = arr;//数组名是首元素的地址int* pend = arr + 9;while (p <= pend){printf("%d ", *p);p++;//指向下一个元素}
}
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[0]);return 0;
}
指针-指针的前提:
两个空间指向同一空间
#include<stdio.h>int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };char c[5];//指针和指针相减的前提是://两个指针指向同一空间printf("%d\n", &arr[9] - &c[0]);return 0;//会有警告
}
用指针-指针求字符串长度
#include<stdio.h>int my_strlen(char * arr)
{char* strat = arr;while (*arr != '\0'){arr++;}return arr - strat;
}
int main()
{char arr[] = "hehe";my_strlen(arr);printf("%d\n", my_strlen(arr));return 0;
}
3.指针的关系运算
#include<stdio.h>#define N_VALUES 5int main()
{float values[N_VALUES];float* vp;for (vp = &values[N_VALUES]; vp > &values[0];){*--vp = 0;}return 0;
}
简化写法:
#include<stdio.h>#define N_VALUES 5int main()
{float values[N_VALUES];float* vp;for (vp = &values[N_VALUES]; vp >= &values[0]; vp--){*vp = 0;}return 0;
}
标准规定:允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针相比较,但是不允许与指向第一个元素之前的那个内存位置的指针相比较。
⚠️
注意:
简化写法实际上,在绝大部分的编译器上是可以顺利完成任务的,然而,我们还是应该避免这样写,因为标准并不保证它可行。
五、指针和数组
- 一个简单的例子说明数组名是数组首元素的地址
1.测试通过数组下标和指针访问数组元素 的地址:
#include<stdio.h>int main()
{int arr[10] = { 0 };int* p = arr;int i = 0;for (i = 0; i < 10; i++){printf("%p <==> %p\n", &arr[i], p + i);}return 0;
}
可以看到通过数组下标访问的地址和指针访问的地址打印出来的是一样的
2.尝试用指针遍历数组并打印数组元素:
#include<stdio.h>int main()
{int arr[10] = { 0 };int* p = arr;int i = 0;for (i = 0; i < 10; i++){//将指针指向的下标元素置为i*(p + i) = i;}for (i = 0; i < 10; i++){//打印数组元素printf("%d ", *(p + i));}return 0;
}
3.指针访问数组具体的某个元素
#include<stdio.h>int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int* p = arr;//数组名printf("%d\n", arr[2]);printf("%d\n", p[2]);//p[2]-->*(p+2)//[] 是一个操作符 2和arr是两个操作数//a+b//b+aprintf("%d\n", 2[arr]);printf("%d\n", arr[2]);//arr[2]-->*(arr+2)-->*(2+arr)-->2[arr]//arr[2]<==>*(arr+2)<==>*(p+2)<==>*(2+p)<==>*(2+arr)==2[arr]//2[arr]<==>*(2+arr)return 0;
}
六、二级指针
指针变量也是变量,是变量就有地址。
那指针变量的地址存放到哪里? 这就是二级指针
#include<stdio.h>int main()
{int a = 10;int* pa = &a;//pa是指针变量,一级指针//后一颗*说明ppa是指针//前一颗*说明ppa指向的对象是pa,而pa的整体叫int*//ppa就是一个二级指针变量int** ppa = &pa;//pa也是变量,&pa取出pa在内存中的起始地址return 0;
}
a的地址存放在pa中,pa的地址存放在ppa中。
pa是一级指针,而ppa二级指针。
七、指针数组
指针数组是指针还是数组?
答案是数组,是存放指针的数组。
#include<stdio.h>int main()
{int arr[10];//整形数组 - 存放整形的数组就是整形数组char ch[5];//字符数组 - 存放的是字符//指针数组 - 存放指针的数组int* arr[10];//整形指针数组char* ch[10];//字符指针指针数组return 0;
}
八、结语
肝了3天终于是完成了✌️ ✌️ ✌️
这次总结了初阶指针的内容,大家可以在评论区给我的文章提提意见,好坏都可以。有什么不懂得问题也可以在评论区问我,只要是我会的都会依次解答。这次的文章涉及的数组,如果感兴趣的话大家可以去我的主页浏览,绝对不会让你们失望的。本篇文章还是比较长的,感谢大家可以看到这里,愿大家都能拿到想要的offer。
最后我脸皮稍微厚一下下,希望大家可以给我的文章点赞并收藏我,小小的一个赞,对我是很有帮助的。原创不易,还请三连,期待下次与各位在知识的海洋里一起畅游。
【明解C语言】之指针初阶详解相关推荐
- c++之模板初阶详解!
c++之模板初阶详解 文章目录 c++之模板初阶详解 泛型编程 函数模板 函数模板概念 函数模板格式 模板的原理 函数模板的实例化 模板实例化的个数 对于同不同类型的传参! 如何处理这个问题呢? 关于 ...
- c语言智力题 操作符详解例题 数据存储 指针初阶 水仙花数 杨辉三角 逆序字符串 喝汽水问题 打印图形 猜凶手 使用指针打印数组内容 调整奇数偶数顺序 运动员猜名次
[题目名称] 下面代码的结果是:a #include <stdio.h> int i; int main() {i--; //sizeof'的返回值是无符号整型if (i > siz ...
- c语言二级指针内存释放,详解C语言-二级指针三种内存模型
二级指针相对于一级指针,显得更难,难在于指针和数组的混合,定义不同类型的二级指针,在使用的时候有着很大的区别 第一种内存模型char *arr[] 若有如下定义 char *arr[] = {&quo ...
- R语言机器学习之caret包详解(一)
R语言机器学习caret包trainControl函数详解 R语言机器学习之caret包详解(一) 简介 数据预处理 各种数据变换 近零方差变量 创建虚拟变量 重抽样技术 k折交叉验证 留一交叉验证 ...
- c语言二级指针有什么作用,C语言中二级指针的实例详解
C语言中二级指针的实例详解 C语言中二级指针的实例详解 用图说明 示例代码: #include int main(int argc, const char * argv[]) { // int a = ...
- 【C语言】小妹不懂指针和数组的关系?那就安排指针数组关系详解
目录 前言 一.什么是数组 二.什么是指针 三.指针变量的大小 四.数组和指针的关系 五.指针变量的自增自减运算 六.两个参数确定一个数组 七.字符型指针和字符型数组 总结 写在最后 前言 前段时间整 ...
- C语言(函数指针数组)详解
要了解函数指针数组,可以从三个角度来分析.所谓函数指针数组,从字面意思上来解析,函数指针数组的组成有三个点,函数,指针,数组.首先我们知道,函数指针数组,是一个数组,数组的每个元素是函数指针,也就是一 ...
- c语言线性表库函数大全,数据结构(C语言版)-线性表习题详解
<数据结构(C语言版)-线性表习题详解>由会员分享,可在线阅读,更多相关<数据结构(C语言版)-线性表习题详解(23页珍藏版)>请在人人文库网上搜索. 1.数 据 结 构 ,线 ...
- c语言 read 文件字节没超过数组大小时会怎样_剑指信奥 | C 语言之信奥试题详解(四)...
趣乐博思剑指信奥系列 ❝ 趣乐博思剑指信奥系列,专门针对全国青少年信息学奥林匹克联赛 NOIP 而开展的专业教育方案.开设的课程有 C 语言基础,C++ 语言基础,算法设计入门与进阶,经典试题分析与详 ...
最新文章
- 【超实用】Angular如何修改当前页面网页浏览器url后面?param1=xxxparam2=xxx参数(多用于通过浏览器地址参数保存用户当前操作状态的需求),实现监听url路由切换、状态变化。
- QQ超时不能刷新好友接收发送信息
- 开源云平台 Nano v0.9.1发布 - 镜像重构/资源绑定/日志
- 删除苹果自带软件后果_删除 iPhone 自带的软件会有什么影响?
- 链表的基本操作 java_Java-实现链表的基本操作
- linux驱动编写(虚拟字符设备编写)
- 〖Python 数据库开发实战 - MySQL篇㉞〗- 综合案例 - 新闻管理系统数据库设计的基本属性
- php书店网站模板源代码_网上书店整套html模板
- 24V600mA限流电路的Pspice仿真实例
- 模拟电路与数字电路基础知识点总结
- JS中国标准时间格式转换
- 支持向量机:Duality
- 孟婆汤传说!!!感人至极!
- leetcode-1833. 雪糕的最大数量(排序+贪心)
- 任务间同步 | 信号量、互斥量和事件集
- 2020牛客暑期多校训练营(第八场)I-Interesting Computer Game(并查集 + 思维)
- DB2 for fetch only VS with ur
- Spring Boot 之 Spring Data JPA(一)
- Java菜单(菜单条、菜单和菜单项)
- android用shape画一条横线