【问题标题】:Understanding the difference between different C pointer assignment理解不同C指针赋值的区别
【发布时间】:2020-02-20 18:47:02
【问题描述】:

我试图了解两种类型的指针分配之间的区别。

uint8_t pInputByte[byteCount];
uint8_t* pNextInputBytes = pInputByte;

在这种情况下,由于 pInputByte[byteCount] 是一个数组,那么第二行是否将这个数组的地址写入 pNextInputByte?为了澄清只调用没有索引的数组变量名返回它的地址?

我的另一个问题是,如果我们这样写,操作会有什么不同。

uint8_t pInputByte[byteCount];
uint8_t* pNextInputBytes;
pNextInputBytes = &pInputByte;

【问题讨论】:

  • 第二个应该得到编译器警告,因为uint8_t *uint8_t (*)[] 非常不同。后者是指向数组的指针,而不是指向值的指针。
  • 数组声明也为您分配内存,而裸指针需要分配或分配现有值(这是您在第一个示例中正确执行的操作)。

标签: c pointers variable-assignment


【解决方案1】:

表达式中使用的数组指示符(极少数例外)被转换为指向其第一个元素的指针。

来自 C 标准(6.3.2.1 左值、数组和函数指示符)

3 除非它是 sizeof 运算符的操作数或一元 & 运算符,或者是用于初始化数组的字符串文字, 具有“类型数组”类型的表达式被转换为 类型为“pointer to type”的表达式,指向初始 数组对象的元素并且不是左值。如果数组对象 有注册存储类,行为未定义。

因此在此声明中

uint8_t* pNextInputBytes = pInputByte;

用作初始化器的数组被转换为指向其第一个元素的指针,并且类型为int *。这个值被分配给声明的指针。

在此声明中

pNextInputBytes = &pInputByte;

对数组类型的对象应用一元运算符 &。所以表达式&pInputByte 的类型是uinmt8_t ( * )[byteCount]。但是在赋值的左侧有一个uint8_t * 类型的对象。没有从一种类型到另一种类型的隐式转换。所以编译器会报错。

你可以写

uint8_t pInputByte[byteCount];
uint8_t (*pNextInputBytes )[byteCount];
pNextInputBytes = &pInputByte;

为了更清楚,为数组类型引入一个 typedef 名称

typedef uint8_t T[byteCount];

然后你可以通过以下方式声明数组和指向数组的指针

T pInputByte;
T *pNextInputBytes = &pInputByte;

现在如果将指针声明中的 typedef 名称 T 替换为定义的类型,那么您将得到

uint8_t ( *pNextInputBytes )[byteCount] = &pInputByte;

要查看其中一个类型为uint8_t * 和另一个类型为uint8_t ( * )[byteCount] 的两个指针之间的区别,请编写以下演示程序。

#include <stdio.h>
#include <stdint.h>

int main(void) 
{
    size_t byteCount = 10;
    uint8_t pInputByte[byteCount];
    uint8_t *pNextInputBytes1 = pInputByte;
    uint8_t ( *pNextInputBytes2 )[byteCount] = &pInputByte;

    printf( "sizeof( *pNextInputBytes1 ) = %zu\n", sizeof( *pNextInputBytes1 ) );
    printf( "sizeof( *pNextInputBytes2 ) = %zu\n", sizeof( *pNextInputBytes2 ) );

    return 0;
}

程序输出是

sizeof( *pNextInputBytes1 ) = 1
sizeof( *pNextInputBytes2 ) = 10

注意,数组名称中使用的前缀p会混淆代码读者。

【讨论】:

    【解决方案2】:

    1.

    uint8_t pInputByte[byteCount];
    uint8_t* pNextInputBytes = pInputByte;
    

    既然pInputByte[byteCount]是一个数组,那么第二行是否把这个数组的地址写入pNextInputByte

    不是数组的地址,是数组第一个元素的地址。但基本上你是对的。

    为了澄清只是调用没有索引的数组变量名返回它的地址?

    一个数组当不与索引元素[i]一起使用时,会衰减为指向第一个元素的指针并返回这个第一个元素的地址,除了在cmets中提到的用户ex nihilo,当它是an operand to sizeof(), the unary &amp; operator or is a string literal used to initialize another array, an expression that has type ''array of type'' is converted to an expression with type ''pointer to type'' that points to the initial element of the array object and is not an lvalue. .引自 ISO/IEC 9899:2011 (C11)。


    2.

    我的另一个问题是,如果我们这样写,操作会有什么不同:

    uint8_t pInputByte[byteCount];
    uint8_t* pNextInputBytes;
    pNextInputBytes = &pInputByte;
    

    第三行是问题所在。您需要省略 & 运算符 &amp; 才能正确编译而不会出现任何警告:

    pNextInputBytes = pInputByte;
    

    但是如果你把那个错字混合掉,两个代码是等价的,是的。


    顺便说一句,你不应该在pInputByte 前面加上p,因为p 应该代表“指针”(我只能推测)。

    【讨论】:

    【解决方案3】:
    1. uint8_t pInputByte[byteCount]; uint8_t* pNextInputBytes = pInputByte;

    在这个例子中,我们分配了数组的地址,它指向数组中的第一个元素。例如,当您尝试访问数组的第一个元素时,您可以说 *pNextInputBytes 并且对于下一个元素 *(pNextInputBytes+1) - +1 会将您带到数组中第二个元素的地址。

    1. uint8_t pInputByte[byteCount]; uint8_t* pNextInputBytes; pNextInputBytes = &pInputByte;

    在本例中,我们获取数组的地址,但它指向整个数组。即如果 pInputByte[5] 则 &pInputByte 将指向 5 个元素的整个数组。因此,当您尝试像 (pNextInputBytes+1) 一样增加它时,地址实际上会跳转 5 个地址。这在多维数组中很有用。假设 double array[5][4] 是一个二维数组。这里,“array”是指向 4 int 数组的指针,而“&array”是指向 5 行 4 int 数组的数组的指针”

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-10-27
      • 1970-01-01
      • 1970-01-01
      • 2013-05-15
      • 2020-08-17
      相关资源
      最近更新 更多