zoukankan      html  css  js  c++  java
  • 指针的指针(一)

    指针的指针

    我们都知道,在C语言中声明一个变量,可以用同类型的指针指向原先那个变量,指针保存的就是前一个变量的地址:

    int a = 12;
    int *b = &a;
    

      

    它们如下图进行内存分配:

    假定我们又声明了第三个变量叫c,并用c = &b对变量c进行初始化,那么它们在内存中的关系大致如下:

    那么问题来了,c的类型是什么?我们都知道,能保存一个地址的变量类型只有指针,毫无疑问,c也是一个指针,只是,c是一个指向指针的指针,那么c该如何声明呢?这里用**c来声明一个指向指针的指针,如下:

    int a = 12;
    int *b = &a;
    int **c = &b;
    

      

    现在让我们来分析一下**c,*操作符具有从右向左的结合性,所以这个表达式相当于*(*c),我们必须从里向外逐层求值。*c访问的是c变量所保存的地址,这里面保存的是b的地址,即为变量a的地址,所以,*c为变量a的地址,但我们光光知道变量a的地址不够,我们想要获取变量a的值,所以需要在*c的基础上再加上一个*,即为*(*c),即可以获取对变量a的访问

    表达式 相当的表达式
    a 12
    b &a
    *b a,12
    c &b
    *c b,&a
    **c *b,a,12

    指针表达式 

    现在,让我们观察各个不同的表达式,并看看当他们分别作为左值和右值时是如何求值的,首先我们先看下一段声明:

    char ch = 'a';
    char *cp = &ch;
    

      

    表达式:ch

    我们先来看表达式ch这个变量,当ch作为右值时,它表达的是存储的值'a',当ch作为左值时,它就是内存的地址,而不是该地址所包含的值

    表达式:&ch

    &ch作为右值时,代表的是变量ch的内存地址。但它不能作为左值,我们都知道C语言中,数组变量是不能赋值给另外一个数组变量的,因为数组变量就代表地址,地址即为常量,我们不能给常量赋值

    表达式:cp

    cp作为右值时,代表是cp这个地址所存储的值,即为ch的地址。作为左值时,代表的是cp本身的内存地址

    表达式:&cp

    &cp作为右值时,代表的是cp本身的内存地址,而它作为左值是非法的,因为&cp是内存地址,即为常量,我们不能给常量赋值

    表达式:*cp

    *cp作为右值时,代表的是cp指针指向的地址所存储的值,即为'a',作为左值的时,它代表的是ch这个内存地址,如果执行*cp = 'b',则修改的是ch这个地址所存储的值

    表达式:*cp + 1

    *的优先级高于+,所以不管作为左值还是右值,*一定都比+先参与运算。我们先看看作为右值时代表的是什么,我们*cp作为右值时,是ch变量所存储的值,所以是'a',而'a'+1则为'b',所以*cp + 1作为右值时它的结果是'b',然而,*cp + 1不能作为左值,原因很简单,这个表达式的运算结果是'b',是一个常量,常量是不能作为左值的

    表达式:*(cp + 1)

    这个表达式和上一个表达式不同,+在()的里面,所以+先参与表达式的运算,其次才是*,我们先看看作为右值时表达式运算结果是如何的,我们都知道cp存储的是ch的地址,+1则代表指向紧随ch之后的内存地址,然后再通过*间接访问这个地址所存储的值。再来是左值,*(cp + 1)作为左值是危险的,因为*(cp + 1)作为左值时,指向的是紧随ch之后的下一个内存地址,但我们不知道这个地址本身有没有保存值,又或者这个地址有没有其他进程在用,如果是其他进程在使用的话,我们要访问或操作这个内存地址,操作系统是不允许的,会报出错误,但如果这个是地址目前没有人使用或者是本进程在用,那么修改这个地址的值,势必会造成本进程其他功能在运行时因原先的值被修改而出错

    表达式:++cp

    由于cp本身存储的是一个地址,作为右值时,++cp先是将cp指向紧随ch之后的下一个内存地址,然后再返回指针的一份拷贝,而++cp是不能作为左值的,因为++cp是一个常量,常量不能作为左值

    表达式:cp++

    cp++作为右值时,首先先返回cp指针的一份拷贝,然后再将cp本身所存储的地址+1,指向紧随ch之后的下一个内存地址,而cp++不能作为作为,原因同上

    表达式:*++cp

    表达式在运算时,是从右到左,所以++的优先级会高于*,当*++cp作为右值时,cp指向ch的下一个内存地址,然后再返回cp指针的一份拷贝,接着*运算,间接访问ch下一个内存地址所存储的值。当*++cp作为左值时,则指向的是ch下一个内存地址

    表达式:*cp++

    这里依旧是++先于*参与运算,作为右值时,cp++首先返回的是cp指针的一份拷贝,即为ch地址,其次才是对cp地址所存储的值+1,指向ch之后的内存地址,然后是*,*接收到的是ch地址,所以通过间接访问,访问到的是ch地址所存储的值,虽然这个时候cp指针指向的已经是ch下一个内存地址了。而作为左值时,*cp++依旧指向的是ch这个地址

    表达式:++*cp

    这里,*与++先参与运算,先来看看表达式作为右值时是如何运算的,表达式会对cp所指向地址的值+1,然后返回增值后的值的一份拷贝,即为'b',而++*cp不能作为左值,因为这个表达式的运算结果返回的是一个常量

    #include <stdio.h>
    
    int main(int argc, char const *argv[])
    {
        char ch = 'a';
        char *cp = &ch;
        printf("++*cp = %c
    ", ++*cp);
        printf("ch = %c
    ", ch);
        printf("++*cp = %c
    ", ++*cp);
        printf("ch = %c
    ", ch);
        printf("++*cp = %c
    ", ++*cp);
        printf("ch = %c
    ", ch);
        return 0;
    }
    

      

    运行结果:

    # gcc main.c -o main
    # ./main 
    ++*cp = b
    ch = b
    ++*cp = c
    ch = c
    ++*cp = d
    ch = d
    

      

    表达式:(*cp)++

    作为右值,*先参与运算,所以*cp为指向ch地址的值,其次是++,先返回ch地址保存的值的拷贝,再对ch地址保存的值+1,即为'b',而它作为左值是非法的。

    表达式:++*++cp

    当这个表达式作为右值时,首先++cp会先将cp指针指向ch下一个内存地址,然后返回一份cp的拷贝,再用*进行间接访问,获取当前cp保存的地址的值,注意:这里cp已经指向ch之后的地址了,再来就是++,对指向地址的值+1,。而这个表达式是不能作为左值的

    我们来验证一下这个表达式作为右值时的行为:

    #include <stdio.h>
    
    int main(int argc, char const *argv[])
    {
        int i = 0;
        char seq[] = {'a', 'b', 'e'};
        char *cp = seq;
        printf("++*++cp = %c
    ", ++*++cp);
        for (; i < sizeof(seq) / sizeof(char); i++)
        {
            printf("seq[%d] = %c
    ", i, seq[i]);
        }
        return 0;
    }
    

      

    运行结果:

    # gcc main.c -o main
    # ./main 
    ++*++cp = c
    seq[0] = a
    seq[1] = c
    seq[2] = e
    

      

    可以看到,这个表达式作为右值时,他的确是把原先指向的值的下一个内存单元所保存的值+1

    表达式:++*cp++

    这个表达式作为右值时,我们先看cp++,它会返回一个cp指针的拷贝,然后对cp指针所保存的值+1,即指向ch之后的地址,再来就是*运算,进行间接访问,这里的*cp++访问的还是ch地址所保存的值,尽管这时候cp已经指向ch之后的内存地址了,然后是++*cp++,*cp++指向的是ch地址所保存的值,即为'a',++即为先对ch地址所保存的值+1,然后再返回一份对其值的拷贝,同样,这个表达式不能作为左值

    让我们验证一下这个表达式的运算结果:

    #include <stdio.h>
    
    int main(int argc, char const *argv[])
    {
        int i = 0;
        char seq[] = {'a', 'b', 'e'};
        char *cp = seq;
        printf("++*cp++ = %c
    ", ++*cp++);
        for (; i < sizeof(seq) / sizeof(char); i++)
        {
            printf("seq[%d] = %c
    ", i, seq[i]);
        }
        return 0;
    }
    

      

    运算结果:

    # gcc main.c -o main
    # ./main 
    ++*cp++ = b
    seq[0] = b
    seq[1] = b
    seq[2] = e
    

      

  • 相关阅读:
    VirtualBox+Windbg 进行双机调试的方法
    (串口通信编程) 开源串口调试助手Common (Com Monitor)
    Win32SDK中(串行)通信资源概要(不断更新)
    对WDK中LIST_ENTRY的遍历
    用C语言写的一个控制台界面的通讯录管理系统
    我对Windows桌面任务栏自动隐藏功能的一点小小改进不再自动弹出(20130226更新)
    对WDK中对LIST_ENTRY的操作的相关函数的实现及简单运用
    我对CONTAINING_RECORD宏的详细解释
    基于51单片机实现模拟IIC总线时序
    uploadfy 常见问题收集
  • 原文地址:https://www.cnblogs.com/beiluowuzheng/p/9231434.html
Copyright © 2011-2022 走看看