【问题标题】:Void* arithmetic and type castingvoid* 算术和类型转换
【发布时间】:2015-12-10 20:47:41
【问题描述】:

我最近通过测试一些东西试图理解void*,这里是测试代码:

#include <stdio.h>

int main(int argc, char** argv)
{
    float nb2 = 2.2;
    void* multi_type_ptr = &nb2;

    printf("\nmulti_type_ptr:\n");
    printf("Elem 1: %f\n",*(float*)multi_type_ptr);

    // *(multi_type_ptr + 1) = 4;

    // *(int*)(multi_type_ptr + 1) = 4;

    *((int*)multi_type_ptr + 1) = 4;

    printf("Elem 2: %d\n",*((int*)multi_type_ptr + 1));

    return 0;
}

这条指令是否不能工作,因为语言/编译器不知道它应该添加多少字节到multi_type_ptr 的地址(它不知道void* 指向的类型的大小)?

*(multi_type_ptr + 1) = 4;

同样void* 不能被取消引用,所以它应该被转换为正确的类型,对吧?
由于上面提到的问题,这条线仍然无法工作。 p>

*(int*)(multi_type_ptr + 1) = 4;

由于语言/编译器理解它必须将(1 * sizeof int) 添加到地址of multi_type_ptr,因此该分配是否有效? 我使用 Visual Studio 中包含的 cl 编译器

*((int*)multi_type_ptr + 1) = 4;

看起来(multi_type_ptr + 1) 的结果仍然是(int*),当它的内容被行首的* 访问时,对吗? 提前感谢您以后的更正、解释和强化。

【问题讨论】:

    标签: c casting void-pointers


    【解决方案1】:
    *(multi_type_ptr + 1) = 4;
    

    表达式 multi_type_ptr + 1 不是有效的 C,因为 C 不允许使用 void * 进行指针运算。

    *((int*)multi_type_ptr + 1) = 4;
    

    因为(int*) multi_type_ptr + 1int * 进行指针运算,所以该语句在您的机器中编译。然而,如果结果指针与操作数(或最后一个元素之后)不在同一个数组对象中,则 C 中的指针添加是未定义的行为,如果结果指针在数组对象之外,则取消引用结果指针是未定义的行为,所以在您的情况 * 表达式(以及语句)调用未定义的行为。

    【讨论】:

    • 吹毛求疵:计算一个越过数组的指针也可以,只要你不取消引用它。
    • 如果2*sizeof(int) &lt;= sizeof(float),那么他没有在对象之外访问。
    • @Barmar 是对的,但即使在这种情况下,由于别名违规,它也是 UB。
    • 没错,但不是你说的。罗德里戈的回答对此进行了更详细的说明。
    【解决方案2】:

    这里有几个问题:

    这个指令不能工作是因为语言/编译器不知道它应该添加多少字节到 multi_type_ptr 的地址(它不知道 void* 指向的类型的大小)?

    *(multi_type_ptr + 1) = 4;
    

    是的,你是对的。一些编译器(例如 GCC)允许使用 void 指针进行指针运算并将它们视为指向大小为 1 的对象(就像 char* 一样),以避免来自 void* 的频繁转换只是为了四处移动。

    此分配是否有效,因为语言/编译器理解它必须将 (1 * sizeof int) 添加到 multi_type_ptr 的地址?我使用 Visual Studio 中包含的 cl 编译器。

    *((int*)multi_type_ptr + 1) = 4;
    

    是的,再次正确。但请注意,虽然这段代码在语法上是正确的,并且可以编译,但在技术上它是未定义的行为,原因有两个:

    1. 你是 type punningtype aliasing nb2 变量。也就是说,您正在使用不同于定义的类型(定义为float,用作int)访问它(例外情况是您使用的是char 指针,但事实并非如此)。李>
    2. 如果2*sizeof(int) &gt; sizeof(float)(很可能)那么您的指针算法将您带出nb2 界限,您可能正在破坏您的堆栈。

    当 (multi_type_ptr + 1) 的内容在行首被 * 访问时,它的结果看起来仍然是 (int*),对吗?

    没有。 Cast 运算符具有高优先级,所以这一行:

    *((int*)multi_type_ptr + 1) = 4;
    

    实际上读作:

    *(((int*)multi_type_ptr) + 1) = 4;
    

    也就是说,像:

    int *temp1 = (int*)multi_type_ptr);
    int *temp2 = t1 + 1;
    *temp2 = 4;
    

    但同样,请注意类型别名

    【讨论】:

    • 您还应该提到,如果 float 类型不足以容纳两个 int 值,则会导致额外的 UB。
    • “也就是说,您正在使用不同于定义的类型(定义为 float,用作 int)访问它。”。如果我这样做 *(int*)((float*)multi_type_ptr + 1) = 4; 怎么办,它仍然会导致未定义的行为吗?
    • @Barmar:嗯,是的。但是由于您已经有了 UB,因为类型双关而不考虑字体大小,而且从技术上讲,您不能有两个 UB,所以这一点在技术上是没有实际意义的。然而,在实践中,类型双关语 UB 不太可能产生副作用,而类型大小 UB 可能会使您的程序崩溃!
    • @aurelienC:同样如此,您正在访问定义为float 的内存,同时取消引用int*
    • @rodrigo : 嗯,谢谢,还有很多我不确定的事情,但这对于这个问题来说已经足够了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-22
    • 2021-02-14
    • 2015-02-14
    相关资源
    最近更新 更多