【问题标题】:Generic Pointer dereferencing using a typedefined datatype使用类型定义数据类型的通用指针解引用
【发布时间】:2019-04-24 17:29:41
【问题描述】:

我的代码如下所示

typedef unsigned short  uint16;

struct STR
{
    const int x;
    const uint16 y;
    const int z;
    void* sptr;
};

struct STR s2[2] = 
{
    {1, 4, 6, {&s2[0].x, &s2[0].y, &s2[0].z}},
    {10,40, 60, {&s2[1].x, &s2[1].y, &s2[1].z}}
};

int main()
{
    void* tptr = s2[0].sptr;
    printf("%d %d %d", *((int*)tptr), *( (uint16*)tptr+1 ), *((int*)tptr+2));
    return 0;
}

这里我使用了一个 void 指针,因为我将从结构中区分不同的数据类型。这里我期望得到的输出是

预期输出:1 4 6

但是如果我使用

printf("%d %d %d", *((int*)tptr), *( (uint16*)tptr+1 ), *((int*)tptr+2));

我得到的输出是 1 0 6。这里我使用了一个类型定义的数据类型 uint16 进行延迟。

如果我使用

printf("%d %d %d", *((int*)tptr), *( (int*)tptr+1 ), *((int*)tptr+2));

我得到正确的输出为 1 4 6。为什么 uint16 类型无法取消引用指针并获得 0 值。请任何人提供帮助并提供解决此问题的想法。

【问题讨论】:

  • 您不能以您的方式分配给指针(即sptr)。有关原因,请参阅 Roflcopter4 的答案。我想我理解你最终想要做什么。请在此处查看我的答案:stackoverflow.com/a/52749660/5382650 了解实现目标的不同方法。
  • 是否编译——初始化是否编译?
  • 是的,它可以编译。面临的问题是使用通用指针访问结构元素,因为结构具有不同大小的不同数据类型。因此,根据结构中其他元素的各个数据类型,每个数据位于矩阵中的不同位置。因此,如果我们在结构中添加更多元素或改变现有元素的任何数据类型,这将有所不同。我们可以推导出一些可以解决这个问题的公式。但似乎有点不可能。否则我们应该有一个指针数组,在这种情况下,每个指针分别指向结构的每个元素。
  • tptr 转换为uint16 * 并加1,可能意味着您正在读取x 的高2 个字节。写*( (uint16*)tptr+2 ) 看看会发生什么,尽管这通常不是你写dereferencing的方式。
  • 为什么不使用联合?

标签: c pointers structure void-pointers dereference


【解决方案1】:

我在这里看到了一些问题。首先,您不能声明这样的结构。 void * 变量是一个指针。您试图在其位置定义一个完整的结构,并使用不正确的语法来做到这一点。我很惊讶编译器没有拒绝代码。

您显然需要定义一个结构,该结构也接受 3 个整数指针作为值。如果您真的想像这样一次性声明所有内容,并让第四个元素成为指针,您可以使用这样的复合文字:

#pragma GCC diagnostic ignored "-Wmissing-braces"
struct other_struct {
        const int *x;
        const uint16 *y;
        const int *z;
};
struct STR s2[2] = {
    {1, 4, 6, (struct other_struct[1]){&s2[0].x, &s2[0].y, &s2[0].z}},
    {10,40, 60, (struct other_struct[1]){&s2[1].x, &s2[1].y, &s2[1].z}}
};

请注意,编译器可能会在文件范围内拒绝复合文字(我认为 MSCV 会)。

您似乎也是这里填充的受害者。除非您明确告诉它不要,否则编译器将始终尝试将结构中的数据对齐到机器字大小的倍数(64 位为 8 个字节)。此外,事物通常至少排成 4 字节边界(整数的大小)。因为你所做的是完全未定义的行为,你的编译器只是试图通过给每个元素一个整数的大小来扩展结构。当您尝试查看结构中的下一个 int16 时,您会向前看 2 个字节,但下一个值实际上是向前 4 个字节,因此您得到的值为 0。这也解释了为什么当您转换为 int * 时它会起作用.

编辑:实际上我在上面犯了一个错误。对不起。您的代码没有按预期工作,不一定是因为填充,而是因为您编写指针算法的方式。试图以这种方式在原始数组中查找元素确实是不可能的,而且无论如何肯定是未定义的行为。

【讨论】:

  • 复合文字是允许的文件范围
  • @M.M 我的错。 MSVC 拒绝将它们作为静态变量的初始化程序。 GCC 和 clang 不会,尽管它们会发出警告。我只是假设对静态变量的限制同样适用于全局变量。
  • 静态变量的初始化器必须是常量表达式;复合文字不是常量表达式。 (但它的地址是)
【解决方案2】:

感谢大家的 cmets。在结构的内存矩阵中解决这个未定义的行为问题。我将结构中的 void 指针替换为指针数组,并在双指针的帮助下访问它,现在它工作正常。但它会消耗更多的内存。这样的新代码

typedef unsigned short  uint16;

struct STR
{
    const int x;
    const uint16 y;
    const int z;
    void* sptr[3];
};

struct STR s2[2] = 
{
    {1, 4, 6, {&s2[0].x, &s2[0].y, &s2[0].z}},
    {10,40, 60, {&s2[1].x, &s2[1].y, &s2[1].z}}
};

int main()
{
    void** tptr = s2[0].sptr;
    printf("%d %d %d", *((int*)*tptr), *( (uint16*)*(tptr+1)), *((int*)*(tptr+2)));
    return 0;
}

//Output: 1  4  6

【讨论】:

    猜你喜欢
    • 2011-04-24
    • 2012-07-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多