PRIu64与intptr_t在不同架构处理器可移植性的作用

1 前言

当项目中需要将原来32位架构处理器的代码移植到ARM64架构处理器上时,在编译过程中会遇到许多的warning,这些警告大多与指针与整型变量的类型转换和printf函数参数的数据类型不相符有关。而intptr_t类型和PRIu64这些宏定义提高了代码在不同架构平台的可移植性。

2 intptr_t类型

intptr_tuintptr_t 类型用来存放指针地址。它们提供了一种可移植且安全的方法声明指针,而且和系统中使用的指针长度相同,对于把指针转化成整数形式来说很有用,这两个数据类型是ISO C99定义的,具体代码在linux平台的/usr/include/stdint.h头文件中

/* Types for `void *' pointers.  */
#if __WORDSIZE == 64
#ifndef __intptr_t_defined
typedef long int  intptr_t;
#define __intptr_t_defined
#endif
typedef unsigned long int uintptr_t;
#else
#ifndef __intptr_t_defined
typedef int   intptr_t;
#define __intptr_t_defined
#endif
typedef unsigned int  uintptr_t;
#endif

这样定义的原因是因为不同的数据类型在不同字长机器上长度大小是不同的:

machinecharshortintlongptr
161byte2byte2byte4byte2byte
321byte2byte4byte4byte4byte
641byte2byte4byte8byte8byte

指针在32位平台和64位平台下均与long类型的长度一致,然而在16位机器上,long为4个字节,而指针为2个字节。
因此,就可以发现intptr_tuintptr_t定义的巧妙之处:
在64位机器上,intptr_tlong intuintptr_tunsigned long int。而在非64位机器上,intptr_tintuintptr_tunsigned int
这样就可以保证intptr_tuintptr_t的长度与机器的指针长度一致,因此在进行整数与指针的相互转换时可以用intptr_t保证可移植性。
以下面这段代码为例:

#include <stdio.h>
#include <stdint.h>
int main()
{
	int a = 12345;    
    int *p = &a;
    int ptr = (int )p;
    printf("%d\n",ptr);  
    printf("sizeof(ptr):%ld,sizeof(p):%ld\n",sizeof(ptr),sizeof(p));
    return 0;
}

在编译时,GCC会给出warning,Wpointer-to-int-cast 表明将指针转换为整型,但是二者大小不同

test.c: In function ‘main’:
test.c:13:12: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
   13 |  int ptr = (int)p;
      |            ^

如果使用uintptr_t改写代码则不会警告:

#include <stdio.h>
#include <stdint.h>
int main()

{
	uintptr_t a = 12345;
    uintptr_t *p = (uintptr_t*)a;
    printf("%p\n",p)
    printf("sizeof(a):%ld,sizeof(p):%ld\n",sizeof(a),sizeof(p));
   	return 0;
}

3 PRIu64

如下这段代码,在32位处理器和64位处理器下编译的警告会不同:

uint64_t a = 0x1234;
uint8_t str[20] = {0};
sprintf(str, "%llu", a);

使用 64 位编译器,编译报警告:uint64_tlong unsigned int 类型,请使用 "%ld"
如果改成 “%ld”,使用 32 位机器编译时,又报警告:uint64_tlong long unsigned int类型,请使用 "%lld"
这是因为在 32 位平台上,std::uint64_t将被定义为 unsigned long long并且格式说明符将为%llu。而在 64 位平台上,std::uint64_t将被定义为 unsigned long并且格式说明符将为 %lu 。
由此可见对于uint64_tint64_t这类数据类型,在sprintfprintf这类函数中的可移植性很差,是因为64位数据类型在不同架构的处理器中定义是不同的。
通过PRIu64PRId64PRIx64等宏可以很好的解决这个问题。
PRIu64 的定义在 inttypes.h 头文件中,使用时需要引入该头文件

# if __WORDSIZE == 64
# define __PRI64_PREFIX	"l"
# else
# define __PRI64_PREFIX	"ll"
# endif
# define PRIu64 __PRI64_PREFIX "u"

对于32 位编译器,会把 "%" PRIu64 "" 扩展为 "%lld",对于64 位编译器,会把 "%" PRIu64 "" 扩展为 "%ld"
示例代码如下:

#include <stdio.h>
#include <inttypes.h>

int main(int argc, char *argv[])
{
    uint64_t u64 = 100;
    int64_t  int64 = 100;
    printf("uint64: %" PRIu64 "\n", u64);
    printf("uint64 hex: %0x" PRIx64 "\n", u64);
    printf("int64: %" PRId64 "\n", int64);
    return 0;
}

注意: 如果是C++程序,需要定义_STDC_FORMAT_MACROS宏,可以在cmakelist.txt中加入add_compile_definitions(_STDC_FORMAT_MACROS)添加该宏

  • 6
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值