关于C23中char*与void*的可互换性及特定代码合法性的技术问询
关于C23中char与void的可互换性及特定代码合法性的技术问询
咱们先把你的问题拆解开,结合C23的标准规则一步步聊,先看你给出的代码片段:
char** L = (char*[]){ "String", 0 }; char* s = * (void**) (void*) L;
逐行拆解代码的合规性
第一行代码:这部分完全符合C23标准。你用复合字面量创建了一个
char*类型的数组,数组会自动隐式转换为指向首元素的指针,再赋值给char**类型的L——这是C标准里数组到指针的常规转换,没有任何问题。第二行代码:这里就踩了标准的红线,咱们拆成步骤看:
- 把
char**类型的L转成void*:这是合法的,C标准(包括C23)规定任意对象指针都能隐式转成void*,反过来也成立。 - 但把
void*转成void**再解引用的操作,就违反了两个核心规则:- 严格别名规则:C标准明确要求,除非用字符类型指针访问对象,否则不能用一种类型的指针去访问另一种不兼容类型的对象。
void**不属于字符类型指针,而你实际访问的是char*类型的数组元素,这就触发了未定义行为。 - 指针对象表示的不确定性:虽然绝大多数常见平台上所有指针的大小和内存布局都一致,但C标准不保证
char**和void**的内存表示完全相同。也就是说,你强转后解引用得到的值,标准不保证就是原本的char*值。
- 严格别名规则:C标准明确要求,除非用字符类型指针访问对象,否则不能用一种类型的指针去访问另一种不兼容类型的对象。
- 把
关于void*和char*的兼容性误区
这里要明确一个关键:void*和char*不是兼容类型,它们只是被标准允许互相隐式转换的不同类型。兼容类型指的是语义和内存表示完全一致的类型(比如signed int和int),而void*和char*的转换特权只限于一级指针,不能延伸到二级指针(比如char**和void**)。
符合标准的替代写法
如果你的目的只是获取L指向的第一个char*元素,完全没必要绕这么多弯,直接写:
char* s = *L;
这完全符合C23标准,清晰易懂,也没有任何未定义行为的风险。
聊点实践中的情况
你提到“实际中可能能正常工作”,这确实是桌面平台的普遍情况——因为这些平台的所有指针类型都用相同的内存布局。但标准不保证这一点,一旦在特殊平台(比如某些嵌入式架构、有不同指针宽度的系统)上运行,这段代码可能会崩溃、数据错乱,出现各种奇怪问题。
内容来源于stack exchange
相关产品推荐
相关产品推荐

