c++ - %p说明符仅用于有效的指针吗?

标签 c++ pointers reinterpret-cast

假设在我的平台上sizeof(int)==sizeof(void*),我有以下代码:

printf( "%p", rand() );

由于传递的值不是%p而不是有效的指针,这将是未定义的行为吗?

最佳答案

为了扩展@larsman的答案(这表示由于您违反了约束,因此行为未定义),这是一个实际的C实现,其中sizeof(int) == sizeof(void*),但是该代码并不等效于printf( "%p", (void*)rand() );
摩托罗拉68000处理器具有16个用于常规计算的寄存器,但它们并不等效。其中八个(从a0a7命名)用于访问内存(地址寄存器),另外八个(从d0d7命名)用于算术(数据寄存器)。此架构的有效调用约定为

  • d0d1中传递前两个整数参数;将其余的传递给堆栈。
  • a0a1中传递前两个指针参数;将其余的传递给堆栈。
  • 传递所有其他类型的堆栈,而不管大小如何。
  • 不管类型如何,在堆栈上传递的参数都会从右向左推送。
  • 基于堆栈的参数在4字节边界上对齐。

  • 这是一个完全合法的调用约定,类似于许多现代处理器使用的调用约定。

    例如,要调用函数void foo(int i, void *p),您可以在i中传递d0,在p中传递a0

    请注意,要调用函数void bar(void *p, int i),您还将在i中传递d0并在p中传递a0

    在这些规则下,printf("%p", rand())将在a0中传递格式字符串,并在d0中传递随机数参数。另一方面,printf("%p", (void*)rand())将在a0中传递格式字符串,并在a1中传递随机指针参数。
    va_list结构如下所示:
    struct va_list {
        int d0;
        int d1;
        int a0;
        int a1;
        char *stackParameters;
        int intsUsed;
        int pointersUsed;
    };
    

    前四个成员用相应的寄存器入口值初始化。 stackParameters指向通过...传递的第一个基于堆栈的参数,并且intsUsedpointersUsed初始化为命名参数的数量,它们分别是整数和指针。
    va_arg宏是编译器固有的,可根据预期的参数类型生成不同的代码。
  • 如果参数类型是指针,则va_arg(ap, T)扩展为(T*)get_pointer_arg(&ap)
  • 如果参数类型是整数,则va_arg(ap, T)扩展为(T)get_integer_arg(&ap)
  • 如果参数类型是其他类型,则va_arg(ap, T)扩展为*(T*)get_other_arg(&ap, sizeof(T))
  • get_pointer_arg函数如下所示:
    void *get_pointer_arg(va_list *ap)
    {
        void *p;
        switch (ap->pointersUsed++) {
        case 0: p = ap->a0; break;
        case 1: p = ap->a1; break;
        case 2: p = *(void**)get_other_arg(ap, sizeof(p)); break;
        }
        return p;
    }
    
    get_integer_arg函数如下所示:
    int get_integer_arg(va_list *ap)
    {
        int i;
        switch (ap->intsUsed++) {
        case 0: i = ap->d0; break;
        case 1: i = ap->d1; break;
        case 2: i = *(int*)get_other_arg(ap, sizeof(i)); break;
        }
        return i;
    }
    
    get_other_arg函数如下所示:
    void *get_other_arg(va_list *ap, size_t size)
    {
        void *p = ap->stackParameters;
        ap->stackParameters += ((size + 3) & ~3);
        return p;
    }
    

    如前所述,调用printf("%p", rand())将在a0中传递格式字符串,并在d0中传递随机整数。但是,当printf函数执行时,它将看到%p格式并执行va_arg(ap, void*),后者将使用get_pointer_arg并从a1而不是d0读取参数。由于a1未初始化,因此包含垃圾。您生成的随机数将被忽略。

    进一步举例说明,如果您有printf("%p %i %s", rand(), 0, "hello");,则将其称为:
  • a0 =格式字符串的地址(第一个指针参数)
  • a1 =字符串"hello"的地址(第二个指针参数)
  • d0 =随机数(第一个整数参数)
  • d1 = 0(第二个整数参数)

  • printf函数执行时,它将按预期从a0中读取格式字符串。当看到%p时,它将从a1中检索指针并打印出来,因此您获得了字符串"hello"的地址。然后它将看到%i并从d0检索参数,因此它将打印一个随机数。最后,它看到%s并从堆栈中检索参数。但是您没有在堆栈上传递任何参数!这将读取未定义的堆栈垃圾,这很可能在您的程序尝试打印它时就好像是一个字符串指针一样使您的程序崩溃。

    关于c++ - %p说明符仅用于有效的指针吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11688162/

    相关文章:

    c++ - OpenGL检测点击另一个对象内的对象

    c++ - 在不重新洗牌的情况下随机选择 std::vector 的所有元素一次的有效方法

    c++ - std::bind 到 std::function 使用 Clang 崩溃

    c++ - 即使标题正确,也没有什么不起作用? (GCC C++)

    c++ - 在 C++ 中打印函数地址,g++/clang++ vs vc++,谁是对的?

    java - 是否存在与 C union 或 C++ std::variant 等效的内存高效 Java?

    c++ - 键盘日志不可用时如何记录击键?

    c - 总线错误: 10 While compiling C program

    c - 为数组赋值时发生段错误

    c++ - reinterpret_cast<> 在 sse/avx 类型上是安全的还是未定义的?