C语言学习笔记(十)--指针

目录

前言

二.指针变量的定义和初始化

                补充

 三.间接寻址运算符

 四.按值调用与模拟按引用调用

五.函数指针及其应用 


前言

上一篇文章讲了数组,这篇文章讲指针。主要内容:指针数据类型,指针变量的定义和初始化,取地址运算符,间接寻址运算符,按值调用与模拟按引用调用,指针变量作函数参数,函数指针。

这节内容建议去看视频讲解!!!


一.变量的内存地址

通过前面的学习,我们已经了解到,C程序中变量的值都是存储在计算机内存特定的存储单元中的,内存中的每个单元都有唯一的地址,就像街区中的房子都有唯一的地址、宾馆中的房间都有唯一一个编号一样,那么如何获得变量的地址呢?

这就要用到取地址运算符,即&。

例:使用取地址符&取出变量的地址,然后将其显示在屏幕上。

代码如下:

#include<stdio.h>
int main(void)
{
	int a = 0, b = 1;
	char c = 'A';
	printf("a is %d,&a is %p\n",a,&a);
	printf("b is %d,&b is %p\n",b,&b);
	printf("c is %d,&c is %p\n",c,&c);
	return 0;
}

运行结果如下:

程序中第6-8行使用了%p格式符,表示输出变量a,b,c的地址值。

注意:这里的地址值是用一个十六进制的无符号整数表示的,其字长一般与主机的字长相同。

内存中的地址都是按字节编号的,即内存中的每个字节的存储单元都有一个地址,在程序编译或函数调用时,根据程序中定义的变量类型为变量分配相应字节数的存储空间。

变量在内存中所占存储空间的首地址,称为该变量的地址,而变量在存储空间中存放的数据,称为变量的值。如果在声明变量时没有给变量赋初值,那么它们的内容就是随机的,不确定的,变量的名字可看成对程序中数据存储空间的一种抽象。


二.指针变量的定义和初始化

存放变量的地址需要一种特殊的变量,这种特殊的数据类型就是指针。具有指针类型的变量,称为指针变量,它是专门用于存储变量的地址值的变量,定义形式如下:

类型关键字 *指针变量名;

 其中,类型关键字代表指针变量要指向的变量的数据类型,即指针变量的基类型,例如:

int *pa;

 那么我们如何定义俩个具有相同基类型的指针变量呢?

int *pa,*pb;        //定义了可以指向整形数据的指针变量pa和pb

int *pa,pb;        //定义了可以指向整形数据的指针变量pa和整形变量pb

显然我们应该用第一种,而不是第二种。并且,指针变量的定义只是声明了指针变量的名字及其所能指向的数据类型,并没有说明指针变量究竟指向哪里,如下面例子。

代码如下:

#include<stdio.h> 
int main(void)
{
	int a = 0, b = 1;
	char c = 'A';
	int *pa, *pb;
	char *pc;
	printf("a is %d,&a is %p,pa is %p\n", a, &a, pa);
	printf("b is %d,&b is %p,pb is %p\n", b, &b, pb);
	printf("c is %c,&c is %p,pc is %p\n", c, &c, pc);
	return 0;
}

在Devc++中可以运行,但是在VS2019中不能运行,这是因为,局部变量pa,pb,pc没有被初始化,即指出用户企图使用未初始化的指针。指针变量未初始化意味着指针变量的值是一个随机值,我们无法预知它会指向哪里。使用未初始化的指针变量所带来的错误,隐患是非常严重的!所以,为了避免忘记指针初始化系统带来的潜在危险,防止指针乱指一气,习惯上在定义指针变量的同时讲其初始化为NULL,将上述代码修改为:

#include<stdio.h> 
int main(void)
{
	int a = 0, b = 1;
	char c = 'A';
	int* pa = NULL, * pb = NULL;
	char* pc = NULL;
	printf("a is %d,&a is %p,pa is %p\n", a, &a, pa);
	printf("b is %d,&b is %p,pb is %p\n", b, &b, pb);
	printf("c is %c,&c is %p,pc is %p\n", c, &c, pc);
	return 0;
}

运行结果如下:

 显然,这个结果也不是我们需要的,必须将指针变量指向确定的内存单元。代码如下:

