C语言小知识之为什么要使用指针详析

刚开始学习C语言的时候,感觉最难理解的就是指针,什么指针变量,变量指针,指向指针的变量,指向变量的指针?一堆概念,搞得人云里雾里的,今天不讨论这些概念的问题,从最底层来分析C语言中为什么要使用指针,指针存在的意义又是什么呢?

首先从一个简单的例子来看,写一段代码来交换x、y的值。

void main( void )
{
    u8 x = 10, y = 20;
    u8 temp;

    __asm( "sim" );                             //禁止中断
    SysClkInit();
    delay_init( 16 );
    LED_GPIO_Init();
    Uart1_IO_Init();
    Uart1_Init( 9600 );
    ADC_GPIO_Init();
    __asm( "rim" );                             //开启中断
    while( 1 )
    {
        LED = ~LED;

        printf( "x = %d,y = %d\r\n", x, y );

        temp = x;
        x = y;
        y = temp;

        printf( "---> x = %d,y = %d \r\n\r\n\r\n", x, y );

        delay_ms( 200 );
    }
}

在STM8单片机中,交换x、y的值,并将值打印出来,打印结果如下:

通过第三个变量temp很轻松的就将x、y的值交换了。

但是为了程序的美观性,不想再主程序中写这么多的代码,于是决定用一个函数在外部来实现这个功能。

void swap( u8 x, u8 y )
{
    u8 temp;
    temp = x;
    x = y;
    y = temp;
}

void main( void )
{
    u8 x = 10, y = 20;
    __asm( "sim" );                             //禁止中断
    SysClkInit();
    delay_init( 16 );
    LED_GPIO_Init();
    Uart1_IO_Init();
    Uart1_Init( 9600 );
    ADC_GPIO_Init();
    __asm( "rim" );                             //开启中断
    while( 1 )
    {
        LED = ~LED;

        printf( "x = %d,y = %d\r\n", x, y );
        sawp( x, y );
        printf( "---> x = %d,y = %d \r\n\r\n\r\n", x, y );

        delay_ms( 200 );
    }
}

在主函数外面定义一个函数,专门用来交换x,y的值。程序运行结果如下:

此时发现x和y的值并没有交换,这是怎么回事?那么在交换函数内部,也将x和y的值打印出来。修改代码如下:

void swap( u8 x, u8 y )
{
    u8 temp;

    printf( "in: x = %d,y = %d\r\n", x, y );
    temp = x;
    x = y;
    y = temp;

    printf( "in: ---> x = %d,y = %d \r\n\r\n\r\n", x, y );
}

打印结果如下:

可以看到在交换函数内部,x和y的值已经交换了,但是在函数外部,x和y的值并没有交换。这是为什么呢?单步调试直接观察x和y在单片机内部分存储情况。

进入主程序之后,首先观察主函数内的x和y在内存中的存储情况,x值为10,也就是16进制的0x0A,在内存中0x000009的位置存储,y的值为20,也就是16进制的0x14,在内存中0x00000B,位置存储。

接下里进入到swap函数中。为了方便观察,将swap函数内部的x和y替换成了m和n。

可以看出在进入子函数之后,m和n的地址和值,都和main函数中的x和y一样。接下来交换m和n的值。

交换完成后发现,内存中0x000009位置的值和0x00000B位置的值亚发生了交换。然后退出子函数,返回到main函数中。

这时候奇怪的事情发生了,刚才内存中交换的值又变回来了?这是为什么呢?在这里就不得不说在C语言中关于局部变量的问题,局部变量在C语言中是没有固定的存储位置的,它是由系统的堆栈统一来管理的,当进入函数内部时,系统就会给这些局部变量临时分配一个存储空间存储它的值,当程序要离开函数时,系统就会将局部变量的值保存在堆栈中,然后变量的存储位置就被释放了。当程序进入另一个函数中时,又会给这个函数内部局部变量分配空间,这时候可能就会出现两个函数中的局部变量都使用了同一个内存空间。此时这个内存空间的值改变后,并不影响上一个函数中局部变量的值,因为上一个函数中的局部变量值此时在堆栈中存放。当程序要离开当前的这个函数时,又会将当前的局部变量值保存在堆栈中。回到上一个函数后,又将堆栈中存储的值恢复给变量,此时变量的地址又是临时申请的,可能此刻申请的地址值还是和上一次一样。但是并不代表这个地址就永远属于这个局部变量。

