日志档案

发表于 2008-1-20 10:23:16

0

标签: 无标签

深入了解指向指针的指针

编程的时候,看到下面的一个函数:

eMBErrorCode
eMBASCIIReceive( UCHAR * pucRcvAddress, UCHAR ** pucFrame, USHORT * pusLength )
{
    eMBErrorCode    eStatus = MB_ENOERR;

    ENTER_CRITICAL_SECTION(  );
    assert( usRcvBufferPos < MB_SER_PDU_SIZE_MAX );
    
    /* Length and CRC check */
    if( ( usRcvBufferPos >= MB_SER_PDU_SIZE_MIN )
        && ( prvucMBLRC( ( UCHAR * ) ucASCIIBuf, usRcvBufferPos ) == 0 ) )
    {
        /* Save the address field. All frames are passed to the upper layed
         * and the decision if a frame is used is done there.
         */
        *pucRcvAddress = ucASCIIBuf[MB_SER_PDU_ADDR_OFF];

        /* Total length of Modbus-PDU is Modbus-Serial-Line-PDU minus
         * size of address field and CRC checksum.
         */
        *pusLength = ( USHORT )( usRcvBufferPos - MB_SER_PDU_PDU_OFF - MB_SER_PDU_SIZE_LRC );

        /* Return the start of the Modbus PDU to the caller. */
        *pucFrame = ( UCHAR * ) & ucASCIIBuf[MB_SER_PDU_PDU_OFF];
    }
    else
    {
        eStatus = MB_EIO;
    }
    EXIT_CRITICAL_SECTION(  );
    return eStatus;
}

当看到UCHAR ** pucFrame的时候就优点晕了。不就是传出一个地址吗?干吗还要用指向指针的指针?于是,顺手修改了程序,将那个形参变成了UCHAR * pucFrame,具体的函数也变成了下面的样子:

eMBErrorCode
eMBASCIIReceive( UCHAR * pucRcvAddress, UCHAR * pucFrame, USHORT * pusLength )
{
    eMBErrorCode    eStatus = MB_ENOERR;

    ENTER_CRITICAL_SECTION(  );
    assert( usRcvBufferPos < MB_SER_PDU_SIZE_MAX );
    
    /* Length and CRC check */
    if( ( usRcvBufferPos >= MB_SER_PDU_SIZE_MIN )
        && ( prvucMBLRC( ( UCHAR * ) ucASCIIBuf, usRcvBufferPos ) == 0 ) )
    {
        /* Save the address field. All frames are passed to the upper layed
         * and the decision if a frame is used is done there.
         */
        *pucRcvAddress = ucASCIIBuf[MB_SER_PDU_ADDR_OFF];

        /* Total length of Modbus-PDU is Modbus-Serial-Line-PDU minus
         * size of address field and CRC checksum.
         */
        *pusLength = ( USHORT )( usRcvBufferPos - MB_SER_PDU_PDU_OFF - MB_SER_PDU_SIZE_LRC );

        /* Return the start of the Modbus PDU to the caller. */
        pucFrame = ( UCHAR * ) & ucASCIIBuf[MB_SER_PDU_PDU_OFF];
    }
    else
    {
        eStatus = MB_EIO;
    }
    EXIT_CRITICAL_SECTION(  );
    return eStatus;
}

    编译后运行,却得不到自己想要的结果。只得回来再分析这个函数。首先搜索到CSDN上的一篇文章;http://dev.csdn.net/author/kgdiwss/477390ddb16b44faa06b6c7014908469.html,里面又好好地对指针做了分析。我下面就把自己的理解再写一遍,以加深印象。

1,指针在编译的时候会变成什么?

    如果我们如下声明变量:

    char a;

    shourt int i;

   short int *pi;

    那么编译的时候,编译器会在内存空间的某处为上面的变量开辟存储空间,下面是一个可能的示意图:

    内存地址 1      2       3        4       5        6       7       8        9          10

------------------------------

                 |int a |short int i    |short int *pi  |

    如上所示,int a 占用一个字节,short int i 占用2个字节, short int *pi 占用2个字节。当我们做了如下的赋值的时候:

    i = 88;

    pi = &a;

    内存映象会得到改变:

                 |int a   |     88        |      2        |

    从上面可以看到,pi是个指针,他的值是2,是变量 i 的内存的起始地址。当我们对 *pi 进行操作的时候,其实就是对变量i 进行操作。如 *pi = 66; ,则等价 i = 66;。下面我们再分析指向指针的指针。指针变量本身跟其他变量一样也是在某个内存地址中存储的,我们也可以让一个指针指向这个地址(指针)。看如下代码:

    short int  **ppi;

    ppi = &pi;

    首先声明了一个指向指针的指针变量ppi,这个ppi是用来存储一个short int *类型指针变量的地址的。第二句的意思是pi的地址赋值给ppi,就是将地址值4给ppi,如下图表示:

                 |int a   | short int i  |short int *pi|short int **ppi |

                 |int a   |     88        |      2          |            4         |

    这样,ppi的值为4,就是pi在内存中的起始地址

    *ppi的值为2,是pi的值

    **ppi的值是88,是i的值,也是*pi的值

    有了上面的分析,我们在看上面那个错误的函数:

eMBErrorCode
eMBASCIIReceive( UCHAR * pucRcvAddress, UCHAR * pucFrame, USHORT * pusLength )
{
    eMBErrorCode    eStatus = MB_ENOERR;

    ENTER_CRITICAL_SECTION(  );
    assert( usRcvBufferPos < MB_SER_PDU_SIZE_MAX );
    
    /* Length and CRC check */
    if( ( usRcvBufferPos >= MB_SER_PDU_SIZE_MIN )
        && ( prvucMBLRC( ( UCHAR * ) ucASCIIBuf, usRcvBufferPos ) == 0 ) )
    {
        /* Save the address field. All frames are passed to the upper layed
         * and the decision if a frame is used is done there.
         */
        *pucRcvAddress = ucASCIIBuf[MB_SER_PDU_ADDR_OFF];

        /* Total length of Modbus-PDU is Modbus-Serial-Line-PDU minus
         * size of address field and CRC checksum.
         */
        *pusLength = ( USHORT )( usRcvBufferPos - MB_SER_PDU_PDU_OFF - MB_SER_PDU_SIZE_LRC );

        /* Return the start of the Modbus PDU to the caller. */
        pucFrame = ( UCHAR * ) & ucASCIIBuf[MB_SER_PDU_PDU_OFF];
    }
    else
    {
        eStatus = MB_EIO;
    }
    EXIT_CRITICAL_SECTION(  );
    return eStatus;
}

    重点关注这个语句:pucFrame = ( UCHAR * ) & ucASCIIBuf[MB_SER_PDU_PDU_OFF];,这句话的意思是将形式参数的值进行改变。
    当调用具体函数的时候eMBASCIIReceive( rcvaddress, pFrame, plen )的时候,首先将实参的值等于形参的值,然后函数体内又对形参值进行了改变,而实参pFrame并没有得到改变。如果将形参变量变成了UCHAR ** pucFrame,我们再来分析:

    当函数调用的时候,变成了eMBASCIIReceive( rcvaddress, pFrame, &plen );那么

    pucFrame = &pFrame;

    在函数体内,pucFrame 指向pFrame。对*pucFrame 进行修改,就是对pFrame的值进行修改。

    所有,这儿要传递地址必须采用指向指针的指针才形。

系统分类: 嵌入式   |   用户分类: 读书笔记   |   来源: 整理   |   【推荐给朋友】

    阅读(390)    回复(1)  

投一票您将和博主都有获奖机会!