🌈指针一直是C语言学习者的一大拦路虎,下面我就简要以数值交换为例,简要说说指针的用处~同时,需要大家理解计算机底层的存储的逻辑,具体可以下拉到总结那的一张图,很清楚 ~ 相信看了这个例子,对于指针的使用你会有更深的了解 ~
🙋>本文参考于C Primer Plus 第六版~ ,开始叭~
指针简介
指针是一个值为内存地址的变量,或数据对象。正如char类型变量的值是字符,int类型变量的值是整数,指针变量的值是地址。在C语言中,指针有许多用法。本章将介绍如何把指针作为函数参数使用,以及为何要这样用。
假设一个指针变量名是ptr,可以编写如下语句:
ptr = &pooh; // 把pooh的地址赋给ptr
对于这条语句,我们说ptr“指向”pooh。ptr和&pooh的区别是ptr是变量,而&pooh是常量。或者,ptr是可修改的左值,而&pooh是右值。还可以把ptr指向别处:
ptr = &bah; // 把ptr指向bah,而不是pooh
现在ptr的值是bah的地址。
对于这条语句, 我们说ptr“指向”pooh。 ptr和&pooh的区别是ptr是变量,而&pooh是常量。 或者,ptr是可修改的左值,而&pooh是右值。
还可以把ptr指向别处:
ptr = &bah; // 把ptr指向bah,而不是pooh
现在ptr的值是bah的地址。
间接运算符 *
间接运算符*(indirection operator)找出储存在bah中的值,该运算符有时也称为解引用运算符。
val = *ptr; // 找出ptr指向的值
ptr = &bah;
val = *ptr;//两句放在一起相当于下面的语句:
val = bah;
//后跟一个指针名或地址时,*给出储存在指针指向地址上的值。
nurse = 22;
ptr = &nurse; // 指向nurse的指针
val = *ptr; // 把ptr指向的地址上的值赋给
//执行以上3条语句的最终结果是把22赋给val。
由此可见,使用地址和间接运算符可以间接完成上面这条语句的功能,这也是“间接运算符”名称的由来。
声明指针
要创建指针变量,先要声明指针变量的类型。
因为声明指针变量时必须指定指针所指向变量的类型,因为不同的变量类型占用不同的存储空间,一些指针操作要求知道操作对象的大小。另外,程序必须知道储存在指定地址上的数据类型。long和float可能占用相同的存储空间,但是它们储存数字却大相径庭。下面是一些指针的声明示例:
int * pi; // pi是指向int类型变量的指针
char * pc; // pc是指向char类型变量的指针
float * pf, * pg; // pf、pg都是指向float类型变量的指针
类型说明符表明了指针所指向对象的类型,星号(*)表明声明的变量是一个指针。
int * pi;
//声明的意思是pi是一个指针,*pi是int类型。
*和指针名之间的空格可有可无。通常,程序员在声明时使用空格,在解引用变量时省略空格。
pc指向的值(*pc)是char类型。pc本身是什么类型?我们描述它的类型是“指向char类型的指针”。pc 的值是一个地址,在大部分系统内部,该地址由一个无符号整数表示。但是,不要把指针认为是整数类型。一些处理整数的操作不能用来处理指针,反之亦然。
例如,可以把两个整数相乘,但是不能把两个指针相乘。所以,指针实际上是一个新类型,不是整数类型。因此,如前所述,ANSI C专门为指针提供了%p格式的转换说明。
使用指针在函数间通信
可以用指针实现数值交换的问题
🐬首先大家看一下这样一个问题,实现x和y的数值交换,需要你写一个函数实现。对于刚刚接触C语言,还不了解指针的uu而言,你们可能跟我当时想的一样。看看下面的代码有问题不?
#include <stdio.h>
void interchange(int u, int v); /* 声明函数 */
int main(void)
{
int x = 5, y = 10;
printf("Originally x = %d and y = %d.\n", x, y);
interchange(x, y);
printf("Now x = %d and y = %d.\n", x, y);
return 0;
}
void interchange(int u, int v) /* 定义函数 */
{
int temp;
temp = u;
u = v;
v = temp;
}
(●ˇ∀ˇ●)嘿嘿~可以尝试运行一下。
你会发现,进入这个函数之后,值未发生改变。
输出如下:
Originally x = 5 and y = 10.
Now x = 5 and y = 10.
🍎现在改进一下:
#include <stdio.h>
void interchange(int u, int v);
int main(void)
{
int x = 5, y = 10;
printf("Originally x = %d and y = %d.\n", x, y);
interchange(x, y);
printf("Now x = %d and y = %d.\n", x, y);
return 0;
}
void interchange(int u, int v)
{
int temp;
printf("Originally u = %d and v = %d.\n", u, v);
temp = u;
u = v;
v = temp;
printf("Now u = %d and v = %d.\n", u, v);
}
🍊输出如下
Originally x = 5 and y = 10.
Originally u = 5 and v = 10.
Now u = 10 and v = 5.
Now x = 5 and y = 10.
interchange()没有问题,它交换了 u 和 v 的值。问题出在把结果传回 main()时。interchange()使用的变量并不是main()中的变量。因此,交换u和v的值对x和y的值没有影响!
函数形参和其他局部变量都属于函数私有,因此,声明在不同函数中的同名变量是完全不同的变量。 而且,函数无法直接访问其他函数中的变量。这种限制访问保护了数据的完整性。但是,当确实需要在函数中访问另一个函数的数据时,可以把指针作为函数的参数。
🌈考虑到这样,我们采用指针解决!
#include <stdio.h>
void interchange(int * u, int * v);
int main(void)
{
int x = 5, y = 10;
printf("Originally x = %d and y = %d.\n", x, y);
interchange(&x, &y); // 把地址发送给函数
printf("Now x = %d and y = %d.\n", x, y);
return 0;
}
void interchange(int * u, int * v)
{
int temp;
temp = *u; // temp获得 u 所指向对象的值
*u = *v;
*v = temp;
}
☘️运行一下,你会发现,很顺利。
interchange(&x, &y);该函数传递的不是x和y的值,而是它们的地址。这意味着出现在interchange()原型和定义中的形式参数u和v将把地址作为它们的值。因此,应把它们声明为指针。由于x和y是整数,所以u和v是指向整数的指针,其声明如下:
void interchange (int * u, int * v)
接下来,在函数体中声明了一个交换值时必需的临时变量:
int temp;
通过下面的语句把x的值储存在temp中:
temp = * u;
记住,u的值是&x,所以u指向x。 这意味着用 * u 即可表示x的值,这正是我们需要的。不要写成这样:
temp = u; /* 不要这样做 */
因为这条语句赋给temp的是x的地址(u的值就是x的地址),而不是x的值。函数要交换的是x和y的值,而不是它们的地址。与此类似,把y的值赋给x,要使用下面的语句:
*u = * v;
这条语句相当于:
x = y;
🌺下面我们总结一下该程序示例做了什么。
我们需要一个函数交换x和y的值。把x和y的地址传递给函数,我们让interchange()访问这两个函数。使用指针和*运算符,该函数可以访问储存在这些位置的值并改变它们。
可以省略ANSI C风格的函数原型中的形参名,如下所示:
void interchange(int *, int *);
🌴一般而言,可以把变量相关的两类信息传递给函数。如果这种形式的函
数调用,那么传递的是x的值:
unction1(x);
如果下面形式的函数调用,那么传递的是x的地址:
function2(&x);
第1种形式要求函数定义中的形式参数必须是一个与x的类型相同的变
量:
int function1(int num)
第2种形式要求函数定义中的形式参数必须是一个指向正确类型的指
针:
int function2(int * ptr)
如果要计算或处理值,那么使用第 1 种形式的函数调用;如果要在被调函数中改变主调函数的变量,则使用第2种形式的函数调用。
我们用过的scanf()函数就是这样。当程序要把一个值读入变量时(如本例中的num),调用的是scanf(“%d”, &num)。scanf()读取一个值,然后把该值储存到指定的地址上。
对本例而言,指针让interchange()函数通过自己的局部变量改变main()中变量的值。
总结
这张图很好地反映了变量 地址 之间的关系。
🌈在许多语言中,地址都归计算机管,对程序员隐藏。然而在 C 中,可以通过&运算符访问地址,通过 * 运算符获得地址上的值。例如,&barn表示变量barn的地址,使用函数名即可获得变量的数值。
例如,printf(“%d\n”,barn)打印barn的值,使用 * 运算符即可获得储存在地址上的值。 如果pbarn=&barn;,那么*pbarn表示的是储存在&barn地址上的值。
简而言之,普通变量把值作为基本量,把地址作为通过&运算符获得的
派生量,而指针变量把地址作为基本量,把值作为通过*运算符获得的派生
量。
使用&、* 和指针可以操纵地址和地址上的内容。
🌞(●ˇ∀ˇ●)有帮助的话~ 点个赞叭 🙋