何时使用const void*?

Mon*_*all 32 c pointers type-safety

我有这个非常简单的测试函数,我正在使用它来弄清楚const限定符发生了什么.

int test(const int* dummy)
{
   *dummy = 1;
   return 0;
}
Run Code Online (Sandbox Code Playgroud)

这个问题让我对GCC 4.8.3产生了错误.然而这个编译:

int test(const int* dummy)
{
   *(char*)dummy = 1;
   return 0;
}
Run Code Online (Sandbox Code Playgroud)

因此,似乎const限定符只有在我使用参数而不转换为其他类型时才有效.

最近我见过使用的代码

test(const void* vpointer, ...)
Run Code Online (Sandbox Code Playgroud)

至少对我来说,当我使用void*时,我倾向于将它转换为char*以用于堆栈中的指针算法或跟踪.const void*如何阻止子程序函数修改vpointer指向的数据?

bol*_*lov 46

const int *var;
Run Code Online (Sandbox Code Playgroud)

const合同.通过接收const int *参数,您"告诉"调用者您(被调用函数)不会修改指针指向的对象.

您的第二个示例通过抛弃const限定符然后修改接收指针指向的对象来显式中断该合约.从来没有这样做过.

这个"合同"由编译器强制执行.*dummy = 1不会编译.通过告诉编译器你真的知道自己在做什么并让你这样做,演员是一种绕过它的方法.不幸的是,"我真的知道我在做什么"通常不是这样.

const 也可以由编译器用来执行否则无法进行的优化.


未定义的行为说明:

请注意,虽然转换本身在技术上是合法的,但修改声明为constUndefined Behavior 的值.所以从技术上讲,原始函数是可以的,只要传递给它的指针指向声明为可变的数据.否则它是未定义的行为.

在帖子的最后更多关于这一点


至于动机和使用让我们采取的参数strcpymemcpy功能:

char* strcpy( char* dest, const char* src );
void* memcpy( void* dest, const void* src, std::size_t count );
Run Code Online (Sandbox Code Playgroud)

strcpy对char字符串进行memcpy操作,对通用数据进行操作.虽然我用的strcpy作为例子,下面的讨论正是上是相同的,但char *const char *strcpyvoid *const void *memcpy:

destchar *因为在缓冲区dest中函数会放置副本.该函数将修改此缓冲区的内容,因此它不是const.

srcconst char *因为该函数只读取缓冲区的内容src.它不会修改它.

只有通过查看函数的声明,调用者才能断言以上所有内容.通过契约strcpy不会修改作为参数传递的第二个缓冲区的内容.


const并且void是正交的.这是所有关于以上的讨论const适用于任何类型(int,char,void,...)

void * 在C中用于"通用"数据.


更多关于未定义的行为:

情况1:

int a = 24;
const int *cp_a = &a; // mutabale to const is perfectly legal. This is in effect
                      // a constant view (reference) into a mutable object

*(int *)cp_a = 10;    // Legal, because the object referenced (a)
                      // is declared as mutable
Run Code Online (Sandbox Code Playgroud)

案例2:

const int cb = 42;
const int *cp_cb = &cb;
*(int *)cp_cb = 10;    // Undefined Behavior.
                       // the write into a const object (cb here) is illegal.
Run Code Online (Sandbox Code Playgroud)

我从这些例子开始,因为它们更容易理解.从这里只有一步功能参数:

void foo(const int *cp) {
    *(int *)cp = 10;      // Legal in case 1. Undefined Behavior in case 2
}
Run Code Online (Sandbox Code Playgroud)

情况1:

int a = 0;
foo(&a);     // the write inside foo is legal
Run Code Online (Sandbox Code Playgroud)

案例2:

int const b = 0;
foo(&b);     // the write inside foo causes Undefined Behavior
Run Code Online (Sandbox Code Playgroud)

我必须再次强调:除非你真的知道你在做什么,所有在现在和未来工作的人都是专家并理解这一点,你有一个好的动力,除非满足以上所有要求,从不抛弃了常量!

  • @bolov:FWIW,具有"正交"的版本实际上比具有"未耦合"的版本更好(更清晰).在任何拥有大量受过良好教育,技术头脑的人的网站上,你会遇到很多关于如何"假设"使用单词或者"实际"含义的说法.其中许多说法是完全错误的; 而David C. Rankin就是其中之一. (8认同)
  • @MoneyBall可以肯定地说,C给你提供了在腿上射击自己的方法(允许抛弃const限定符就是其中之一).话虽如此,假设一个函数不会抛弃const限定符是安全的.除非它是恶意或错误的功能. (5认同)
  • @ DavidC.Rankin英语不是我的第一语言,所以我的使用可能不对,但"正交"也有"独立"的含义http://www.oxforddictionaries.com/definition/american_english/orthogonal,http: //www.merriam-webster.com/dictionary/orthogonal (4认同)
  • @MoneyBall我添加了关于执行此合同的编译器的注释 (2认同)

edm*_*dmz 7

int test(const int* dummy)
{
   *(char*)dummy = 1;
   return 0;
}
Run Code Online (Sandbox Code Playgroud)

不,这不起作用.抛弃constness(使用真正的const数据)是未定义的行为,例如,如果实现将const数据放入ROM中,您的程序可能会崩溃."它有效"这一事实并没有改变您的代码格式错误的事实.

至少对我来说,当我使用void*时,我倾向于将它转换为char*以用于堆栈中的指针算法或跟踪.const void*如何阻止子程序函数修改vpointer指向的数据?

A const void*表示指向某些无法更改的数据的指针.为了阅读它,是的,你必须将它转换为具体的类型,如char.但是我说读书,而不是写作,这也是UB.

这涉及更深入这里.C允许您完全绕过类型安全:防止这种情况是您的工作.

  • 实际上抛弃constness并不是未定义的行为.即使修改了指向的值,它也被明确定义,条件是对象被声明为可变的.有关详细示例,请参阅我的答案.但我100%同意这不是允许抛弃常数的理由 (3认同)