强制类型转换中const限定符的必要性

3
假设我有这样一段代码:
void foo (void *ptr) {
    const struct some *azaza = (const struct some *)ptr;
    azaza->some_field = 123; // inadvertently assignment => error
    // ...

const关键字在类型转换中真的有必要吗?


编辑: foo是一个回调函数,具有特定原型,我无法更改它(它不在我的代码中)。


1
在函数中,当不允许出现像 azaza->some_field = 123; 这样的语句时,这确实是必需的。 :) - Vlad from Moscow
4个回答

6
在你的特殊情况下,你甚至不需要进行强制转换。在 void * 的情况下,只有在解引用时才需要进行转换。所以只需要这样做就足够了:
const struct some *azaza = ptr;

为了增加趣味性,

#gcc test.c命令:

// 1)
const struct some *azaza = ptr;
azaza->some_field = 123; // error: assignment in read-only object

// 2)
struct some *azaza = (const struct some *)ptr; // (!)warning: initialization discards ‘const’ qualifier

azaza->some_field = 123;

#gcc test.c -pedantic-errors:

// 1)
const struct some *azaza = ptr;
azaza->some_field = 123; // error: assignment in read-only object

// 2)
struct some *azaza = (const struct some *)ptr; // error: initialization
                                               // discards ‘const’ qualifier

azaza->some_field = 123;

总之:

  1. 在类型转换中不需要使用 const
  2. 如果你仅在类型转换中使用 const(出于一些奇怪的原因 ._.),并且没有开启 -pedantic-errors 选项 - 意外赋值将不会导致错误,这可能带来后果。

0
  1. 如果指针 ptr 指向的值被声明为 const,那么你试图做的是未定义行为。你不应该更改 const 值。你可以使用强制类型转换去除 const,并且仅在最初没有声明为 const 的情况下修改值。

    int modify(void *ptr) {
        some_t *value = ptr;
        value->member = 0; // 这里可能会发生 UB
    }
    
    int main(void)
    {
       some_t obj0 = {0};
       const some_t obj1 = {0};
       modify(&obj0); // 正常
       modify(&obj1); // 导致未定义行为
       return 0;
    }
    
  2. 否则,如果它没有被声明为 const,为什么不直接这样写: struct some *azaza = ptr;


"...是未定义行为" - 不,这不是有意的赋值。我指的是无意中的赋值,而const正是警告我们避免这种情况发生的。 - z0lupka
@z0lupka,我真的很难理解你的代码应该做什么以及它所涉及的问题。一方面,该函数接受非常量指针,而在内部添加const,然后尝试更改引用值。是的,代码不应该编译。 - Volodymyr Boiko
请阅读我的评论,关于非常量函数参数的 El Stepherino 的回答。 - z0lupka
@z0lupka 我在谈论修改一个由类型为“void *”的函数参数指向的对象。任何试图修改其类型为const限定符的对象都会导致未定义的行为。 - Volodymyr Boiko

0

这取决于您想要做什么。您的代码片段之所以令人困惑,是因为:

该函数接受非const指针,这意味着函数可能希望更改“ptr”所指向的内容。

然后,在进入函数时,您将“ptr”强制转换为const指针。因此,编译器现在相信函数没有修改ptr所指向的内容的意图。因此,当程序调用以下命令时出现错误是正常的:

azaza->some_field = 123; // not allowed !

根据您的意图,函数可以以以下两种形式之一编写:
选项#1:函数不打算修改指向 ptr 的内容:
void foo( const void * ptr ) {
    const struct some * azaza = (const struct some *) ptr ;
    // ... do something other than writing to 'azaza', for example:
    printf("Some field: %d\n", azaza->some_field ) ;

}

选项2:此函数确实意图修改指针ptr所指向的内容:

void foo(void * ptr ) {
    struct some * azaza = (struct some *) ptr ;
    // ... write to 'azaza', for example:
    azaza->some_field = 123 ;
}

该函数接受一个非const指针void *。这种情况通常用于使用特定原型的回调函数。因此,关于非const参数并没有什么混淆的地方,不是吗? :) - z0lupka

0

如果我理解你的问题,你想要通过const来保护实例,你在问的是写下以下代码是否足够:

  const struct some *azaza = (struct some *)ptr;

与其

const struct some *azaza = (const struct some *)ptr;

我认为这是可以的,因为const属性可以被编译器自动应用,但它不能被取消。如果这是C++,那么这将是最小充分的代码,但正如其他人所提到的,在C中写入以下内容就足够了:

const struct some *azaza = ptr;

由于C允许将void指针转换为任何其他指针类型,然后可以推断出constness。但是,我认为C++的规定使代码更清晰易读。

请注意,如果您已经有一个const指针,则无法将其分配给非const指针而不“强制转换”const。为了匹配C++规范,我倾向于放入2个转换,尽管C会接受其中任何一个:

void foo (const void *ptr) {
  struct some *azaza = (struct some *)(void*)ptr;
  azaza->some_field = 123; // intentional overwrite!
  // ...

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接