【C指针】初识指针

 

本文将引领读者初步认识指针。本人建议读者先将代码读懂,然后再自己编写代码,通过调试或直接输出的方式,查看指针的执行过程。

目录

什么是指针

指针变量

基本特性


什么是指针

在程序中定义了一个变量,在对程序进行编译时,系统就会给这个变量分配内存单元。编译系统根据程序中定义的变量类型,分配一定长度的空间。

下面的实操中,我将尝试帮助读者理解一个变量是如何存在一块地址中的

打开VS2019,写如下代码:

  1. 在” return 0”所在行打好断点。进入调试模式(一般是点击“本地Windows调试器”);
  2. 将输出的地址复制到上图3箭头里,并回车。注意,程序每次执行的地址都是不一样的;
  3. 如果没有弹出“内存1”对话框,则在调试模式下,前往(调试-窗口-内存-内存1)开启;
  4. 在空白处右键;
  5. 在弹出的对话框中,将显示格式设置为“4字节整数”,带符号显示。因为int型是4个字节,并且存储的是一个正数10,这样更方便我们查看。
  6. 这时,我们就可以看到,上图箭头2所输出的地址中,保存的数据是“10”

 

于是,在程序中,我们得知了地址为:“000000F7F65CF954”中存储的数据是“10”。通过地址能找到所需的变量单元,可以说,地址指向该变量单元,我们将地址形象化地称为“指针”。目前来说,指针,就是某个内存地址!

就像是你家的地址是Xxx省Xxx市Xxx区Xxx小区第x栋11楼2号,我通过这个地址就一定能找你家。那么,这个地址就是一个指针,指向的是你家。

变量的两种访问方式

我们变成一般是通过变量名来对内存单元进行,例如上图第7行,使用变量a直接取得里面的数据。其实程序经过编译以后已经将变量名转换为变量的地址,对变量值的存取都是通过地址进行的。这种按变量地址存取变量的方式称为直接访问方式

 

还有一种间接访问的方式,即变量中存放的是另一个变量的地址。也就是说,变量中存放的不是数据,而是数据的地址。按C语言的规定,可以在程序中定义整型变量、实型变量、字符型变量,也可以定义这样一种特殊的变量,它是存放地址的。

例如上图中,我先把变量a的地址放在一个指针中(别忘了指针就是专门用来存放地址的),然后通过取出指针中的地址,再根据地址去取出地址内的数据。

指针变量

就像是第4、5行代码中,变量a先是保存的数据为10,然后将保存的数据更换为100。指针也可以像变量一样更换保存的数据,那么这个指针就成为指针变量,它可以保存该种类型的任何地址。就像是上图中的第8、9行代码,指针p最初保存的是xa的地址,接下来就不保存xa的地址了,转而保存xb的地址。

 

指针变量的基本特性:

  1. 若有一个变量专门用来存放另一个变量的地址,那么就称它为“指针变量”。也就是说,指针变量里面存放的是指针,即地址
  2. 一定要区分“指针”和“指针变量”这两个概念。指针是一个地址,而指针变量是存放地址的变量
  3. 习惯上我们也将“指针变量”简称为“指针”,但大家心里一定要明白这两个指针的区别。一个是真正的指针,它的本质是地址;而另一个是指针变量的简称。
  4. 指针变量也是一种变量,占有内存空间,用来保存内存地址。指针类型的所有数据类型全部占4字节(32 bit:4字节;64bit:8字节),因为它本身是存储地址的!
    printf("int*:%d,char*:%d,double*:%d,folat*:%d",sizeof(int*), sizeof(char*),sizeof(double*), sizeof(float*));
  5. *:间接操作符
    1. 在指针声明时,* 号表示所声明的变量为指针
    2. 在指针使用时,* 号表示取出该地址中的数据。
    3. 即:*p放在等号的左边赋值(给内存赋值,写内存);*p放在等号的右边取值(从内存获取值,读内存)

 

指针变量的高级特性:

  1. 指针变量中只能存放地址(指针),不要将一个整数赋给一个指针变量。
  2. 指针变量和它所指向的内存块是两个不同的概念。
    1. 给p赋值p=0x1111; 只会改变指针变量值,不会改变所指的内容的值。
    2. 给*p赋值*p='a'; 不会改变指针变量的值,只会改变所指的内存块的值。
  3. 当我们不断的给某一指针变量赋值,就是不断的改变该指针变量的指向,与所指向的内存空间没有任何关系。

定义指针变量及初始化

