指针是C语言的显著的优势之一,其中使用起来是十分的灵活。而且是能够高效率的提高程序的使用,但是,如果使用使用不恰当的话程序是很容易被"挂死"的往往都是错误导致指针造成的。之所以C语言到现在还能够适应时代其中指针是不可或缺的 🎄
那再来说说指针的优缺点吧。
指针的优点→是标识一块内存。电脑内存上的每一个字节都具有一个编号,称为地址(可以简单理解为指针),任何读写内存的指令都必须携带地址信息,否则电脑不知道读写那块内存。不管程序是用什么语言写的,要运行数据和代码必须驻留内存,CPU要执行指令必须有一个"指针"程序计数器指向内存的代码块,如果某个指令要操作内存数据,该指令必须携带额外的地址信息 🌹
指针的缺点→指针可以操作任何东西,所以指针很灵活、很强大,但也引入了复杂性 🎋
🍁 前言
指针!指针!指针!重要的事情说三遍,之所以这样说是因为指针对于我们学习C语言真的是特别特别的重要。可以说会指针和不会指针那就是天壤之别 🤐
你想要成为"C语言大佬"指针就必须玩的起来,这样就是你成为大佬的第一步,相比之前的内容,指针会难上一点,但只要肯下功夫,多多打磨、多去理解、多去上手练习,迟早你就能把指针玩开了 😋
有些初始C语言的小伙伴们,可能一遇到指针就会放弃或者对指针不够重视。千万不能有这样的想法,你想学习C语言到后面的话指针是你一定要跨越的"山峰",当你跨过这段"山峰"的时候到达顶端时候,你就会感慨值了",所以加油,干就完事了 😤
指针是C语言的显著的优势之一,其中使用是十分灵活的而且能提高某些程序的效率,但是如果使用不当则很容易造成系统错误。许多程序"挂死"往往都是错误地使用指针造成的 😱
⚔ 内存
计算机当中所有的数据都是必须要放在内存当中的,不同类型的数据占用的字节数不一样。
如果当我们买回来的计算机当中有 4g 内存或者 8g 内存空间,那么我们因该如何去使用它们呢🤔
解释:内存(空间)的使用跟我们"现实"生活当中有非常相似的地方,在我们"现实"生活当中国土面积总共有960万平方公里,就像是当我们真的是去访问这些内存空间的时候,都给了它们的一个有效的地址。比如:这个时候我们想去找到一个地方,有省,其次市、县、镇、乡这样不同的规划。然后找到你人在哪里,而我们现实生活中市怎么样找到这一块空间的那就是通过地址,而这个地址又是跟我们一个个"房间"是相互关联的。我们是不是跟每一个房间都整理了编号,然后通过地址就可以找到房间⇩
那么其实对于内存也是一样的,内存是一块大的空间,如下流程图所示⇩
当我把内存划分成这样一个长方体的格子之后, 那么其实就是和生活当中的房间是一样我们现实生活当中给每一个房间都编了号。
而我们内存空间也是一样,划分着每一个格子也相应的进行编号。这就是内存空间的管理方式。
🗡 地址与指针
概述:在计算机中,所有的数据都是存放存储器内存当中。一般把存储器中的一个字节称为一个 内存 占用的内存单元数不等,如整型量占 4 个单元,字符量占 1 个单元等,在前面已有详细的介绍。为了正确地访问这些内存单元,必须为每个内存单元编上号。根据一个内存单元的编号即可准确的找到该内存单元,内存单元的编号也叫做地址。 既然根据内存单元的编号或地址就可以找到所需的内存单元,所以通常也把这个地址称为指针。 内存单元可以用一个通俗的例子来说明 它们之间的 到银行去存取款时, 银行工作人员将根据我们的帐号去找我们的存款单, 取款的金额。在这里,帐号就是存单的指针, 存款数 是存单的内容。对于一个内存单元来说,单元的地址即为指针,其中存放的数据才是该单元 的内容。 语言中,允许用一个变量来存放指针,这种变量称为指针变量。因此,一个指针变量的值就是某个内存单元的地址或称为某内存单元的指针 🤩
Ⅰ地址就是内存区中对每个字节的编号。
Ⅱ指针看作是内存中的一个地址,多数情况下,这个地址是内存中另一个变量的位置。
在程序中定义一个变量,在进行编译的时候就会给改变了在内存当中分配一个地址,通过访问这个地址可以找到所需的变量,这个变量的地址称之为该变量的 "指针"
🍊指针就是用来存储内存变量的当中的地址!①个内存单元 == ①字节 。
🍊地址就是用来通过内存区的编号找到变量,然后再把自己内存区的编号赋值给指针。
从上述的话就可以看出来,为什么也会说其实指针无非就是地址,地址无非就是指针了。
🗡 变量与指针
变量的地址是 变量 和 指针 二者之间纽带,如果一个变量包含了另一个变量的地址,则可以理解成第一个变量指向第二个变量。所谓 "指向" 就是通过地址来进行实现的。 因为指针变量是指向一个变量的地址,所以将一个变量的地址值 赋值给这个指针就 "指向" 了该变量 😜
例如:将变量 i 的地址值赋值给指针变量 p 中,p 就指向 i,其关系如下所示↓
在程序代码中是通过 变量名 对 内存 单元进行存取操作的,但是代码经过编译后已经将变量名转换为该变量在内存中存放的地址,对变量值的存取都是通过地址进行的。如下代码所示
a+b
其含义是:根据变量名与地址的对应关系,找到变量 a 的地址。假设 1000,找到变量 i 的地址 1000,然后从 1000 开始读取 4 个字节(整形类型)存放在 CPU 的寄存器当中,再找到变量 j 的地址 1004 ,从 1004 开始读取 4 个字节数据存放在 CPU 另一个寄存器当中,通过 CPU 的加减法从中计算出来 🙄
⚔ 定义指针变量
对指针变量的定义包括 ③ 个内容↓
指针类型说明,即为变量为一个指针变量。
指针变量名。
变量值(指针)所指向的变量的。
定义指针变量与定义普通变量非常类似,不过要在变量名前面加星号(*) 格式如下所示↓
datatype *name;或者 datatype *name = value;
解释:* 表示一个指针变量,datatype 表示该 指针变量所指向的数据的类型。如↓
int *p;// %p打印地址
表示 p 这是一个指针变量,变量名即为指针的变量名,类型说明符表示本指针变量所指向的变量的数据类型
p 是指向int 类型数据的指针变量,至于究竟指向哪一份数据,应该由赋予它的值决定。如下↓代码所示。
int *p1; // p1 是指向整形变量的指针 float *p2; // p2 是指向浮点变量的指针 char *p3; // p3 是指向浮点变量的指针
🔥注意:①个指针变量只能是指向同类型的变量。
1. int a = 100; 2. int *pa = &a;
(1) a在内存中要分配空间4个字节。
(2) 取出a的地址赋值给指针变量papa说明执行对象是int类型。
🔥注意→取地址a并不会拿出4个字节的地址,只会拿出第一个字节地址。
1字节 = 8比特位,按照十六(0x)进制的方式来的。
💣有效声明指针
有效指针,顾名思义就是可以有效的在程序当中运行不会出现错误的指针类型 😐
int *ip; /* 一个整型的指针 */ double *dp; /* 一个 double 型的指针 */ float *fp; /* 一个浮点型的指针 */ char *ch; /* 一个字符型的指针 */
🗡使用指针
使用指针时会频繁进行以下几个操作:定义一个指针变量、把变量地址赋值给指针、访问指针变量中可用地址的值。这些是通过使用一元运算符(*)来返回位于操作数所指定地址的变量的值。下面的实例涉及到了这些操作如下↓
#include<stdio.h> int main(void) { int a = 20; //a在内存中要分配空间4个字节 int *pa = &a; //取出a的地址赋值给指针变量pa, pa说明执行对象是int类型! printf("无改变:%d\n", *pa); *pa = 30;//进行解引用操作符 *pa 就是通过解引用(pa)里边的地址来找到地址a的值。 printf("改变的:%d\n", *pa);//解引用操作符是可以改变取地址原来的值的! return 0; }
运行结果🖊
a = 20
b = 30
知识内容→上面的结果也是通过指针变量取得数据, 然后再通过解引用(*)操作符改变取地址原来的值。指针的解引用可以获取地址赋值给指针变量从而获取数值的大小(这个是初学者有时候不明白的地方,不懂可以多看几遍或者自己上手代码进行调试)
💣指针变量初始化
指针变量初始化是非常重要的,很多初学指针的小伙伴们很容易就会把指针没有进行指针变量的初始化。
如下代码所示↓
#include<stdio.h> int main(void) { int a = 10; int *p = &a; printf("*p = %d\n",p); return 0; }
🔥注意→上述代码是错误的,在打印printf的时候,p并没有对其进行解引用操作(找不到p的数值) 此时p是找不到a的地址的,也可以说并没有指向&a。
如下代码所示↓
#include<stdio.h> int main(void) { int a = 10; int *p = &a; printf("*p = %d\n",*p); return 0; }
此时,对其中打印 printf 进行解引用操作找到 a 的地址。运行结果为:*p = 10
🔥注意→在使用指针的时候,是必须要给指针变量进行初始化的,不然就会是野指针。关于野指针是什么这个在后面会说的。
💣赋值语句的方法
如下代码所示↓
1. int a; 2. int *p; 3. p= &a;
不允许把一个数赋予指针变量,故下面的赋值是错误的。
1. int * p; 2. p = 10;
另外,指针变量和一般变量一样,存放在它们之中的值是可以改变的,也就是说可以改变它们的指向。
🗡指针变量的大小
如下代码所示↓
#include<stdio.h> int main(void) { printf("%d\n", sizeof(int *)); printf("%d\n", sizeof(long *)); printf("%d\n", sizeof(long long*)); printf("%d\n", sizeof(float *)); printf("%d\n", sizeof(double *)); printf("%d\n", sizeof(short *)); return 0; }
运行结果🖊
④(全部)
从上述结果可以看出指针变量都是④个字节。
为什么不同类型的变量的地址所占的字节数都是一样的呢。
原因是:他们数据类型都是指针类型,切记(☆-v-)
🔥注意→这个是在32位的操作系统 = 4字节,64位的操作系统上 = 8字节。
⚔ 上述总结
指针就是变量,用来存放地址的变量。(存放在指针当中的值都是会被当做是地址来处理)
还有下列②个问题如下↓
一个小的单元到底是多大(①个字节)
如何进行编址。
经过仔细的计算,一个字节(⑧比特位)对应其一个地址是比较合适的(①字节等于①地址)
指针就是用来存储地址的,地址是唯一表示一块地址空间。
指针大小在32位的平台上是④个字节,在64位上的平台是⑧个字节。
在32位平台上产生的地址线就是由 32个0&1组成的地址线,在64位平台上产生的地址线就是由64个二进制0&1组成的地址线。
💣 * 和 & 认识
假设有一个 int 类型的变量 a,pa 是指向它的指针,那么*&a和&*pa分别是什么意思呢?
*&a可以理解为*(&a),&a表示取变量 a 的地址(等价于 pa),*(&a)表示取这个地址上的数据(等价于 *pa),绕来绕去,又回到了原点,*&a仍然等价于 a
&*pa可以理解为&(*pa),*pa表示取得 pa 指向的数据(等价于 a),&(*pa)表示数据的地址(等价于 &a),所以&*pa等价于 pa
💣 对解引用(*)认识
表示乘法,例如 int a = 3, b = 5, c; c = a * b; 这是最容易理解的。
表示定义一个指针变量,以和普通变量区分开,例如int a = 100; int *p = &a;
表示获取指针指向的数据,是一种间接操作,例如 int a, b, *p = &a; *p = 100; b = *p;
表示获取指针指向的数据,是一种间接操作。这里我来举出一个代码例子↓
#include<stdio.h> int main(void) { int a = 10; int *p = &a; printf(" a = %d\n", a); printf("*p = %d\n", *p); //注意:解引用改变p,同时也会改变指向a的地址(改变a的值同样p也会跟着改变) *p = 50; printf(" a = %d\n", a); printf("*p = %d\n", *p); int b = *p; printf(" b = %d", b); return 0; }
运行结果🖊
a = 10
*p = 10
a = 50
*p = 50
b = 50这里大家可以好好思考下为什么,为什么运行结果是这样看看能不能说出来。这样你才能真正的掌握这些知识点。
🗡 " * & 的应用"
如下所示👇
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> int main(void) { int i = 0; int* p = &i; printf("请输入数字:"); scanf("%d", &i); printf("numbers1 = %d\n", *&i); printf("numbers1 = %d\n", i); printf("numbers1 = %d\n", *p); return 0; }
行结果
三个 printf() 打印的值都是一样的(同样可以思考下这个是为什么)
🗡 通过指针交换变量值
既然都看到这里了,那么我们就来尝试做一道题目吧,看看你是否掌握这些知识点了。
题目内容:用指针交换两个值。
#include <stdio.h> void swap(int *pa, int *pb) { int temp = *pa; //10 *pa = *pb; //20 *pb = temp; //10 } int main(void) { int a = 10, b = 20; int *pa = &a, *pb = &b; printf("交换前的值:a=%d, b=%d\n", a, b); swap(pa,pb); printf("交换后的值:a=%d, b=%d\n", a, b); return 0; }
运行结果🖊
交换前的值:a = 10, b = 20
交换后的值:a = 20, b = 10
对程序进行说明如下↓
swap()是用户定义的函数,它的作用是交换两个变量(a 和 b)的值。swap 函数的形参 pa、pb 是指针变量。程序运行时,先执行 main 函数,已知 a 和 b 的值。然后将 a 和 b的地址分别赋给指针变量 pa 和 pb,使 pa 指向 a,pb 指向 b
用图形表示如下如下↓