这个就很类似于超市中的储物柜,你要进去超市买东西,先将东西存到一个柜子中,买完东西后,又将东西存储物柜取了出来。然后隔了几个小时,又要去这个超市买东西,又需要将东西存起来,但是此时存储的柜子编号还是上上一次存储时一样。但是这并不能代表这个柜子的编号就是专属于你的了。它只是储物柜临时分配给你的空间,当你取出东西后这个空间就会被系统收回,如果你下一次还需要用,系统又会自动给你分配,但是这两次分配的刚好是一个编号而已。

那么要如何解决这种变量交换的问题呢?有两种方法,第一种就是直接将要交换的这个两个变量定义为全局变量,让它在程序运行的过程中独占一个地址空间,这样就不会有其他变量来使用这个位置了。但是这样的话就会比较浪费内存空间,只使用了一次,但是却要永久的占用。第二种方法就是直接使用指针。

下面将代码改成使用指针的方式。

void swap( u8 *m, u8 *n )
{
    u8 temp;
    temp = *m;
    *m = *n;
    *n = temp;
}
void main( void )
{
    u8 x = 10, y = 20;
    __asm( "sim" );                             //禁止中断
    SysClkInit();
    delay_init( 16 );
    LED_GPIO_Init();
    Uart1_IO_Init();
    Uart1_Init( 9600 );
    ADC_GPIO_Init();
    __asm( "rim" );                             //开启中断
    while( 1 )
    {
        LED = ~LED;

        printf( "x = %d,y = %d\r\n", x, y );
        sawp( &x, &y );
        printf( "---> x = %d,y = %d \r\n\r\n\r\n", x, y );

        delay_ms( 200 );
    }
}

在向swap函数传递参数的时候需要使用&符号。打印输出结果

此时x和y的值已经成功交换了。现在也将子函数内部的交换情况打印一下。

void swap( u8 *m, u8 *n )
{
    u8 temp;

    printf( "in: m = %d,n = %d\r\n", m, n );
    temp = *m;
    *m = *n;
    *n = temp;

    printf( "in: ---> m = %d,n = %d \r\n\r\n\r\n", m, n );
}

从输出的结果来看,怎么m和n的值一个时1021,一个时1020.这个值是什么呢?直接单步调试看。

此时main函数中x的地址变成了0x0003FD,y的地址变成了0x0003FC.接着进入子函数。

这时可以看出m的值为0x03FD,n的值为0x03FC.*m的值为0x0A也是就10,*0x14也就是20.
接下来开始交换值。

可以看出m和n的值没变,但是m和n的值交换了。接下来回到主函数中。

此时主函数中x和y的值也交换了。

那刚才串口打印出来的1021和1020是什么呢?1021的十六进制是0x03FD,1020的十六进制是0x03FC.也就是说刚才打印的m的值和x的地址一样,n的值和y的地址一样。

那为什么m和n会变成x和y地址,*m 和 *n又会变成 x 和 y 的值。这里就要说指针的本质了。在存储器内部,它是不认识什么变量和指针的,对于存储空间来说,它只有地址和值。也就是说在什么地址处,存储什么值。对于普通的变量来说,变量的名称就会被编译器编译成地址,也就是说x和y就是它自己地址的别名。x和y的值就是地址中对应的值。

当操作普通变量x和y的时候,系统默认操作的就是它的值。而指针刚好和它相反,指针默认是把地址作为它的值,当操作指针的时候,默认操作的就是地址。为了将普通变量和指针进行区分,那么如果要使用指针的时候,就需要给它贴一个标签,告诉系统,我这个是特殊变量,它是直接操作地址的,不是操作值的。

所以在定义指针的时候给变量前面加一个*号,就表示告诉系统,我这个是特殊的。比如定义了一个int *m。就表示告诉系统,当我默认操作m的时候,你就给我它的地址,而不要给我它的值。当需要取值的时候就需要添加上标签 *m,告诉系统我现在要取的值,不是地址,这是特殊情况,不要把默认的地址给我。

