今天萌新博主打算写几篇关于C语言就指针方面的学习文案,因为萌新博主也是一位在校大学生,专业课也涉及到C语言,在学习C语言——指针的时候,相信和大家一样,在这块知识面感觉晦涩难懂,很抽象,怎么学也学不会,学不好。但是博主并没有放弃,指针是C语言的灵魂,热心的萌新博主想帮助大家,前车之覆,后车之鉴,我们一起来学习指针。有不足的地方,还请大家多多批评指正!若写的好了,大家多多支持一下。谢谢啦~
—————————废话不多说!我们开始吧,敲黑板啦!——————————
【1.什么是变量】
在介绍指针之前,我们先必须知道什么是变量,就这一点萌新博主认为在理解C语言指针非常重要!
那么变量是什么呢?
从字面上理解,就是计算机中存储的数据,并且这个数据是可以变化的。
那么计算机是如何存储变量的呢?
电脑使用内存来记忆计算时所使用的数据
换句话说
对即将处理的数据,要把它存储在内存中
那么内存是如何存储数据的呢?
(哈哈哈,当然数据不是直接就存放在内存中的呀!)
●存储数据之前,首先我们必须申请内存空间,就好比我们去住酒店开房间一样,只有当我们开好房间后,我们才能入住,对不对!换句话说,内存也是一样的,内存就像旅馆,数据各式各样,要先根据数据的需求(即类型)为它申请一块合适的空间。开房间也是一样的,一个人住,单人间就够了;两个人住,双人间就可以了。
同样数据在内存中占用的字节大小也不一样 ,所以申请的空间也就不一样。
那一次要申请多大的内存空间呢?
当我们int num1 和 int num2
的时候,实际上这个时候我们就已经在给这两个整数类型的变量num1 ,num2开始申请空间了。
我们是根据数据类型来决定内存空间的大小的
short int——短整数类型——占用2个字节空间
int————整数类型——占用2个字节空间
long int————长整数类型——占用4个字节空间
long long int——超长整数类型——占用4个字节空间
同时,我们可以使用sizeof()函数来输出不同数据类型的字节长度,在不同的环境下,相同数据占用的字节长度可能不同。
int main(void)
{
printf("sizeof(int)=%d\n",sizeof(int));//占4个字节空间
printf("sizeof(float)=%d\n",sizeof(float));//占4字节空间
printf("sizeof(double)=%d\n",sizeof(double));//占8字节空间
printf("sizeof(char)=%d\n",sizeof(char));//占1字节空间
printf("sizeof(long int)=%d\n",sizeof(long int));//占8字节空间
}
所以,根据数据类型,决定空间大小
那么,由谁去申请内存空间呢?
实际上当我们在写声明变量的时候,就已经在申请内存空间了,声明变量后,程序执行时由系统去自动分配内存空间,根据数据类型的字节长度去分配合适大小的内存空间,这个过程是由编译器完成的。这就好比有些软件占用的内存空间小,而有些软件占用的内存的大,占用内存空间大的程序运行时调用的变量就多,而内存小的,调用的变量就少,一个道理。
那么已经将数据存入内存,如何找到它呢?
我们先要知道数据存入内存中,不是为了玩的,而是要让CPU去调用,去运算的。
不同的数据存入内存中有不同的地址空间,一块地址空间只能存放一个数据,而这个地址空间,就是变量!!
换句话说,每申请出的一块空间,就是一个变量,变量就是一块地址空间。
还是和开房间一样,当我们退房的时候,下一次的顾客依然可以入住上一次已经退过房的房间,而我们常说的变量,就是一块可以存放数据的内存空间。(形象吧!)
那我们如何找到这个地址空间呢?
张三住401,李四住307,我们在找张三的时候,只需要知道401这个房间号就可以了。
内存空间也是一样,也有它的房间号。内存空间由32位二进制数表示,常用标记方法是:0xFFFFFFFF,是由0x八段十六进制整数表示的。
内存地址 —————存储值
0x0001ab2e—————4
0x0001ab2a—————5
这就是内存空间的房间号了,当我们在找存储在这个空间里面的数据(存储值)的时候,用这个地址就可以找到了。但是博主相信没有哪个小伙伴会这么记忆内存地址的吧,这么一长串的数字,不过不排除记忆力好的小伙伴。
那么内存地址不好记,怎么办?
计算机可以通过内存地址找到它所存储的值,变量名就是为了方便人的记忆,给内存空间起的别名。
给内存地址起的名字,叫做变量名;
对于直接输出变量值,我们无需知道变量具体存储在哪块内存空间,只需要知道变量名即可;
int num1=1;
int num2=2;
int num3=3;
内存地址——【别名】———存储值
0x0001ab10【num1】——1
0x0001ab14【num2】——2
0x0000ab18【num3】——3
那我们知道了内存地址,如何去操作它?
这个时候,大家期待的指针就来了
注意:前方高能预警!!!
我们使用指针去保存并操作变量的地址(注意是保存和操作内存空间的地址),一个变量就是一块地址空间哦!
那我们为什么要用指针?原因如下:
●提高程序执行效率
●实现函数与指针的双向通信
●利用指针实现动态内存分配
●利用指针操作内存
我们通过指针去访问变量(内存空间)这个过程,叫做间接访问;
通过访问变量a,b的地址,然后找到变量a,b的值的过程,就是间接访问。
我们先声明两个指针变量
int *p1=&a;//指针p1保存a的地址
int *p2=&b;//指针p2保存b的地址
*p1告诉我变量a的地址,然后就可以找到变量a
*p2告诉我变量b的地址,然后就可以找到变量b
地址———内存内容——变量名
0x0001————1————a
0x0002————2————b
那么使用&地址符也能取出变量地址,干嘛还需要指针呢?
因为在程序运行中,变量的地址是变化的!不是静态的。
【2.什么是指针?】
指针:是一种数据类型,用于保存其他变量的地址。
指针又分为:整数指针,浮点数指针,字符指针,函数指针,数组指针,指针的指针。
指针变量保存其他变量的地址,通过对变量地址的操作来间接的操作变量,实现更多的功能。
声明的变量是指针类型,这个变量就是指针变量,指针变量保存其它变量的地址,来间接访问变量。(切记!)
【如何定义一个指针变量】
*数据类型名 指针变量名;
(指针指向的数据类型,*必须存在,且最好靠近指针变量名)
指针类型————说明
int *——————指向整数类型的指针
float *——————指向浮点数类型的指针
char *——————指向字符类型的指针
来个例子:
先声明int num1=…
我们有一个变量,叫num1,它是一个整数类型
我们将要声明一个指针p,去保存num1的地址(叫p指向num1),所以我们声明的指针为int *p;
【与指针相关的操作符】
指针声明好后,我们就要给它赋值了,但是我们不能随便赋值。注意,指针保存的是地址,这个地址是以32位二进制整数的形式表示的,当我们赋一个随便的整数,那么指针就指向了一块未知的地址,问题来了,在这个连你都不知道的地址里有你想要的数据吗?答案当然是不!
【那么我们如何给指针赋值呢?】
**
1.先得到地址空间的地址(即取地址)
2.然后把这个地址交给指针来保存(指针就是一种用来保存地址的数据类型,上面已经讲过)**
给指针赋值:p=&a;//&a叫取a的地址
记作:指针p指向变量a,实际上就是a把它的地址赋给指针p
既然我们已经得到了地址,就可以取在地址里的数据了,但是问题又来了
怎么取这个地址里的数据(内容)呢?——萌新博主真的是煞费苦心啊!
前方高能,注意!
我们先声明一个指针:int p;这时有小伙伴就要用指针p去接收变量a的地址,于是写出p=&a;这是错误的。为什么?我们在操作指针p的时候是不能带星号的,应该写成p=&a,这叫用指针p去接收变量a的地址。我们在声明完int *p后,以后出现的星号就代表取地址里的值了(这一点大家要好好理解)。
p,表示保存变量的地址
*p,表示取出已保存地址中的值,即取出变量a的值,如a=3,取出3。
注意!
声明一定要带星号
赋值不能带星号
int *p;//声明指针
p=&a;//p保存了变量a的地址
*p;//在a的地址中取值,得到变量a的值
来个例子吧!
#include
int main(void)
{
int a=3;//声明变量a,把数值3赋给a
int *p=NULL;//声明指针p,指针初始化赋空值
p=&a;//让指针p保存a的地址
printf{"var a add=%p\n",&a};//输出变量a的地址
printf{"p=%p\n",p};//输出指针P的值,也就是保存a的地址
printf{"date a=%d\n",a};//输出a的值
printf{"*p=%d\n",*p};//对p取值
return 0;
}
运行结果:
var a add=0x7fff2c4dc274
p=0x7fff2c4dc274
date a=3
*p=3
——小伙伴们这下应该能够理解吧!
【指针变量关联的四个属性】
如果有指针变量int *p1=&a;
●指针变量的地址——指针本身的内存地址
●指针变量的值——a的地址,取&a的值
●指针指向的内容——变量a的值
●指针指向的地址——a的地址,即&a的值
所以【指针变量的值】与【指针指向的地址】是两个相同的概念,小伙伴们切记!
【什么叫指向?当指针保存了某个变量的地址,就叫做指向】
【指针的值和变量的地址之间的联系】
#include
int main(void)
{
int num1=3;
int num2=2;
int *p=NULL;
P=&num1;
printf("p date=%p\n",p);
p=&num2;
printf("p date2=%p\n",p);
printf("address num1=%p\n",&num1);
printf("address num2=%p\n",&num2);
return 0;
}
结果:
p date=0x7fff7670e214
p date2=0x7fff7670e210
address num1=0x7fff7670e214
address num2=0x7fff7670e210
我们可以看出,我们可以通过指针去保存某个变量的值,但是我们不能去更改变量的地址。好比我们只能记录张三退房后又搬到哪里去了,但是我们不能去更改张三住过的房间,房间是固定的,同样地址也是固定的。
【指针变量的赋值】
●指针变量初始化必须赋值,而且只能赋值已经声明后的变量地址(先有变量,再有指针);
●给指针变量直接就赋值一个整数,这是十分危险的;
●对指针变量赋值,实际就是让指针指向一个新的内存空间;
再举个例子
int a-0;
int *p1=&a;
int *p2=23;
如果我们直接用int *p=23
声明一个整数,这么做,会将指针直接指向23号内存空间,即0x00000017,你能保证这个地址里一定有正确的值吗?
记住
【int *p1=值 ;
——声明指针p1的时候给p1赋值】
与
【int *p1;*p1=值;
——给p1指向的内存空间赋值】
意义不一样!
【指针变量的字节长度与所指向的内容无关】
指针既然是变量,也会有它的字节长度,但是指针变量的字节长度与所指向的内容无关。
在32位的操作系统中,指针变量的字节长度是4。
在64位的操作系统中,指针变量的字节长度是8。
int main()
{
printf("sizeof(int *)=%d\n",sizeof(int *));//8个字节
printf("sizeof(float *)=%d\n",sizeof(float *));//8个字节
printf("sizeof(double *)=%d\n",sizeof(double *));//8个字节
printf("sizeof(char *)=%d\n",sizeof(char *));//8个字节
printf("sizeof(long int *)=%d\n",sizeof(long int *));//8个字节
return 0;
}
【比较讨厌的星号,因为它有双重意义】
*在声明的时候和在取值的时候,意义不同:
●在声明指针的时候,加星号只是表示:为声明的是一个指针;
●在取值的时候,加星号表示:我要从该地址取值出来;
【最后注意】
1.指针的类型和它所指向的变量类型要匹配
char c='e';
int *p=&c;
//指针的类型为int型,但是指向变量的类型是char型
2.声明指针的时候,同时给指针赋值,容易受到惯性思维的影响,所以别一开始就给指针一个整数常量
int *p=0xfff20321;
推荐
int *p=NULL; //声明指针的时候先给指针p赋空值
p=&a;//再让指针保存其它变量的地址
3.不经过初始化的指针,是不安全的,一般可以给它赋上空值。
int a=0;
int *p;
*p=&a;
●声明的时候,* 的作用只是为了区别普通变量与指针
●操作的时侯,p代表的是取变量(内存地址)的值,所以声明以后再用 p就只能表示:把&a(a的地址)中的值赋给 p,**即a地址中存储的值被p取出。
【指向const的指针变量】
int const *p;
或者是const int *p
该指针p指向了一个const变量,即指向了一个只读变量,此时不能改变变量的值
const int *p=&a;
*p=1;//错误操作,此时不能改变指针所指变量的值
【指针变量本身就是const型的】
int * const p;
int a=12;
p=&a;
这个时候,该指针只能指向一个地址,不能再指向其他地址。
【3.指针的运算规则】
1.两个指针永远不能做加法运算;
●张三住301房间,李四住407房间,我们把301和407加起来,是没有意义的。
2.同类型的指针相减:两个指针所指的两个地址之间有多少个该类型的变量
●通过407-301,我们就能得出在407-301之间有多少房间了,对不对!
3.不同类型的指针不能相减;
4.指针加n
表示指针在原来的值+n*sizeof(指针所指变量类型)
5.指针减n
表示指针在原来的值-n*sizeof(指针所指变量类型)
int *p=NULL;
p=&a;
&a(地址)=0x00001000
p(保存的地址)=0x00001000
p+1=0x00001000+sizeof(int)
p+2=0x00001000+2×sizeof(int)
p+3=0x00001000+3×sizeof(int)
6.只有相同类型的指针,才能相互赋值,否则必须强制类型转换一个变量
int a=3;
int b-4;
int *pa=&a;
int *pa=&b;
pa=pb;
(a=b或pa=pb是等效操作,但不是等价的,因为虽然a的值变化了,但是变来那个a和b的地址没有与变化)
【指针的自加运算】
*p++:++的优先级高于星号,但程序自左向右运行,p++:是先取出p存储的值,再使p这个地址空间加1(不是存储的值加1);相当于把1存储在下一个地址;
//p是个指针,给p加1,相当于让p指向了x所在地址的下一个位置。
(*p)++:括号优先级最高,先算(p),再算++
(p)++含义:先取出p指向的变量(地址空间)中的值,对存储的值自加;
//p则是p指向的位置对应的值,此时的p就是x(x是地址)的值,若为3,当输出p之后,再给p自加1,所以x中存储的值由3就变为4,而地址x并没有移动。
最后,萌新博主谢谢大家了,C语言指针很难,真的希望可以通过自己的博客,帮到大家!