如何将易失性 typedef 结构转换为非易失性 typedef 结构作为函数的参数

How to cast volatile typedef struct to non-volatile typedef struct as argument of the function

提问人:t1m013y 提问时间:11/5/2023 最后编辑:273Kt1m013y 更新时间:11/5/2023 访问量:96

问:

我正在为 STM32 编写一个 C 库,遇到了一个问题。

我有typedef结构:

typedef struct foo {
    // some struct elements
} foo;

类型为:foo

volatile foo bar;

带有 -typed 参数的函数:foo

int foobar(foo* baz) {
    // some operations with baz
    return 0;
}

当我尝试调用时,出现错误:foobar(&bar);error: invalid conversion from ‘volatile foo*’ to ‘foo*’ [-fpermissive]

如果我投射到 () 会起作用吗?volatile foo*foo* foobar((foo*)&bar);

我试图投射到,但我不知道它是否可以在没有错误的情况下工作。volatile foo*foo*

C 铸造 挥发性

评论

2赞 HolyBlackCat 11/5/2023
为什么不接受一个?通过非易失性指针访问它会像它不是一样工作(我相信除了 UB 之外),这可能是不希望的,否则它不会首先出现?volatile foo *bazvolatilevolatile
0赞 t1m013y 11/5/2023
@HolyBlackCat 因为类型并不总是用作易失性。foo
0赞 HolyBlackCat 11/5/2023
然后我会做一个接受易失性和正常对象。template
1赞 273K 11/5/2023
没有这样的编程语言 C/C++。您可以开发具有 C 接口或 C++ 接口的库。
3赞 HolyBlackCat 11/5/2023
不同时标记 C 和 C++ 是惯例。您可以使用 C++ 功能,或者仅限于 C。如果您仅限于 C,请标记 C,否则标记 C++。

答:

0赞 Pepijn Kramer 11/5/2023 #1

注意:在我创建答案后,标签 C++ 已被删除。

您可以使用模板函数解决此问题,std::enable_it_t 是一个额外的 (SFINAE) 检查,以避免接受除 foo_t 以外的任何内容。 注意:我使用refences而不是指针,因为我希望输入是一个有效的实例(使用指针,你总是必须做额外的nullptr检查)

#include <type_traits>
#include <iostream>

/*typedef*/ struct foo_t  // <== typedef is "C" syntax, not needed
{
};

// any form of foo_t is ok (with or without const, volatile or reference)
template<typename type_t>
constexpr bool is_foo_t()
{
    return std::is_same_v<foo_t,std::remove_cvref_t<type_t>>;
}

template<typename type_t>
auto foobar(const type_t& foo) -> std::enable_if_t<is_foo_t<type_t>(),int>
{
    // reusable code here 
    // just a demo to show both arguments are accepted
    if constexpr(std::is_volatile_v<type_t>)
    {
        std::cout << "Called with volatile foo_t\n";
    }
    else
    {
        std::cout << "Called with non-volatile foo_t\n";
    }

    return 0;
}


int main()
{
    volatile foo_t volatile_foo;
    foobar(volatile_foo);

    foo_t foo;
    foobar(foo);
}

评论

0赞 t1m013y 11/5/2023
我用 C 编写以保存与 C 和 C++ 的兼容性。C 语言中没有模板。
1赞 HolyBlackCat 11/5/2023
@t1m013y 看,这就是为什么我们不喜欢同一个问题上的 C 和 C++ 标签。
0赞 Pepijn Kramer 11/5/2023
是的,当我创建这个答案时,C++标签被删除了。我现在把它留在这里。至少它显示了 C++ 和 C 的实际不同;)
0赞 chux - Reinstate Monica 11/5/2023
@t1m013y “C语言中没有模板”没错,但这里有通用的方法可以解决同样的问题。
0赞 t1m013y 11/5/2023
@chux-恢复莫妮卡 你是说超载吗?
1赞 0___________ 11/5/2023 #2

它不仅是铸造的。如果需要以这种方式强制转换,则意味着代码有问题。

如果要使用不对对象进行参数的函数,则意味着需要将此对象视为不容易产生副作用。函数不必假设编译器不可见的内容可以更改它并应用对象无法实现的优化。最重要的是,对对象的所有访问都必须应用于其永久存储位置。volatilevolatilevolatilevolatile

如果您有此问题,则:

  1. 你过度使用关键字,你的对象不一定是(因为你想把它用作非)。您应该重新考虑您的程序数据类型。volatilevolatilevolatilevolatile

  2. 您可以将函数声明为 take - 但它会阻止许多可能的优化。volatile

  3. 您可以有不同的函数处理和非数据。volatilevolatile

如果我将易失性 foo* 转换为 foo* ( foobar((foo*)&bar);),它会起作用吗?

不。它只会使警告静音,但它可能非常危险。

struct  x
{
    bool dangerous;
    float data;
};

int dangerous(struct x *ptr)
{
    if(!ptr -> dangerous)
    {
        /* do something */
        if(!ptr -> dangerous) never_call_if_dangerous_is_true();
    }
}

int dangerous_volatile(volatile struct x *ptr)
{
    if(!ptr -> dangerous)
    {
        /* do something */
        if(!ptr -> dangerous) never_call_if_dangerous_is_true();
    }
}

以及生成的代码:

dangerous:
        cmp     BYTE PTR [rdi], 0
        jne     .L2
        xor     eax, eax
        jmp     never_call_if_dangerous_is_true
.L2:
        ret
dangerous_volatile:
        mov     al, BYTE PTR [rdi]
        test    al, al
        jne     .L5
        mov     al, BYTE PTR [rdi]
        test    al, al
        jne     .L5
        jmp     never_call_if_dangerous_is_true
.L5:
        ret

在非易失性版本中,跳过第二次检查。如果某些东西(信号、中断)改变了成员,它将调用该函数 - 例如杀死某人dangerous

评论

0赞 0___________ 11/5/2023
如果您正确使用,几乎永远不需要它volatile
0赞 t1m013y 11/5/2023
请解释一下到底什么是危险的?never_call_if_dangerous_is_true()
0赞 t1m013y 11/5/2023
“危险”是否意味着该结构现在正在其他地方使用(例如中断)?