C语言学习 指针简介(以交换数值为例)

🌈指针一直是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地址上的值。

简而言之,普通变量把值作为基本量,把地址作为通过&运算符获得的
派生量,而指针变量把地址作为基本量,把值作为通过*运算符获得的派生
量。

使用&、* 和指针可以操纵地址和地址上的内容。

🌞(●ˇ∀ˇ●)有帮助的话~ 点个赞叭 🙋

  • 30
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

~光~~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值