【问题标题】:Struct pointers and their initialization结构指针及其初始化
【发布时间】:2022-01-07 05:24:09
【问题描述】:

我在 C 中有一个结构如下:

typedef struct ENETPACKET_S {
    unsigned    n_rxcmd, n_txcmd;
    uint64_t    n_mac;  // 8 bytes unsigned or 64 bit
    unsigned    n_rxmiss, n_rxerr, n_rxcrc, n_txcol;
} ENETPACKET;

下面这个结构的变量定义了一个指针:

 static volatile ENETPACKET *const _net1 = ((ENETPACKET *)0x00500000);

我有两个疑问:

  1. 我理解指向结构的指针,但在_net1 的声明中,定义了来自结构的变量/对象的指针。我不明白。

  2. 我对@9​​87654325@ 指针的((ENETPACKET *)0x00500000) 初始化感到困惑,我不明白这里初始化了什么以及括号和* 运算符。

【问题讨论】:

  • 简单来说,可以把(ENETPACKET *)看作ENETPACKET地址变量类型。所以总是把ENETPACKET * 当作变量类型的符号。
  • 我看到的是一个具有七个成员的结构类型别名,以及一个静态 volatile const 指针,指向其中一个名为 _net1 的结构,它被硬初始化为似乎是特定内存地址的地址(对于像虚拟设备驱动程序这样的东西并不少见,其中真实或虚拟硬件将 IO 区域映射到特定的记录内存地址)。我不确定你在说什么 (1)。
  • 电脑设备上的地址是什么?它表示一个长的二进制或十六进制数。所以将十六进制数转换为地址变量类型就是引用计算机上的地址。
  • @WhozCraig 感谢您解决这个问题!我错过了结构的 typedef 部分,因此清除了我的第 1 点。也感谢您清除我的问题的第 2 部分。这里正在初始化指针的值,我将它与正在初始化的结构内的某个变量的值混合在一起。

标签: c pointers struct


【解决方案1】:

您似乎正在使用可能带有微控制器的嵌入式设备。在这样的系统中,可以通过设置一些寄存器来配置/使用一些外围设备。这些寄存器可以直接读取/写入,因为它是您的 C 程序可用的内存。

举一个小例子来说明它的样子:

/* Control the PIN10 (input/output) */
/* The CONTROLLER of PIN10 is mapped at the address 0x00400000 */
static volatile uint8_t * const PIN10_CTRL = 0x00400000;

/* PIN10 control the output PIN of the MCU who is connected to a LED */
/* The STATUS of the PIN10 is mapped at the address 0x00400001 */
static volatile uint8_t * const PIN10 = 0x00400001;

*PIN10_CTRL = 0x1; /* Configure the PIN10 as a output PIN */

/* Blink the LED every second */
while (1)
{
  *PIN10 = 1;
   sleep(1);
  *PIN10 = 0;
   sleep(1);
}

如果我们现在考虑您的代码:

static volatile ENETPACKET *const _net1 = ((ENETPACKET *)0x00500000);

似乎你的微控制器的网络接口正在将ENETPACKET 的结果放在地址0x00500000。这个地址是固定的,可以在你的微控制器的用户手册中找到。

结构是volatile,以防止编译器对他将生成的二进制代码做任何假设。如果省略了关键字 volatile,您可能会对程序进行一些优化,并且数据会过时。

这个地址0x00500000可能是N x ENETPACKET数据包数组的地址。

【讨论】:

  • 这正是我正在做的事情(在嵌入式设备上工作)。您编写的示例代码帮助大量了解我的代码正在执行的整个内存映射概念。谢谢。
  • 不客气!一个小例子总是有帮助的,LED 闪烁示例是嵌入式世界中的一种问候世界,因为很容易获取 PIN 输出并使用它来了解资源有限平台上发生的情况。通常,您将其连接到示波器并在您正在调试的事件发生时切换 PIN。这很方便。
【解决方案2】:

对于你的第一个问题(这有点不清楚),也许考虑如何声明和初始化指向 simple 对象的指针:

int i; // declares "i" as an "int"
int* p; // declares "p" as a "pointer to an int"
int* pi = &i; // declares "pi" as a pointer-to-int and initializes with address of "i"

同样,去掉各种限定符:

ENETPACKET str; // Declares "str" as an actual ENETPACKET structure
ENETPACKET *pstr = &str; // pstr is a pointer-to-structure set to address of "str"

现在,const 限定符* 之后(以及名称之前)意味着您的代码不能(不得)更改值(地址)在初始化时分配给_net1 变量,但指向的实际数据(即结构及其成员的值)可以更改:指针是const,而不是引用的数据;据推测,这是因为分配的值是硬件中的特定位置。

第二点,常量前面的(ENETPACKET*)cast operator;没有它,您的编译器会将0x00500000 视为一个整数 常量,并生成如下警告(来自clang-cl):

警告:不兼容的整数到指针转换初始化 'volatile ENETPACKET *const'(又名'volatile struct ENETPACKET_S *const') 带有 'int' 类型的表达式 [-Wint-conversion]

添加 explicit 类型会告诉编译器:“是的,我知道类型不一样,但我知道我在做什么,并且希望您将它们视为兼容的。”谨慎使用演员表!

第二个(外部)括号实际上不是必需的,但作者为了清楚起见添加了(这是一件好事);如果使用的值从常量/文字(目前如此)更改为涉及指针算术的更复杂的表达式,或者在(可能更可能)宏是用于代替固定文字。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-08
    相关资源
    最近更新 更多