遇到的问题

今天遇到了一个很奇怪的问题,当使用malloc分配了一个堆空间后,分别尝试用coutprintf尝试打印该地址,出现了两个地址不一样的情况:

int *pp = (int*)malloc(10*sizeof(int));
*pp = 1234;
cout << pp << '\t' << *pp << endl;
printf("0x%x\t%d\n", pp, *pp);
free(pp);

可以看出两个地址低32位是一致的,出现问题的原因应该是printf显示不全导致的。

image-20230423162758215

后来弄明白了,这是C/C++里占位符之间的区别导致的。

C/C++占位符%x和%p

%x%X:表示的是输入数字的十六进制表达,长度为32bit

%p:表示的是输入变量的指针(地址)值,十六进制数,长度由当前操作系统决定

实际上,上面cout流输出的pp表示的就是实际的地址值,和%p是一个意思,当我们使用%p输出时,结果就是正常的:

printf("%p\t%d\n", pp, *pp);

image-20230423172245192

那么%x输出的时候为什么不对呢?这是因为%x是按照int占位4字节来表示的,在16位和32位系统里,由于地址不超过4字节,所以不会导致%x和%p的输出区别。

但是在64位系统里,%p占位变成了8字节,而%x则还是4字节,所以高位就被截断了。

如果需要正确输出,我们可以指定%llx,这样输出结果就是一致的了:

image-20230423173910954

C/C++占位符总结

格式化占位符(format placeholder),语法是:

%[*parameter*][*flags*][*field width*][.*precision*][*length*]*type*

Printf.svg

Parameter可以忽略或者是:

字符描述
*n*$n是用这个格式说明符(specifier)显示第几个参数;这使得参数可以输出多次,使用多个格式说明符,以不同的顺序输出。 如果任意一个占位符使用了parameter,则其他所有占位符必须也使用parameter 这是POSIX扩展,不属于ISO C。例:printf("%2$d %2$#x; %1$d %1$#x",16,17) 产生"17 0x11; 16 0x10"

Flags可为0个或多个:

字符描述
+总是表示有符号数值的’+‘或’-'号,缺省情况是忽略正数的符号。仅适用于数值类型。
空格使得有符号数的输出如果没有正负号或者输出0个字符,则前缀1个空格。如果空格与’+'同时出现,则空格说明符被忽略。
-左对齐。缺省情况是右对齐。
#对于’g’与’G’,不删除尾部0以表示精度。对于’f’, ‘F’, ‘e’, ‘E’, ‘g’, ‘G’, 总是输出小数点。对于’o’, ‘x’, ‘X’, 在非0数值前分别输出前缀0, 0x, and 0X表示数制。
0如果width选项前缀以0,则在左侧用0填充直至达到宽度要求。例如printf("%2d", 3)输出" 3",而printf("%02d", 3)输出"03"。如果0-均出现,则0被忽略,即左对齐依然用空格填充。

Field Width给出显示数值的最小宽度,典型用于制表输出时填充固定宽度的表目。实际输出字符的个数不足域宽,则根据左对齐或右对齐进行填充。实际输出字符的个数超过域宽并不引起数值截断,而是显示全部。宽度值的前导0被解释为0填充标志,如上述;前导的负值被解释为其绝对值,负号解释为左对齐标志。如果域宽值为*,则由对应的函数参数的值为当前域宽。

Precision通常指明输出的最大长度,依赖于特定的格式化类型。对于d、i、u、x、o的整型数值,是指最小数字位数,不足的位要在左侧补0,如果超过也不截断,缺省值为1。对于a,A,e,E,f,F的浮点数值,是指小数点右边显示的数字位数,必要时四舍五入或补0;缺省值为6。对于g,G的浮点数值,是指有效数字的最大位数;缺省值为6。对于s的字符串类型,是指输出的字节的上限,超出限制的其它字符将被截断。如果域宽为*,则由对应的函数参数的值为当前域宽。如果仅给出了小数点,则域宽为0。

Length指出浮点型参数或整型参数的长度。此项Microsoft称为“Size”。可以忽略,或者是下述:

字符描述
hh对于整数类型,printf期待一个从char提升的int尺寸的整型参数。
h对于整数类型,printf期待一个从short提升的int尺寸的整型参数。
l对于整数类型,printf期待一个long尺寸的整型参数。
对于浮点类型,printf期待一个double尺寸的整型参数。
对于字符串s类型,printf期待一个wchar_t指针参数。
对于字符c类型,printf期待一个wint_t型的参数。
ll对于整数类型,printf期待一个long long尺寸的整型参数。Microsoft也可以使用I64
L对于浮点类型,printf期待一个long double尺寸的整型参数。
z对于整数类型,printf期待一个size_t尺寸的整型参数。
j对于整数类型,printf期待一个intmax_t尺寸的整型参数。
t对于整数类型,printf期待一个ptrdiff_t尺寸的整型参数。

