C问题:(const void *)与(void *)有何不同?

37

const void *void *有什么区别?在什么情况下可以将void指针转换为const void指针?

2个回答

36

const void *指向的内存不应该被修改。

void *(非const)指向的内存可以被修改(但不能直接使用void *修改,需先转换类型)。

当你使用memmove()时,源地址被强制转换为const void *

void *memmove(void *dst, const void *src, size_t nbytes);

当你知道你不会修改指针所指向的内存时,就可以在任何时候将void指针转换为常量void指针。这适用于任何指针类型,而不仅仅是void指针。

相反地,将常量指针转换为非常量指针是一种更加危险的操作。不能保证指向的内存实际上是可修改的;例如,字符串字面值可以存储在只读(常量)内存中,如果你使用强制类型转换丢失const限定并尝试修改该字符串,很可能会导致分段错误或类似的错误-程序将突然停止而不受您的控制。这不是一个好事情。因此,在非常确信可以欺骗编译器的情况下,不要将指针从常量转换为非常量。请注意,编译器不喜欢被欺骗,并且通常会在最不方便的时刻报复你(例如在向重要潜在客户演示程序时,你的老板、老板的老板和老板的老板的老板都在场)。


8
“你的上司的上司的上司” - 很好,这可能会成为我新的未定义行为示例。我通常使用“着火”,但现在我正在考虑“检测有多少人正在观看,如果少于3个人,则工作”,或者“仅当代码在单元测试构建中运行时才工作”。 - Steve Jessop
1
更准确地说,const void * a 表示这个特定的指针不能修改数据,但是可以修改指向数据的指针。另一方面,void * const a 表示指针不可变,但是数据可以被修改。void * 表示可变指针可以指向可被修改的数据。 - paraflou
2
对于像我这样不了解C语言中void指针的新手:
  1. 在C语言中,通用指针被称为void指针;
  2. 它没有与之关联的任何数据类型;
  3. 它可以存储任何类型变量的地址;
  4. void指针是C语言中原始地址的一种约定。
- ZhaoGang

8

void * 强制转换为 const void * 是完全合理的,编译器应该在幕后自动执行此操作,而无需您考虑任何事情,但反过来则是危险的,必须避免。

记住,如果函数接受一个 const 指针,则可以自由地向其传递 const 或非 const 值。说你使用了 const 指针只是声明该内存不会被您的函数修改。

例如:(注意标有 DANGER 的行应该会引发编译器错误)

const void *p_const;
void *p_buffer;

// const pointers are allowed to hold static memory
p_const = "Foo"; // allowed
p_buffer = "Foo"; // DANGER!!!

// casting to const is implicit
p_const = malloc(LEN); // allowed - implicit cast
p_buffer = malloc(LEN); // allowed

// casting to const implicit again
write(STDOUT, p_const, LEN); // allowed
write(STDOUT, p_buffer, LEN); // also allowed - implicit cast

// const memory cannot be used where mutable memory is expected
read(0, p_buffer, LEN); // allowed
read(0, p_const, LEN); // DANGER!!

// To make the above more obivous, we'll skip the intermediate variable
// and place instead what it holds
read(0, malloc(LEN), LEN); // allowed - useless but at least no crashes
read(0, "foo", 4); // DANGER!!!

作为一般规则,如果您编写的函数接受一个指向您不打算修改的值的指针,则函数签名应使用const指针。使用未声明为const的指针意味着允许修改您指向的内存。
另一个例子:
void do_something(const void* ptr, int length);

// Since the signature is a const pointer, I know I can call it like this:
do_something("foo",4);

相反,如果函数调用需要一个非常量指针,则我必须允许它:
void do_something(void* ptr, int length);

// This tells me that the function may overwrite my value.
// The safe solution therefore looks more like this:

char *myptr = char[4];
memcpy(myptr,"foo",4);    
do_something(myptr,4);

同样地,如果你遇到需要将一个const指针转换为非const指针的情况,你应该将指向的值复制到可变部分内存中,并将副本传递给函数,而不是原始值。如果这听起来让人头疼,那是因为它确实很麻烦。如果你发现自己处于这种情况下,那么你可能做错了什么。
从概念上讲,如果变量保存的是“值”,那么它很可能是一个const指针。如果它保存的是一个“缓冲区”,那么它就是一个非const指针。
在函数签名中,指针应该始终声明为const,除非你打算写入该内存。遵循这个规则将帮助你避免程序逻辑中的灾难性问题。
我直到编程6年才理解这个简单的规则。

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