当要将普通变量传递给指针时,因为直接操作变量默认就是普通变量的值,而指针存贮的是地址,所以当普通变量和指针传递数据的时候,也要给普通变量添加一个标签 & ,这个符号就告诉系统,我现在不要默认的值,我要的是特殊情况的地址。

所以将x和y传递给指针的时候,前面要加&符号。

swap( &x, &y );对应的就是 swap( u8 *m, u8 *n );

刚才上面不是说了吗,指针默认的是地址,加上*号就是值了。那么这样直接传递过去不就是相当于 *m = &x 了吗?

由于这个子函数是定义和传值在一起操作了,省略了一步,标准操作应该是。

int *m;

m=&x;

先定义一个指针,然后将普通变量的特殊情况,也就是取普通变量的地址,传递给指针的默认情况。这样m的默认情况下就代表的值x的地址,而x的值就是*m。

如果定义变量和给变量赋值在一条语句时,上面的代码就可以简写为

int *m = &x;

所以上面的函数 swap( &x, &y ); 给指针传递值的时候,指针的定义和赋值是在一条语句完成的, swap( u8 *m, u8 *n ); 这是一种常用的简写形式。

在swap函数内部操作 *m 也就相当于直接操作的是x,操作 *n 就是直接操作的y,所以交换 *m 和 *n的值,就相当于交换x和y的值。

在没有使用指针时通过子函数交换,此时传递的是变量的值,相当于把变量的值拷贝了一份,给了子程序。

而有了指针之后,相当于将变量的地址直接给了子函数。相当于给变量x和y又起了一个别名。操作别名的时候,也就相当于直接操作的是x。

由此可见,指针只是为了方便编写程序而设置的一种给变量起别名的方法,也就是相当于给自己柜子配了了一把钥匙。只要别人有这个钥匙,也就可以打开你的柜子。所以指针在使用的时候会有危险性。

如果系统中有关键数据,那么如果这个数据用指针传递给了外部函数,那么当外部函数修改数据的时候,系统就会存在风险。有可能外部函数修改了一个值,而这个值是非法的,自己的系统就奔溃了。所以在使用指针的时候一定要注意安全性问题。

通过上面的例子,相信对指针就有了更深层次的理解了。它是为了方便操作变量而设置的特殊情况,是被贴了标签的变量。至于什么指针变量,变量指针,那都是起的名字而已,搞不清楚这些概念也不用去纠结,只要在使用的时候,知道如何使用就行了。

总结