类型 Type,也称转换说明(conversion specification/specifier),可以是:

字符描述
d, i有符号十进制数值int。‘%d’与’%i’对于输出是同义;但对于scanf()输入二者不同,其中%i在输入值有前缀0x或0时,分别表示16进制或8进制的值。如果指定了精度,则输出的数字不足时在左侧补0。默认精度为1。精度为0且值为0,则输出为空。
u十进制unsigned int。如果指定了精度,则输出的数字不足时在左侧补0。默认精度为1。精度为0且值为0,则输出为空。
f, Fdouble型输出10进制定点表示。'f’与’F’差异是表示无穷与NaN时,‘f’输出’inf’, ‘infinity’与’nan’;‘F’输出’INF’, ‘INFINITY’与’NAN’。小数点后的数字位数等于精度,最后一位数字四舍五入。精度默认为6。如果精度为0且没有#标记,则不出现小数点。小数点左侧至少一位数字。
e, Edouble值,输出形式为10进制的([-]d.ddd e[+/-]ddd). E版本使用的指数符号为E(而不是e)。指数部分至少包含2位数字,如果值为0,则指数部分为00。Windows系统,指数部分至少为3位数字,例如1.5e002,也可用Microsoft版的运行时函数_set_output_format 修改。小数点前存在1位数字。小数点后的数字位数等于精度。精度默认为6。如果精度为0且没有#标记,则不出现小数点。
g, Gdouble型数值,精度定义为全部有效数字位数。当指数部分在闭区间 [-4,5] 内,输出为定点形式;否则输出为指数浮点形式。'g’使用小写字母,'G’使用大写字母。小数点右侧的尾数0不被显示;显示小数点仅当输出的小数部分不为0。
x, X16进制unsigned int。'x’使用小写字母;'X’使用大写字母。如果指定了精度,则输出的数字不足时在左侧补0。默认精度为1。精度为0且值为0,则输出为空。
o8进制unsigned int。如果指定了精度,则输出的数字不足时在左侧补0。默认精度为1。精度为0且值为0,则输出为空。
s如果没有用l标志,输出null结尾字符串直到精度规定的上限;如果没有指定精度,则输出所有字节。如果用了l标志,则对应函数参数指向wchar_t型的数组,输出时把每个宽字符转化为多字节字符,相当于调用wcrtomb函数。
c如果没有用l标志,把int参数转为unsigned char型输出;如果用了l标志,把wint_t参数转为包含两个元素的wchart_t数组,其中第一个元素包含要输出的字符,第二个元素为null宽字符。
pvoid *
a, Adouble型的16进制表示,“[−]0xh.hhhh p±d”。其中指数部分为10进制表示的形式。例如:1025.010输出为0x1.004000p+10。'a’使用小写字母,'A’使用大写字母。[2][3] (C++11流使用hexfloat输出16进制浮点数)
n不输出字符,但是把已经成功输出的字符个数写入对应的整型指针参数所指的变量。
%'%'字面值,不接受任何flags, width, precision or length。
length<-------------------------- -------- type -------- ------------------>
d 、 i \mathrm{d、i} di u 、 o 、 x 、 X \mathrm{u、o、x、X} uoxX f 、 F 、 e 、 E 、 g 、 G \mathrm{f、F、e、E、g、G} fFeEgG c \mathrm{c} c s \mathrm{s} s p \mathrm{p} p
(none)intunsigned intdoublecharchar *void *
h h \mathrm{hh} hhsigned charunsigned char
h \mathrm{h} hshort intunsigned short int
l \mathrm{l} llong intunsigned long intwint_twchar_t
l l \mathrm{ll} lllong long intunsigned long long int
L \mathrm{L} L long double
j \mathrm{j} jintmax_tuintmax_t
z \mathrm{z} zsize_tssize_t
t \mathrm{t} tptrdiff_tptrdiff_t

参考文献

1:C语言中的%p和%x的区别_百度知道

2:格式化字符串 - 维基百科,自由的百科全书

如有疑问或错误,欢迎和我私信交流指正。
版权所有,未经授权,请勿转载!
Copyright © 2023 by Mr.Idleman. All rights reserved.

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