C语言 何时使用 const void*?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/34842224/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 12:18:23  来源:igfitidea点击:

When to use const void*?

cpointerstype-safety

提问by MoneyBall

I have this very simple test function that I'm using to figure out what's going on with const qualifier.

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

int test(const int* dummy)
{
   *dummy = 1;
   return 0;
}

This one throws me an error with GCC 4.8.3. Yet this one compiles:

这个给我带来了 GCC 4.8.3 的错误。然而,这个编译:

int test(const int* dummy)
{
   *(char*)dummy = 1;
   return 0;
}

So it seems like the const qualifier works only if I use the argument without casting to other type.

所以似乎 const 限定符只有在我使用参数而不转换为其他类型时才有效。

Recently I've seen codes that used

最近我看到了使用的代码

test(const void* vpointer, ...)

At least for me, when I used void*, I tend to cast it to char*for pointer arithmetic in stacks or for tracing. How can const void*prevent subroutine functions from modifying the data at which vpointeris pointing?

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

回答by bolov

const int *var;

constis a contract. By receiving a const int *parameter, you "tell" the caller that you (the called function) will not modify the objects the pointer points to.

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

Your second example explicitly breaks that contractby casting away the const qualifier and then modifying the object pointed by the received pointer. Never ever do this.

您的第二个示例通过丢弃 const 限定符,然后修改接收到的指针所指向的对象显式地破坏了该合同。永远不要这样做。

This "contract" is enforced by the compiler. *dummy = 1won't compile. The cast is a way to bypass that, by telling the compiler that you really know what you are doing and to let you do it. Unfortunately the "I really know what I am doing" is usually not the case.

这个“契约”是由编译器强制执行的。*dummy = 1不会编译。演员表是一种绕过这一点的方法,它告诉编译器你真的知道你在做什么并让你去做。不幸的是,“我真的知道我在做什么”通常并非如此。

constcan also be used by compiler to perform optimization it couldn't otherwise.

const编译器也可以使用它来执行其他情况下无法执行的优化。



Undefined Behavior note:

未定义行为注意:

Please note that while the cast itself is technically legal, modifying a value declared as constis Undefined Behavior. So technically, the original function is ok, as long as the pointer passed to it points to data declared mutable. Else it is Undefined Behavior.

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

more about this at the end of the post

在帖子末尾有更多关于这个的信息



As for motivation and use lets take the arguments of strcpyand memcpyfunctions:

至于动机和用途,让我们采用strcpymemcpy函数的参数:

char* strcpy( char* dest, const char* src );
void* memcpy( void* dest, const void* src, std::size_t count );

strcpyoperates on char strings, memcpyoperates on generic data. While I use strcpy as example, the following discussion is exactly the same for both, but with char *and const char *for strcpyand void *and const void *for memcpy:

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

destis char *because in the buffer destthe function will put the copy. The function will modify the contents of this buffer, thus it is not const.

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

srcis const char *because the function only reads the contents of the buffer src. It doesn't modify it.

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

Only by looking at the declaration of the function, a caller can assert all the above. By contract strcpywill not modify the content of the second buffer passed as argument.

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



constand voidare orthogonal. That is all the discussion above about constapplies to any type (int, char, void, ...)

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

void *is used in C for "generic" data.

void *在 C 中用于“通用”数据。



Even more on Undefined Behavior:

更多关于未定义行为:

Case 1:

情况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

Case 2:

案例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.

I began with these examples because they are easier to understand. From here there is only one step to function arguments:

我从这些例子开始是因为它们更容易理解。从这里到函数参数只有一步:

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

Case 1:

情况1:

int a = 0;
foo(&a);     // the write inside foo is legal

Case 2:

案例2:

int const b = 0;
foo(&b);     // the write inside foo causes Undefined Behavior

Again I must emphasize: unless you really know what you are doing, and all the people working in the present and in the future on the code are experts and understand this, and you have a good motivation, unless all the above are met, never cast away the constness!!

我必须再次强调:除非你真的知道你在做什么,并且所有在现在和将来从事代码工作的人都是专家并且理解这一点,并且你有很好的动力,除非满足以上所有条件,否则永远不会抛弃恒常!!

回答by edmz

int test(const int* dummy)
{
   *(char*)dummy = 1;
   return 0;
}
int test(const int* dummy)
{
   *(char*)dummy = 1;
   return 0;
}

No, this does not work. Casting away constness (with truly constdata) is undefined behaviorand your program will likely crash if, for example, the implementation put constdata in ROM. The fact that "it works" doesn't change the fact that your code is ill-formed.

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

At least for me, when I used void*, I tend to cast it to char* for pointer arithmetic in stacks or for tracing. How can const void* prevent subroutine functions from modifying the data at which vpointer is pointing?

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

A const void*means a pointer to some data that cannot be changed. In order to read it, yes, you have to cast it to concrete types such as char. But I said reading, not writing, which, again, is UB.

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

This is covered more in depth here. C allows you to entirely bypass type-safety: it's your job to prevent that.

此处将对此进行更深入的介绍。C 允许您完全绕过类型安全:防止这种情况发生是您的工作。

回答by Davislor

It's possible that a given compiler on a given OS could put some of its constdata in read-only memory pages. If so, attempting to write to that location would fail in hardware, such as causing a general protection fault.

给定操作系统上的给定编译器可能会将其某些const数据放在只读内存页中。如果是这样,尝试写入该位置将在硬件中失败,例如导致一般保护故障。

The constqualifier just means that writing there is undefined behavior. This means the language standard allows the program to crash if you do (or anything else). Despite that, C lets you shoot yourself in the foot if you think you know what you're doing.

const预选赛只是意味着写作有不确定的行为。这意味着语言标准允许程序在您这样做(或其他任何事情)时崩溃。尽管如此,如果您认为自己知道自己在做什么,C 可以让您用脚射击。

You can't stop a subroutine from reinterpreting the bits you give it however it wants and running any machine instruction on them it wants. The library function you're calling might even be written in assembler. But doing that to a constpointer is undefined behavior, and you really don't want to invoke undefined behavior.

你不能阻止子程序重新解释你给它的位,但它想要并运行它想要的任何机器指令。您正在调用的库函数甚至可能是用汇编程序编写的。但是对const指针这样做是未定义的行为,您真的不想调用未定义的行为

Off the top of my head, one rare example where it might make sense: suppose you've got a library that passes around handle parameters. How does it generate and use them? Internally, they might be pointers to data structures. So that's an application where you might typedef const void* my_handle;so the compiler will throw an error if your clients try to dereference it or do arithmetic on it by mistake, then cast it back to a pointer to your data structure inside your library functions. It's not the safest implementation, and you want to be careful about attackers who can pass arbitrary values to your library, but it's very low-overhead.

在我的脑海中,有一个可能有意义的罕见示例:假设您有一个传递句柄参数的库。它如何生成和使用它们?在内部,它们可能是指向数据结构的指针。所以这是一个应用程序,typedef const void* my_handle;如果您的客户尝试取消引用它或错误地对其进行算术运算,那么编译器可能会抛出错误,然后将其转换回指向库函数中的数据结构的指针。这不是最安全的实现,您要小心攻击者可以将任意值传递到您的库,但它的开销非常低。