C语言 -- 指针
一、变量的地址
在C语言中,每定义一个变量,系统就会给变量分配一块内存,比如,定义一个 int num = 10; ,这样就会在计算机内存中开辟一块内存,来存放这个10,而这一小块内存是有地址的,我们可以通过这个地址,来找到里边的内容。如果把计算机的内存区域比喻成一个学校,那么每块内存的地址就像教室的门牌号。
C语言采用运算符&来获取变量的地址。如下:
1 2 3 4 5 6 7 8 9 10 11 12 | int main() { int num1 = 10; char english = 'a' ; double price = 100.00; printf ( "%p\n" , &num1); printf ( "%p\n" , &english); printf ( "%p\n" , &price); system ( "pause" ); } |
结果:
在printf函数中,输出内存地址的格式控制符是%p,地址采用十六进制的数字显示。
二、指针
指针是一种特别变量,全称是指针变量,专用于存放其它变量在内存中的地址编号,这样的一份数据可以是数组、字符串、函数,也可以是另外的一个普通变量或指针变量。指针在使用之前要先声明。
现在假设有一个 char 类型的变量 c,它存储了字符 'K'(ASCII码为十进制数 75),并占用了地址为 0X11A 的内存(地址通常用十六进制表示)。另外有一个指针变量 p,它存储了 0X11A,正好等于变量 c 的地址,这种情况我们就称 p 指向了 c,或者说 p 是指向变量 c 的指针。
定义指针变量的语法是:
datatype *varname; |
或者
datatype *name = value; |
“*”表示这是一个指针变量,datatype
表示该指针变量所指向的数据的类型 。例如:
int *count; |
count 是一个指向 int 类型数据的指针变量,至于 count 究竟指向哪一份数据,应该由赋予它的值决定。再如:
1 2 3 | int num = 100; count = &num //将num的地址给count |
将变量num的地址赋予给count,此时 count就指向了 num。取地址用的就是 &
注意:不管是整型、浮点型、字符型,还是其他的数据类型的内存变量,它的地址都是一个十六进制数,可以理解为内存单元的编号。我们用整数型指针存放整数型变量的地址;用字符型指针存放字符型变量的地址;用双精度型指针存放双精度型变量的地址,用自定义数据类型指针存放自定义数据类型变量的地址。
如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | int main() { int num1 = 10; char english = 'a' ; double price = 100.00; int *num2; num2 = &num1; char *enlist2; enlist2 = &english; double *price2; price2 = &price; } |
定义指针变量时必须带*
,给指针变量赋值时不能带*,如下
:
1 2 3 4 5 6 7 8 9 10 11 | int main() { int num1 = 10; char english = 'a' ; double price = 100.00; int *num2 = &num1; char *enlist2 = &english; double *price2 = &price; } |
三、通过指针操作内存变量
定义了指针变量,并指向了内存变量的地址,就可以通过指针来操作内存变量(在指针前加星号 *),效果与使用变量名相同。
格式:
*pointer; |
要取得指针所指向内存中的值,需要用*号,*
称为指针运算符,用来取得某个地址上的数据,加上指针变量名称即可,如下:
1 2 3 4 5 6 7 | int main() { int num1 = 10; int *num2 = &num1; printf ( "%d\n" ,*num2); printf ( "%d\n" ,num1); } |
结果:
假设 num1的地址是 0X1000,num2 指向 num1 后,num2本身的值也会变为 0X1000,*num2表示获取地址 0X1000 上的数据,也即变量 num1 的值。从运行结果看,*num2和num1 是等价的。
CPU 读写数据必须要知道数据在内存中的地址,普通变量和指针变量都是地址的助记符,虽然通过 *num2 和 num1 获取到的数据一样,但它们的运行过程稍有不同:num1 只需要一次运算就能够取得数据,而 *num2 要经过两次运算,多了一层“间接”,因为*num2要先找到地址,然后取里边的值,而num1是直接取值。
假设变量 num1、num2 的地址分别为 0X1000、0XC100,它们的指向关系如下图所示:
![](http://c.biancheng.net/uploads/allimg/190114/1IG3MJ-2.jpg)
程序被编译和链接后,num1、num1 被替换成相应的地址。使用 *num2 的话,要先通过地址 0XC100 取得变量 num2本身的值,这个值是变量 num1 的地址,然后再通过这个值取得变量num1 的数据,前后共有两次运算;而使用 num1 的话,可以通过地址 0X1000 直接取得它的数据,只需要一步运算。
也就是说,使用指针是间接获取数据,使用变量名是直接获取数据,前者比后者的代价要高。
指针除了可以获取内存上的数据,也可以修改内存上的数据,例如:
1 2 3 4 5 6 7 8 9 10 | int main() { int num1 = 10; int *num2 = &num1; *num2 = 20; printf ( "%d\n" , *num2); printf ( "%d\n" , num1); } |
四、再来讨论函数的参数传递
平时在函数调用时候,一般都会传入一个普通的值,然后在函数里进行一些操作,比如传入一个整形的id,通过这个id查找对应的信息,如果我们想通过一个函数,来改变主函数的内容,就需要把地址当作参数传递,在函数里边通过地址,修改里边的内容,比如:
1 2 3 4 5 6 7 8 9 10 11 12 13 | void updateVal( int *num) { *num = 20; } int main() { int num1 = 10; updateVal(&num1); printf ( "%d\n" , num1); } |
主程序把变量num1的地址传递给函数updateVal,updateVal函数的参数num是一个指针,接存放变量num1的地址。在函数updateVal中,根据指针中的地址直接操作内存,从而修改了主程序中变量num1的值。
我们已经使用scanf函数很多次了,调用scanf函数的时候,需要在变量前面加符号&,其实就是把变量的地址传给scanf函数,scanf函数根据传进去的地址直接操作内存,改变内存中的值,完成了对变量的赋值。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core内存结构体系(Windows环境)底层原理浅谈
· C# 深度学习:对抗生成网络(GAN)训练头像生成模型
· .NET 适配 HarmonyOS 进展
· .NET 进程 stackoverflow异常后,还可以接收 TCP 连接请求吗?
· SQL Server统计信息更新会被阻塞或引起会话阻塞吗?
· 本地部署 DeepSeek:小白也能轻松搞定!
· 传国玉玺易主,ai.com竟然跳转到国产AI
· 自己如何在本地电脑从零搭建DeepSeek!手把手教学,快来看看! (建议收藏)
· 我们是如何解决abp身上的几个痛点
· 如何基于DeepSeek开展AI项目