当前位置: 首页 > 知识库问答 >
问题:

强制转换结构指针

郑宜民
2023-03-14

假设代码是用c11编译的,并且启用了严格别名。

我不是在寻找一种不同的方法,我想专注于这个具体的问题,以及它是否可行或为什么不可行。

(如果我无意中犯了一些无关的错误,请告诉我,我会改正的)

C11标准说:

6.2.5.28所有指向结构类型的指针应具有彼此相同的表示和对齐要求。

6.7.2.1.6结构是由成员序列组成的类型,其存储按有序顺序分配

这意味着结构A和B中指针的大小和对齐方式相同。

#include <stdio.h>
#include <stdlib.h>

struct S1
{
    int i ;
} ;

struct S2
{
    float f ;
} ;

struct A
{
    struct S1* p ;
} ;


struct B
{
    struct S2* p ;
} ;


int main( void )
{

结构 A 和 B 具有指向结构 S1 和 S2 的指针,并且结构 A 和 B 保证具有相同的大小和对齐方式。

我们有一个< code>struct B,它的成员指针是一个struct S2指针,但它指向某个struct S1,这是通过void* cast实现的。

struct S1 s1 = { 0 } ;

struct B* b = malloc( sizeof( *b ) ) ;
b->p = ( void* ) &s1 ;

没关系,我们可以存储指针,只要我们不实际使用指针。但是我们想。我们可以将指针强制转换为结构S1。

( ( struct S1* )(b->p) )->i = 123 ;    //redundant brackets for emphasis

printf("%d\n" , s1.i ) ;

并正确使用。

到目前为止,我没有看到任何问题,因为指针被转换到正确的类型。

但是,我们可以将整个结构 B 转换为结构 A 吗?它们在大小和对齐方式上是相同的,尽管标准可能会抱怨(?),编译器是否会产生未定义的行为?

( ( struct A* )b)->p->i = 666 ;

printf("%d\n" , s1.i ) ;

我知道解决方案是使用联合(或使用void并且随时正确投射),因为标准允许使用上次未用于存储值的成员。

6.5.2.3.3( 95)如果用于读取联合对象内容的成员与上次用于在对象中存储值的成员不同,则值的对象表示的适当部分将被重新解释为6.2.6中描述的新类型的对象表示(该过程有时称为“类型双关”)。这可能是一个陷阱表示。

但是,我想避免这种情况:

struct C
{
    union
    {
        struct S1* p1 ;
        struct S2* p2 ;
    } ;
} ;

struct C* c = malloc( sizeof( *c ) ) ;

c->p2 = ( void* )&s1 ;

c->p1->i = 444 ;

printf("%d\n" , s1.i ) ;

return 0 ;
}

上面的代码没有文本。

共有3个答案

鲁杜吟
2023-03-14

我认为在这种特殊情况下,您的示例将起作用,并且符合标准。ANSI标准说:

A pointer to a structure object, suitably
cast, points to its initial member (or if that member is a bit-field,
then to the unit in which it resides), and vice versa.  There may
therefore be unnamed holes within a structure object, but not at its
beginning, as necessary to achieve the appropriate alignment.

在您的示例中,指针p始终是结构的第一个(也是唯一的)字段。根据我对上一段的理解,指向结构A的指针与指向A::p的指针相同(请原谅我的C符号),这与指向B::p的指针相同,这与指向B的指针相同。显式转换不会改变指针的值,因此您的示例应符合标准。

说它不是很漂亮是没用的,你的老板可能不会欣赏这种编程风格。

薛墨一
2023-03-14

在表达式< code>((struct A *) b)中-

诸葛柏
2023-03-14

到目前为止你所描述的:

但是我们能把整个结构B转换成结构A吗?

是完全正确的,但不幸的是,这个问题的答案是否定的。只有当两个结构包含“公共初始序列”,即它们的前几个成员具有相同的类型时,才允许通过指向不兼容类型的指针访问结构。由于您的结构没有(即,第一个成员是不同类型的),因此通过指向S2的指针访问S1类型的对象是不合法的,反之亦然。特别是,这样做违反了严格的混淆现象。

来自C99,6.5.7:

对象的存储值只能由具有以下类型之一的左值表达式访问:76)

-与对象的有效类型兼容的类型,

-与对象的有效类型兼容的类型的限定版本,

—对应于对象有效类型的有符号或无符号类型,

-对应于对象有效类型的限定版本的有符号或无符号类型的类型,

— 在其成员中包括上述类型之一的聚合或联合类型(以递归方式包括子聚合或包含的联合的成员),或

— 字符类型。

 类似资料:
  • 考虑以下代码: 我必须定义一个回调函数,我想在该函数中使用一些附加信息。我定义了自己的结构并将成员的地址传递给函数。在函数中,我想通过强制转换“检索”整个结构,但指针似乎不匹配,我得到了错误的结果。我想我在选演员时做错了什么,但我不确定是什么?

  • 我有以下结构: void*arr保存指向内存中某些单元格的指针。每个单元格都有一个指向内存中另一个单元格的空指针(作为链表)、一个char*字符串和一个具有自己大小的值。 编辑--因为它是一个指针链表,难道我不能也做一些类似于-map->arr+index*sizeof(Void*)的事情来获得给定单元格的开始吗?

  • 指针变量数据类型的强制转换 必须显式强制类型转换,不允许隐式类型转换 指向空间的强制类型转换,本质上就是普通变量的强制类型转换 int a = 10; float b = 3.14; int *pa = &a; float *pb = &b; *pa = (int)*pb; // 等价于 a = (int)b; 指针本身强制类型转换,改变的是对其指向空间的引用方式(空间大小和存储结构) int

  • 问题内容: 如果我有: 上面是固定的-我不能更改foo或bar。另外,baz必须在bar内转换回foo struct指针。如何将&foo {}强制转换为interface {},以便在调用bar时可以将其用作参数? 问题答案: 要打开到一个很简单: 为了返回到,您可以执行 类型断言 : 或 类型开关 (类似,但可以是多种类型则很有用):

  • 隐含的强制转换 隐含的 强制转换是指这样的类型转换:它们是隐藏的,由于其他的动作隐含地发生的不明显的副作用。换句话说,任何(对你)不明显的类型转换都是 隐含的强制转换。 虽然 明确的 强制转换的目的很明白,但是这可能 太过 明显 —— 隐含的 强制转换拥有相反的目的:使代码更难理解。 从表面上来看,我相信这就是许多关于强制转换的愤怒的源头。绝大多数关于“JavaScript强制转换”的抱怨实际上都

  • 明确的强制转换 明确的 强制转换指的是明显且明确的类型转换。对于大多数开发者来说,有很多类型转换的用法可以清楚地归类于这种 明确的 强制转换。 我们在这里的目标是,在我们的代码中指明一些模式,在这些模式中我们可以清楚明白地将一个值从一种类型转换至另一种类型,以确保不给未来将读到这段代码的开发者留下任何坑。我们越明确,后来的人就越容易读懂我们的代码,也不必费太多的力气去理解我们的意图。 关于 明确的