文章目录
指针变量
计算机中,内存以字节为单位分割,其中每个字节存储 8 位信息。每个字节都有自己的地址,这样程序才能找到需要的信息,就像每个人都有自己的家庭住址,其他人才能通过这个住址找到想找的那个人,而指针变量就是存储这个“家庭住址”的变量。程序中每个变量都存在内存中的一个或多个字节中,把第一个字节的地址作为这个变量的地址
如果把某个变量 i
的地址赋给指向 i
这种变量类型的指针变量 p
,我们就称之为 p
指向 i
,即 p
是 i
的指针
指针变量的声明
int *p;
其中 int
表示指向对象的类型,*
表示声明的这个变量是指针变量
指针变量的声明可以和其他变量的声明一起出现 int i, j, a[3], *p;
*
和 &
&
取地址符
从名字上就可以看出 &
的作用是取一个变量的地址,然后你要拿这个地址做什么我就不知道了
我们先来看看我们取到的地址是什么样的吧
#include <stdio.h>
int main() {
int i;
printf("%p\n", &i);
return 0;
}
输出结果为:0x7ffcf04c01f4
0x7ffcf04c01f4
就是变量 i
的地址,你的输出结果一般都和我不一样,这涉及到系统分配内存的知识,反正你每次运行程序系统不会给你把你那个变量一直分配到同一个内存中的,所以地址也不会是一样的
指针的赋值
既然已经说到了取地址符,那么就要提一下取地址符一个很重要的作用——为指针变量赋值
...
int i , *p;
p = &i;
...
取 i
的地址赋给 p
,那么指针 p
就指向变量 i
了,即 p
是 i
的指针
上面的代码也可以合并成一句:int i, *p = &i;
只要指针的初始化在要指向的变量的声明之后即可
*
间接寻址符
*
是个很神奇的符号,使用它我们就可以获得一个内存地址里面存放的信息
比如:当 p
指向 i
后,我们在 p
前面加上 *
即 *p
就能获取 i
的内容,也可以通过 *p
修改 i
的内容,也就是我们可以认为 *p
等价于 i
#include <stdio.h>
int main() {
int i, *p = &i;
i = 2;
printf("let i = 2\n");
printf("i = %d\n", i);
printf("*p = %d\n\n", *p);
*p = 3;
printf("let *p = 3\n");
printf("i = %d\n", i);
printf("*p = %d\n", *p);
return 0;
}
输出结果为:
let i = 2
i = 2
*p = 2
let *p = 3
i = 3
*p = 3
注意:永远不要对没有初始化的指针变量使用间接寻址符
*
q = p
和 *q = *p
的区别
我们先拟一个叙述的背景方便叙述
...
int i, j, *p, *q;
p = &i;
q = &j;
...
q = p
:把指针变量 p
的值赋给指针变量 q
,这样指针变量 q
就存放着个指针变量 p
一样的数据,指向同一个内存地址,即指针 q
也将指向变量 i
*q = *p
:我们说过在指针变量前加上 *
就等价于它所指向的变量,所以这个式子等价于 j = i
把变量 i
的值赋给变量 j
指针作为参数
作用:
- 解决 C 语言用值进行参数传递无法在函数中改变实际参数值的问题
- 解决当传递参数是个存储空间很大的变量时造成的时间和空间的浪费问题
无法修改实际参数值问题
在 C 语言中函数参数的传递都是通过值进行传递的,它会创建一个副本变量(这个副本的值和传入的值是一样的,但副本变量和传入的变量在内存中是存储在两个不同的地方的,也就是两个值一样的不同变量)在函数中使用,所以是不会对传入的实际参数值造成修改的,指针就解决了这个问题,我们可以将想在函数中修改的变量的地址传给函数,地址作为实际参数,也是无法被修改的,但是函数内可以通过这个地址直接修改这个地址的信息,间接的解决了 C 语言无法发修改实际参数的问题
现在你们该明白了为什么 scanf()
函数中要赋值的变量前经常要加个 &
,使用 scanf()
函数时我们只有传入想修改的变量的地址,函数内才能对这个变量进行修改
下面是一个简单的用指针作为参数的例子:
#include <stdio.h>
void zero(int *);
int main() {
int i;
i = 2;
printf("i = %d\n", i);
zero(&i);
printf("after zero()\n");
printf("i = %d\n", i);
return 0;
}
void zero(int *p) {
*p = 0;
}
输出结果为:
i = 2
after zero()
i = 0
解决参数传递造成资源浪费
因为 C 语言的用值传递要创建一个副本,当要传递的这个变量需要很大的存储空间时,这个副本的创建将浪费大量的内存空间和时间,但如果只传入这个变量的指针的话就可以节省这部分内存的开销并使程序运行的更快
用 const 保护参数
假如你要传入一个需要存储空间很大的变量而且不想修改它,只是为了让程序更高效,于是你传入了这个变量的地址。可是传入的这个函数就有可能不小心修改了这个变量,为了保护这个变量,你可以在函数声明中在想要保护的参数前加上 const
例如:void zero(const int *);
这个时候编译就会报错,因为 zero()
中修改了传入的指针所指向的变量的值
const 在不同位置的区别
const int *a
这里的 const
修饰的是 int
,int
是一个整型变量,因此 a
所指向的整型变量受到保护,不能通过 *a
来修改,但可以重新给 a
赋值,使它指向另一个对象,例如:
const int *a = NULL;
int b = 1;
int c = 1;
a = &b; //ok
a = &c; //ok
*a = 2; //error
int *const a
这里的 const 修饰的是 a
,a
是一个指针变量,因此不能重新给 a
赋予其他值,即 a
不能指向其他变量,但可以修改 a
指向的变量的值
const int *const a
这是上面两个的结合体啦,此时既不能让 a
指向其他变量,也不能修改 a
所指向的变量的值
指针返回值
指针和其他变量一样也可以作为函数的返回值,好像没什么要说的了
注意:永远不要返回指向局部变量的指针,因为函数的局部变量在函数结束时,变量的内存空间将被回收,这块内存将不属于这个程序,对其进行访问是一个危险的操作