#include<stdio.h> 
int main(void)
{
	int a = 0, b = 1;
	char c = 'A';
	int* pa, * pb;
	char* pc;
	pa = &a;
	pb = &b;
	pc = &c;
	printf("a is %d,&a is %p,pa is %p,&pa is %p\n", a, &a, pa, &pa);
	printf("b is %d,&b is %p,pb is %p,&pb is %p\n", b, &b, pb, &pb);
	printf("c is %c,&c is %p,pc is %p,&pc is %p\n", c, &c, pc, &pc);
	return 0;
}

运行结果如下:

指向某变量的指针变量,通常简称为某变量的指针,虽然指针变量中存放的是变量的地址值,二者在数值上相等,但在概念上变量的指针并不等同于变量的地址,变量的地址是一个常量,不能对其进行赋值。而变量的指针则是一个变量,其值是可变的。 


补充

指针变量只能指向同一基类型的变量,否则会引起warning

例如将上面代码第10行,改为:pc=&b;会出现不兼容的问题。

我们可以在定义指针变量的同时对指针变量进行初始化,例如:

int *pa=&a;

这个变量声明语句中的*只是一个指针类型说明符,不是间接寻址运算符,所以,不能理解为将&a的值赋给pa所指向的变量。

它等同于:int *pa;   pa= &a;

含义为:定义一个可以指向整型数据的指针变量pa,并用整型数据变量a的地址值对指针变量pa进行初始化,从而使指针变量pa具体地指向了整型变量a。 

 三.间接寻址运算符

通过指针变量间接存取它所指向的变量的访问方式称为间接寻址。

在C语言中,获取变量的地址需要使用取地址运算符&。那么,如何通过指针变量pa来存取它所指向的变量a的值?

这就要用到指针运算符,也称间接寻址运算符或解引用即*。间接寻址运算符*用来访问指针变量指向的变量的值。

例如:使用指针变量,通过间接寻址输出变量的值。

代码如下:

#include<stdio.h> 
int main(void)
{
	int a = 0, b = 1;
	char c = 'A';
	int* pa = &a, * pb = &b;
	char* pc = &c;
	*pa = 9;
	printf("a is %d,&a is %p,pa is %p,&pa is %d\n", a, &a, pa, *pa);
	printf("b is %d,&b is %p,pb is %p,&pb is %d\n", b, &b, pb, *pb);
	printf("c is %c,&c is %p,pc is %p,&pc is %c\n", c, &c, pc, *pc);
	return 0;

运行结果如下:


通过上面几个例子,我们使用指针应该遵循3条准则

1.永远清楚每个指针指向了哪里,指针必须指向一块有意义的内存;

2.永远清楚每个指针指向的对象的内容是什么;

3.永远不要使用未初始化的指针变量 

 四.按值调用与模拟按引用调用

用普通变量作函数参数的方法,它其实是一种按值调用的方法,即程序将函数调用语句中的实参的一份副本传给函数的形参。

下面代码是演示程序按值调用的例子:

#include<stdio.h>
void Fun(int par);
int main(void)
{
	int arg = 1;
	printf("arg=%d\n", arg);
	Fun(arg);
	printf("arg=%d\n", arg);
	return 0;
}
void Fun(int par)
{
	printf("par=%d\n", par);
	par = 2;
}

运行结果如下:

 

程序在函数Fun()中第14行改变了函数的形参值,并在第8行调用了函数Fun()后再次输出实参的值,由程序的运行结果可以看出,函数形参值的改变并未影响实参值的改变。这是因为传给函数形参的值只是函数调用语句中实参的副本,因此,按值调用的方法不能在被调函数中改变其调用语句中的实参值。 

下面代码是演示程序按引用调用的例子:

#include<stdio.h>
void Fun(int* par);
int main(void)
{
	int arg = 1;
	printf("arg=%d\n", arg);
	Fun(&arg);		//传递变量arg的地址值给函数
	printf("arg=%d\n", arg);
	return 0;
}
void Fun(int* par)
{
	printf("par=%d\n", *par);		//输出形参指向的变量的值
	*par = 2;			//改变形参指向的变量的值
}

运行结果如下:

 程序第11行将函数的形参声明为指针类型,使用指针变量作为函数形参,这就意味着形参接收的数据只能是一个地址值。因此,程序的第七行代码使用取地址运算符&获取变量arg的地址值并将其传给函数Fun(),从而使得形参par指向了变量arg。程序第14行,函数Fun()是使用间接寻址运算符*改变了形参指向的变量的值,因此第8行再次向屏幕输出变量arg的值时,值由1变为2.

五.函数指针及其应用 

函数指针就是指向函数的指针,指向函数的指针变量中存储的是一个函数在内存中的入口地址。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

瑟瑟发抖的可乐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值