//指针的先定义后初始化
void test01()
{
	int a=100;
	int* p; //定义指针变量

	p = &a; //把a变量的地址赋值给p,p此时保存的是a的地址;

	printf("p保存的地址为%p\n", p);
	printf("*p=%d\n", *p);
}

//指针的定义就初始化
void test02()
{
	int a = 10;
	int* p = &a;

	printf("p保存的地址为%p\n", p);
	printf("*p=%d\n", *p);
}

指针变量的赋值

void test01()
{
	int x = 9;
	int* pa, *pb;

	pa = &x; //把x的地址赋值给pa
	pb = pa; //指针变量的赋值

	printf("*pa=%d,*pb=%d\n", *pa, *pb); //都是一样的值
}

可见,可以直接将一个指针变量赋给另一个指针变量,只要将指针变量名赋给另一个指针变量名即可。但是需要注意的是:

  1. 这两个指针变量的基本数据类型一定要相同。
  2. 在赋值之前,赋值运算符“=”右边的指针变量必须是已经初始化过的。也就是说,切忌将一个没有初始化的指针变量赋给另一个指针变量。这是非常严重的语法错误。

基本特性

指针的申明与初始化

void test01()
{
	int* a, b, c; //只有a才是指针变量,b、c都是int型变量
	
	//如果要想同时申明多个指针变量,需要每个变量名前都加“*”号
	int* aa, * bb, * bc;
}
void test02()
{
	int* a, * b, * c; //只有这样a、b、c才都是int型指针变量

	/*不可直接赋值为常数
	*a = 10; 报错为:使用了为初始化的局部变量a
	*b = 20;
	*c = 30;
	因为指针变量a、b、c保存的是一个内存地址,而不是
	*/
}

void test03()
{
	int* a, * b, * c; //只有这样a、b、c才都是int型指针变量
	int x = 100;

	//a = x; 错误,相当于把x的值 当做一个地址值 赋值给a,当使用*a时,通过改地址取出里面的数就会出错
	a = &x; //正确,把x的地址 赋值给a,当使用*a时,通过改地址取出里面的数
	printf("%d", *a);

	b = &x;
	c = &x;
	printf("%d %d %d", *a, *b, *c); //全部输出100

	x = 90;
	printf("%d %d %d", *a, *b, *c); //全部输出90
}

scanf初始化指针

int num = 10;
printf("%p\n", &num);
int *p;  //等价于int *p=NULL; 
scanf("%p", &p);  //以地址的形式格式化输入;例如  0062FEA8 
printf("%p,%d", p, *p); //p是格式化输出指针本身(地址),*p是输出该指针所指的内容;

指针指向的改变

double a=10.8;
double b=20.8;  //指针的类型决定了该指针应该读取多长的地址,
//char型指针向后读取一个字节,int型指针向后读取4字节,
//如果int型指针读取char型指针,则会出错为 在内存中多读取了3个字节;则会多抓到一些垃圾值;这也要求指针类型和所指类型要一致;
printf("%p,%p\n",&a,&b);//先输出a和d的地址,以十六进制的方式显示;
double *p=&a;
printf("%lf,%p\n",*p,p);
p=&b; //改变了指针p的指向;
printf("%lf,%p",*p,p);

二级指针

//定义:int ** text,相当于是:int*/*text; 就是用来保存一级指针的地址
int x = 10;
int *p;//向OS提出申请,但还没有落实到那一块内存空间; printf("p的地址:%p", p);是无法执行的;
int **pp;
int ***ppp;
printf("x的地址:%p\n", &x); //显示内存地址:%p和%x(舍掉开头的00,并且小写);

p = &x;
printf("指针p的地址:%p,p的指向的值:%d\n", p, *p);

pp = &p;//二级指针只可以用来被一级指针的地址赋值;
printf("指针pp的地址:%p,pp的指向的值:%d\n", pp, **pp);

指针的初始化

int *p;//指针没有初始化;则不能这样:printf("%x", p);
int *pa = NULL;
printf("空指针的地址:%p", pa);  //地址为00000000
//空指针决不可读取里面的内容;
//定义:是这块内存区域的首地址,然而是类型决定指针要向前读取多少个字节;
//指针存储的是首地址,类型决定在哪里结束合解析的方式;

指针运算

int num = 100;
int *p1 = #//地址的赋值
int *p2 = p1; //指针的赋值,p1,p2保存的都是num的地址;
			  //p1,p2,num,一个改变,其他两个都会改变
printf("num:%d,*p1:%d,*p2:%d\n", num, *p1, *p2);
*p2 = 3;//改变其中任何一个,就将会改变全部
printf("改变后:num:%d,*p1:%d,*p2:%d\n", num, *p1, *p2);

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值