C语言中的指针:掌握内存的钥匙
引言
C
语言是一种结构化编程语言,它提供了对硬件底层的直接访问,其中最强大的特性之一就是指针。指针允许程序员直接操作内存地址,这对于理解程序的内部工作原理以及优化代码性能至关重要。本文将深入探讨C语言中指针的概念、使用方法以及一些高级技巧。
什么是指针
指针就是内存地址,指针变量是用来存放内存地址的变量。就像其他变量或常量一样,必须在使用指针存储其他变量地址之前,对其声明。
声明形式:
type *pointName;
其中type
是指针的基类型,它必须是一个有效的C
数据类型,pointName
是指针变量的名称。用来声明指针的星号*
与乘法中使用的星号是相同的,但是在c
语句中,星号是用来指定一个变量是指针。
例如:指针简单使用
输出结果
指针与变量
内存区的每一个字节都有一个编号,这就是"地址"
,如果在程序中定义了一个变量,在对程序进行编译运行时,系统就会给这个变量分配一个内存
单元,并确定它的内存地址也就是一串这个地址独有的编号,指针是内存单元的编号,指针变量是存放地址的变量,通常我们会把指针变量称为指针,其实指针与指针变量含义式不一样的。
指针变量赋值
指针变量在使用之前, 一定要先赋值,也就是让指针变量指向一个地址。注意,给指针变量赋值只能式地址,通过"&"符号来获取普通变量的地址。“&” - 取地址运算符,“*” - 指针运算符,或称为间接访问运算符,取地址的值。
例子:
输出结果:上面代码演示了如何在C语言
中使用指针,第一步定义一个整型变量i和一个指向整型变量的指针p
。然后将i的值初始化为10
,并将p
指向i的地址,然后打印出i
,和p
的内存地址以及i的值和通过指针p
访问到的值。
i = 0x7ffd55fc953c
变量i的存储地址, p = 0x7ffd55fc953c
是指针变量p指向的地址,以为代码中把i的地址赋给了p
,所以p
指向i
的存储地址。i=10
给i
赋值10
,*p = 10
指针变量p
指向地址里的值,p
指向的是地址0x7ffd55fc953c
,而这个地址里存储的值为10
,所以*p=10
。
通过地址取值:*((unsigned char *)p))
输出结果:((unsigned char *)7c3d726c))
代表把 0x7c3d726c
这个值,强制转换成一个无符号字符类型指针,相当于告诉编译器,这是一个地址,强制转换的类型和变量a
的数据类型保持一致,因为要通过地址,把变量i的值读出来,最后通过*
把这个地址的数据读出来,结果7c3d726c = 10
通过指针改变某个内存地址里的值
上面说了如何获取内存地址的值,那也可以通过指针改变某个内存地址里面的值。格式: *指针变量 = 数值; 【*p = 20】
输出结果:
案例2:输入a和b两个整数,然后按先大后小顺序输出a和b。不交换整型变量的值,而是交换两个指针变量的值。
分析:输入a=5,b=6,由于a<b,将p1和p2交换,注意的是,a和b的值并未交换,他们仍然保持原值,但是p1和p2的值改变了,p1的值原来为&a,后来变成了&b, p2的值原来为&b后来变成了&a,这样在输出p1和p2的时候,实际上是输出了变量b和a的值,所以先输出6后输出5。
指针变量作为函数参数
函数的参数不仅可以是整型,浮点型,字符型,还可以是指针类型,它的作用将一个变量的地址传送到另一个函数中。
案例:通过指针类型的数据作为函数的参数,对输入的两个整数按大小顺序输出。【通过指针实现交换两个变量的值】
案例:输入三个整数a,b,c,
要求按由大到小的顺序输出。
指针与一维数组
一个变量有地址,一个数组包含若干个元素,每个数组元素都在内存中占有存储单元,它们都有相应的地址,指针变量既然可以指向变量,当然也可以指向数组元素,把某一元素的地址放到一个指针变量中,所谓的数组元素的指针就是数组元素的地址。c语言定义数组时,编译器会分配连续地址的内存,来存储数组里的元素,实际上数组本质上也是指针。
分别打印了arr
和 &arr[0]
的地址,发现地址是一样的,既然是个地址,那就可以使用指针的形式,去访问地址里存储的值,然后打印*arr
的值,得到的结果为1
,正好和arr[0]
的值对应,最后得出数组也可以使用指针的形式去使用。
数组元素的引用
引用数组元素可以使用下标法,也可以使用指针法,即通过指向数组元素的指针找到所需的元素。使用指针法能使程序质量高,占用内存少,运行速度快。
p = &a; // p的值是a[0]的地址
p = a; // p的值是数组a首元素即a[0]的地址
注意:程序中的数组名不代表整个数组,只代表数组首元素的地址。上面的p = a
的作用是把a数组首元素的地址赋给指针变量p
,而不是把数组a
各元素的值赋给p
,可以简写为 int *p = &a[0];
也可以写成int *p = a;
也可以写成两行的形式int *p; p = &a[0];
他们都有一个作用:将a
数组首元素即a[0]
的地址赋给指针变量p
(而不是*p
)。
- 下标法
a[i]
的形式 - 指针法如
*(a+i)
或*(p+i)
。其中a
是数组名,p
是指向数组元素的指针变量。
或者用指针变量指向数组元素
注意:在使用指针变量指向数组元素的时候,可以通过改变指针变量的值指向不同的元素,例如上面代码中的方法是使用指针变量p
来指向元素,用p++
使p
的值不断改变宠儿指向不同的元素。
换一种想法,如果不用p
变化的方法而用数组名a
变化的方法如a++
行不行呢。for(p=a;a<(p+10);a++) printf("%d",*a);
答案是不行的,因为数组名a
代表数组首个元素的地址,他是一个指针型常量,它的值在程序运行期间是固定不变的,既然a
是常量,所以a++
是无法实现的。
例:通过指针变量输出整型数组a的10个元素。
很明显,输出的数值并不是a
数组中各个元素的值,因为在执行第二个for
循环读入数据后,p
已指向a
数组的末尾,因此在执行第二个for
循环时候,p的起始值不是&a[0]
了,而是a+10
。
解决上面问题只要在第二个for循环之前加一个赋值语句即可
常用指针引用数组元素的情况
- (1) p++; p; p++使p指向下一个元素a[i],然后再执行p,则得到下一个元素a[i]的值。
- (2) p++; 由于++和同优先级,结合方向为自右而左,因此它等价于*(p++)。先引用p的值,实现*p的运算,然后再使p自增1。
- (3) (p++)与(++p)作用,前者先取p值,然后使p加1。后者是先使p加1,再取p。若初始值为a即&a[0],若输出*(p++)得到a[0]的值,而输出*(++p)得到a[1]的值。
- (4) ++(*p) 表示p所指向的元素值加1,如果p = a,则++(*p)相当于++a[0],若a[0]的值为3,则在执行++(*p)即++a[0]后的值为4。
- (5) 如果p当前指向a数组中第i个元素a[i],则:
*(p--)
相当于a[i--]
,先对p进行运算(求p所指向的元素的值),再使p自减。
*(++p)
相当于a[++i],先使p自加,在进行运算。
*(--p)
相当于a[–i],先使p自减,再进行*运算。
将++和–运算符用于指针变量十分有效,可以使指针变量自动向前或向后移动,指向下一个或上一个数组元素。
持续更新中。。。