指针
指针
变量的地址
内存每一个字节都有一个对应的地址编号,方便计算机快速找到对应内存空间。也可以将地址形象的称为指针
假如:
int a=6; 系统分配10002~10005地址空间给a,存储int类型的数值6,并且生成变量a和地址10002的对照表。在执行代码时,通过变量名找到对应的地址,然后通过数据类型int获取4个字节空间内的信息,读取对应的数值
scanf(“%d”, &a); &获取对应a变量的地址,将键盘输入的指存入到对应地址内存中
指针变量
如果将一个变量b用来存储另一个变量a的地址(指针),称变量b为指针变量
int a;
int *b;
变量a的地址为10001,地址10001的内容为int类型数据10;
变量b的地址为10007,地址10007的内容为指针类型数据10001
定义指针变量
类型名 *指针变量名
-
类型名表示指针变量用来指定对应变量的数据类型。让系统知道对应地址的数据在内存中占的字节数和存放方式
-
*表示变量时一个指针类型的变量
-
指针变量只能存放指针,不能直接将一个数值赋值给指针变量。 int *p = &a;
在定义指针变量时,可以同时该指针变量初始化
引用指针变量
&:取地址运算符。例如&a获取变量a的地址
*:指针运算符。例如*p表示指针变量p指向的对象,与定义时不同
函数传递指针变量
也可以在函数的参数传递过程中传递指针变量
指针和数组
数组元素的指针
变量有地址,同样的数组元素也有地址,也可以通过指针指向数组元素
数组名代表数组中第一个元素的地址。并不是代表整个数组
数组元素的指针运算
当指针指向数组元素时,可以对指针进行加减运算
- p+1: 指向同一个数组中的下一个元素地址,不是地址数值+1,根据定义的类型大小增加(++)
- p -1: 指向同一个数组中的上一个元素地址,不是地址数值 -1 ,根据定义的类型大小减小(--)
- 如果p = &a[0], p+3 是a[3]的地址,也等效于a+3
- *(p+3)获取p+3地址的数组元素。[ ]实际是变址运算符,a[i]等效为*(a+i)
- 如果p1和p2指向同一个数组中的元素,则p1-p2的结果为之间的元素个数
指针引用数组元素
通过指针的方法引用数组元素的方法:
- 下标法 例如:a[i]
- 指针法 例如:*(a+i)或*(p+i),其中a是数组名,p是指向数组元素的指针变量
注意:
- a是数组首元素的地址,是一个常量,不能通过a++等方式改变a的值
- 当指针p指向数组元素时,指针p可以使用p[i]带下标的方式。当p = a时,p[i]等同于a[i].当p = &a[2],p[i] = a[i+2]
函数传递指针
在之前的函数参数为数组这一节强调,函数如果传递数组名时,传递的是数组首地址,且如果形参数组中各元素的值发生变化,则实参数组元素的值也跟着改变
-
形参经过编译后,自动编译为一个指针变量a,因此形参的定义[ ]中括号中可以是任意数值
-
函数传递数组一共有4中情况:
- 形参和实参都用数组名
- 实参数组名,形参指针名
- 实参指针名,形参数组名
- 形参和实参都用指针名
指针引用多维数组
现有一个二维数组:int a[3][4] = {{3,8,6,2},{5,6,3,9},{2,5,3,0}}(a包含3行,分别用a[0],a[1],a[2]表示)
二维数组地址说明:
-
a代表二位数组首行的起始地址1244952
-
a+1代表序号为1的行的地址1244968
计算方法:1244952+4*4 = 1244968
-
a[0],a[1],a[2]是一维数组名,也是各行的首地址
a[0] = &a[0][0] = *(a+0)
a[1] = &a[1][0] = *(a+1)
a[2] = &a[2][0] = *(a+2)
-
a[0]是一维数组名,则a[0]+1 表示 a[0][1]的地址
a[1] + 2 表示a[1][2]的地址,即&a[1][2]
a[1]+2 = *(a+1) +2 = &a[1][2]
-
a[0][2] = *(a[0]+2) = ((a+0) +2)
a[1][2] = *(a[1]+2) = ((a+1) +2)
注意: a+1 和a[1]的结果一样,但是性质不同。a+1指向第2行(属性为行),a[1]指向第2行首元素地址(属性为元素),因为C语言的地址信息中即包含了位置信息,也包含了数据的类型信息
例如:a+1再加1,是下一行,而a[1]+1是下一个元素
指针和字符串
指针指向字符串常量
引用字符串的方法
- 字符数组存储字符串
- 字符指针变量指向字符串常量
注意:
- %s是通过字符指针找到字符串常量的首地址,使地址不断加1,直到遇到\0为止,输出地址对应内容
- 对数值型数组是不能通过%s一次性输出全部元素,只能元素逐个输出
函数传递字符指针
在函数传递过程中,可以使用字符数组名作参数,也可以使用字符指针变量作参数
字符数组和字符指针的对比
通过以上的例子,可以看到字符数组和字符指针变量都能实现对字符串的操作
它们之间的区别:
- 字符数组包含多个元素,每个元素都有自己的地址,而字符指针变量中存放的是地址
- 可以对字符指针变量赋一个地址,但不能对字符数组名赋值,它是一个常量
- 字符数组除了在初始化是可以一次性对所有元素赋值,非初始化部分不能一次性赋值,但字符指针变量可以实现
- 字符指针变量一旦定义,需要及时对它赋予一个地址。否则会出现指针地址不明确,导致破会系统
- 字符数组的元素值可以被改变,但字符指针指向的字符串常量中的元素不能被改变
补:使用字符指针变量指向的字符串或字符数组都可以实现printf函数的可变格式输出。
函数指针变量
函数指针
什么是函数指针?
程序代码中的函数在编译时,会分配一段存储空间存储函数可执行代码,函数名代表函数的起始地址。调用该函数时,会从函数名处找到函数的起始地址,并执行对应函数
函数名即是函数指针
定义一个指针变量用于指向函数,存放函数的起始地址,则此指针为函数指针变量
定义函数指针变量
类型名 (*指针变量名)(函数参数列表)
注意:
-
定义函数指针变量之前需要确定好类型名,类型名代表可以指向的函数类型
例如:int ( *p )(int ,int)代表指针p只能指向int类型的函数,且对应的函数有两个整型形参
-
*指针变量名 需要用括号括起来
-
给函数指针变量赋值时,只需给出函数名, 不用写入参数
-
(*指针名)是调用函数,使用(*指针名)替代函数名即可
-
函数指针变量没有加减运算,是无意义的运算
函数指针变量对比函数名的两者使用的优点:函数名调用函数,只能调用指定的一个函数,而函数指针变量可以通过改变指针变量的地址灵活调用不同的函数
函数指针做函数参数
在实际使用中,常常将函数指针作为另一个函数的参数,传递到其他函数中
例如函数指针变量p指向函数a,函数b中的一个参数为变量p
int b( int ( *p ))
函数返回指针
函数除了可以返回整型、字符、浮点数等以外,还可以返回指针
返回指针的函数定义方式:
类型名 *函数名 (参数列表)
例如:
int *ff(int a, int b)
返回指针的方法:通过return函数返回指针变量名
指针数组
什么是指针数组
一个数组,每个元素都是指针型数据,称为指针数组
定义方法:
类型名 *数组名[数组长度]
使用场景:当有多个字符串时,可以使用指针数组处理更加灵活
多重指针
一个指针变量指向另一个指针称为多重指针
定义多重指针的方法:
指针类型 **指针名
例如:
char **p
理论上可以出现3重地址,甚至更多重地址,但是通常间接访问变量一般不会超过二级地址。级数越多越难于理解和使用
main函数的参数
main函数是程序的起始位置,一般写成void main( ),其中括号里面是没有任何参数,本质上main函数可以给定参数。有参数的形式为:
void main(int argc, char *argv[ ] )
其中argc和argv就是main函数的形参。argc接收参数个数,argv是字符指针数组
由谁传递实参给main函数?
由操作系统传递实参给main函数。实参是和执行文件的命令一起给出的
如何传递实参给main函数?
-
visual c++菜单栏project—settings—Debug—program arguments
-
在DOS、Linux终端、Macos终端中命令行的一般形式为:
命令名 参数1 参数2 … 参数n
其中命令名为可执行文件名(包含main函数的.exe文件),各个参数之间用空格分隔
动态内存操作
内存存储区分为静态存储区和动态存储区,全局变量以及静态局部变量是分配的静态存储区,而局部变量(非静态)是分配动态存储区。动态分配的空间使用时随时开辟,不需要时随时释放。也可以通过代码来分配动态内存
malloc
开辟动态存储区
malloc(size)
例如:
int *p
p = malloc(7*4)
开辟一个长度为size(字节)的连续空间,size为正整数,函数返回分配空间的首地址,malloc函数为指针型函数。如果此函数没有成功分配空间(例如空间不足、空间异常等),则返回空指针(NULL)
calloc
开辟动态存储区
calloc(unsigned n, unsigned size)
例如:
p = calloc(50,4)
开辟一个n*size长度的空间,空间分配较大
realloc
为已开辟的空间重新分配动态存储区
realloc(p, size)
例如:
p = realloc(p,10)
p为之前已开辟过的动态存储空间,通过realloc可以改变p的空间大小,改为size大小
free
释放已经开辟的内存空间
free(p)
例如:
free(p)
p为之前已开辟过的动态存储空间,通过free可以将p指向的内存空间释放,让系统回收
动态内存操作函数包含在stdlib.h头文件中,因此在使用malloc、calloc、realloc、free函数之前需要在文件开头加上#include<stdlib.h>
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律