到此这篇关于C语言小知识之为什么要使用指针的文章就介绍到这了,更多相关C语言使用指针内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C语言入门之指针用法教程

    本文针对C语言初学者详细讲述了指针的用法,并配以实例进行说明.具体分析如下: 对于C语言初学者来说,需要明白指针是啥?重点就在一个"指"上.指啥?指的地址.啥地址?内存的地址. 上面说明就是指针的本质了. 这里再详细解释下.数据存起来是要存在内存里面的,就是在内存里圈出一块地,在这块地里放想放的东西.变量关心的是这块地里放的东西,并不关心它在内存的哪里圈的地:而指针则关心这块地在内存的哪个地方,并不关心这块地多大,里面存了什么东西. 指针怎么用呢?下面就是基本用法: int a, b,

  • 详解C语言结构体中的函数指针

    结构体是由一系列具有相同类型或不同类型的数据构成的数据集合.所以,标准C中的结构体是不允许包含成员函数的,当然C++中的结构体对此进行了扩展.那么,我们在C语言的结构体中,只能通过定义函数指针的方式,用函数指针指向相应函数,以此达到调用函数的目的. 函数指针 函数类型 (*指针变量名)(形参列表):第一个括号一定不能少. "函数类型"说明函数的返回类型,由于"()"的优先级高于"*",所以指针变量名外的括号必不可少.  注意指针函数与函数指针表示

  • C语言指针学习经验总结浅谈

    这篇C语言指针学习经验总结主要是我入职以来学习C指针过程中的点滴记录.文档里面就不重复书上说得很清楚的概念性东西,只把一些说得不清楚或理解起来比较费解的东西做一下讲解,希望能达到以下三个目的 1.通过写这些东西,把我脑袋中关于C的模糊的知识清晰化.2.给初转C的同事们一点提示和帮助.3.也希望各位前辈检查一下文档中是否有理解偏差的地方.1 指针的概念分解      指针是一个特殊的变量,它里面存储的数值被解释成为内存里的一个地址. 要搞清一个指针需要搞清指针的四方面的内容: 1.指针的类型2.指

  • 基于C语言中野指针的深入解析

    "野指针"的成因主要有两种:(1)指针变量没有被初始化.任何指针变量刚被创建时不会自动成为NULL指针,它的缺省值是随机的,它会乱指一气.所以,指针变量在创建的同时应当被初始化,要么将指针设置为NULL,要么让它指向合法的内存.例如 复制代码 代码如下: char *p = NULL;     char *str = (char *) malloc(100); (2)指针p被free或者delete之后,没有置为NULL,让人误以为p是个合法的指针.参见7.5节.别看free和dele

  • 关于C语言指针赋值的问题详解

    一个代码: 复制代码 代码如下: #include<stdio.h>#include<stdlib.h>#define uchar unsigned char#define uint unsigned int void display(uchar *p); char h[4] = {'A','B','C','\0'};char e[4] = {'E','F','L','\0'};char l[4] = {'M','N','O','\0'};char o[4] = {'X','Y',

  • c语言指针之二级指针示例

    二级指针的概念 首先任何值都有地址,一级指针的值虽然是地址,但这个地址做为一个值亦需要空间来存放,是空间就具有地址,这就是存放地址这一值的空间所具有的地址,二级指针就是为了获取这个地址,一级指针所关联的是其值(一个地址)名下空间里的数据,这个数据可以是任意类型并做任意用途,但二级指针所关联的数据只有一个类型一个用途,就是地址,指针就是两个用途提供目标的读取或改写,那么二级指针就是为了提供对于内存地址的读取或改写指针的表现形式是地址,核心是指向关系指针运算符"*"的作用是按照指向关系访问

  • C语言的指针类型详细解析

    指针存储了内存的地址,同时指针是有类型的,如int*,float*,那么,一个自然的猜想就是指针变量应该存储这两方面的信息:地址和指针类型,比如,就像下面的结构体: 复制代码 代码如下: struct pointer{    long address;    int type;} 举个例子:打印sizeof(int*),值为4,可见4字节是存储内存地址用的,反过来就说明指针并没有存储类型信息的地方,那么指针的类型信息存放在哪儿呢?下面剖析一段简单的代码. 复制代码 代码如下: // ma.cpp

  • C语言指针应用简单实例

    C语言指针应用简单实例 这次来说交换函数的实现: 1. #include <stdio.h> #include <stdlib.h> void swap(int x, int y) { int temp; temp = x; x = y; y = temp; } int main() { int a = 10, b = 20; printf("交换前:\n a = %d, b = %d\n", a, b); swap(a, b); printf("交换

  • C语言中函数与指针的应用总结

    1. 首先,在C语言中函数是一种function-to-pointer的方式,即对于一个函数,会将其自动转换成指针的类型. 复制代码 代码如下: #include<stdio.h> void fun(){} int main(void){   printf("%p %p %p\n", &fun, fun, *fun);   return 0;} -------------------------------------------------------------

  • C语言指针详解及用法示例

    新手在C语言的学习过程中遇到的最头疼的知识点应该就是指针了,指针在C语言中有非常大的用处.下面我就带着问题来写下我对于指针的一些理解. 指针是什么? 指针本身是一个变量,它存储的是数据在内存中的地址而不是数据本身的值.它的定义如下: int a=10,*p; p=&a int a=10; int *p=&a; 首先我们可以理解 int* 这个是要定义一个指针p,然后因为这个指针存储的是地址所以要对a取地址(&)将值赋给指针p,也就是说这个指针p指向a. 很多新手都会对这两种定义方法

随机推荐