You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++打印指针十六进制可读无符号值g++报错及运行结果异常问题

问题说明

开发需求为将指针的地址值打印为人类可读的无符号十进制数值,实际编码中遇到两类异常:

  • 第一版代码在Mobile C环境可正常运行输出十进制地址,但在Fedora 36系统的g++环境下编译直接报错
  • 自行修改后的第二版代码可通过编译,但输出带0x前缀的十六进制地址,且指针自增、自减偏移后,解引用得到不符合预期的异常数值

第一版原始代码(Mobile C可运行,g++编译报错)

#include <iostream>
using namespace std;

int main()
{
    int i{14},j{20};
    int *pj{&j},*pi{&i};
    
    cout<<endl<<(unsigned)pi;
    cout<<"\t"<<*pi;
    cout<<endl<<(unsigned)pj;
    cout<<"\t"<<*pj;
    
    --pi;
    ++pj;
    
    cout<<endl<<(unsigned)pi;
    cout<<"\t"<<*pi;
    cout<<endl<<(unsigned)pj;
    cout<<"\t"<<*pj;
    
    return 0;
}

Mobile C环境运行输出:

2582956840  14
2582956836  20
2582956836  20
2582956840  14

第二版修改代码(可编译但运行结果异常)

#include <iostream>
using namespace std;

int main()
{
    unsigned int i{14},j{20};
    unsigned int *pj{&j},*pi{&i};
    
    cout<<endl<<(unsigned*)pi;
    cout<<"\t"<<*pi;
    cout<<endl<<(unsigned*)pj;
    cout<<"\t"<<*pj;
    
    --pi;
    ++pj;
    
    cout<<endl<<(unsigned*)pi;
    cout<<"\t"<<*pi;
    cout<<endl<<(unsigned*)pj;
    cout<<"\t"<<*pj;
    
    return 0;
}

onlinegdb环境运行输出:

0x7fff539232d0  14
0x7fff539232d4  20
0x7fff539232cc  21863
0x7fff539232d8  1402090200
问题根因
  • 第一版代码g编译报错:64位环境(Fedora 36默认为64位系统)下指针长度为8字节,普通unsigned int长度为4字节,C不允许将指针强制转换为长度不匹配的整型,该写法属于未定义行为,编译器会直接报错。而Mobile C是32位运行环境,指针与unsigned int均为4字节,写法碰巧可以运行,不具备可移植性。
  • 第二版代码输出带0x前缀的十六进制值:cout对所有指针类型的默认输出格式就是十六进制带前缀格式,将指针强转为unsigned*本质还是指针类型,自然触发默认指针输出逻辑,不会输出十进制数值。
  • 指针偏移后解引用得到异常值:C指针的自增/自减偏移步长为指向类型的大小,int*偏移1次的移动距离为4字节(即一个int的长度)。代码中i、j是两个独立的栈局部变量,C标准不保证栈上独立局部变量的内存地址连续相邻,偏移后的指针已经指向不属于这两个变量的栈内存区域,里面存储的是栈上残留的随机脏数据,解引用本身就是未定义行为,输出任意值都符合预期。Mobile C环境下偏移后刚好输出14、20,只是该环境下编译器凑巧把i和j分配到了连续栈地址,属于环境巧合,不是标准保证的行为。
正确实现方式

要跨平台将指针地址打印为无符号十进制数值,必须使用<cstdint>头文件中定义的uintptr_t类型,该类型是专门用于存储指针对应整数值的无符号整型,长度与当前环境下的指针长度完全一致,不会出现长度不匹配的问题。
正确代码如下:

#include <iostream>
#include <cstdint>
using namespace std;

int main()
{
    int i{14},j{20};
    int *pj{&j},*pi{&i};
    
    cout << endl << (uintptr_t)pi;
    cout << "\t" << *pi;
    cout << endl << (uintptr_t)pj;
    cout << "\t" << *pj;
    
    --pi;
    ++pj;
    // 以下两处解引用为野指针访问,属于未定义行为,仅为复现测试逻辑保留,生产代码禁止编写
    cout << endl << (uintptr_t)pi;
    cout << "\t" << *pi;
    cout << endl << (uintptr_t)pj;
    cout << "\t" << *pj;
    
    return 0;
}
注意事项
  • 禁止对指向独立变量的指针随意做偏移解引用操作,只有指针指向连续合法内存(如数组、动态申请的连续内存块)时,在合法范围内的偏移解引用才是标准定义的行为。
  • 禁止使用长度不匹配的整型存储指针值,uintptr_t是唯一跨平台安全的指针转整型的目标类型。

内容的提问来源于stack exchange,提问作者SogaBan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 11:54:22