sprintf 格式代码使用不规范在不同平台下的表现

sprintf 格式代码使用不规范在不同平台下的表现(C 语言)


 作者:高玉涵
 时间:2022.6.22 10:09
 博客:blog.csdn.net/cg_i

时代变了,大人

在这里插入图片描述

 像很多人一样我学习 C 语言是由 1978 年 Dennis M. RitchieBrian W. Kernighan 合著为《The C Programming Language》这本书走向 C 语言的世界。首先,书中描述的 C 是旧式的(有时称做 Kernighan 和 Ritchie[KERN 78],或称 K&R C)并已被 ANSI C (以下简称标准)从根本上取代了。尽管绝大多数以 K&R C 写成的程序仅需极微小的修改即可在 ANSI C 环境运行,但标准并未硬性规定 C 编译器对这些差异进行检查,而且绝大多数 C 编译器确实也不进行检查。因此,你必须自行保证编写的代码符合规范。如果使用不当,它们就会引发错误,导致细微而令人困惑的症状,并且极难发现原因。只是略知一二便放手使用是件非常危险的事。如果那样的话,它给带来的总是痛苦而不是欢乐。

正文
1. 使用环境
  • CPU

 HPUNIX:安腾 9700 系列

 LINUX:X86 架构

  • 操作系统

 HPUNIX:HP-UX xxxxx2 B.11.31 U ia64 2932500072 unlimited-user license

 LINUX:Linux xxxxkf1 4.19.90-24.4.v2101.ky10.x86_64 #1 SMP Mon May 24 12:14:55 CST 2021 x86_64 x86_64 x86_64 GNU/Linux

  • 编译器程序及版本

 HPUNIX:cc: HP C/aC++ B3910B A.06.25 [Nov 30 2009]

 LINUX:

使用内建 specs。
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-linux-gnu/7.3.0/lto-wrapper
目标:x86_64-linux-gnu
配置为:../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --with-linker-hash-style=gnu --enable-languages=c,c++,objc,obj-c++,fortran,lto --enable-plugin --enable-initfini-array --disable-libgcj --without-isl --without-cloog --enable-gnu-indirect-function --build=x86_64-linux-gnu --with-stage1-ldflags=' -Wl,-z,relro,-z,now' --with-boot-ldflags=' -Wl,-z,relro,-z,now' --with-tune=generic --with-arch_32=x86-64 --disable-multilib
线程模型:posix
gcc 版本 7.3.0 (GCC) 
2.代码展示

 先看示例代码:

#include<stdio.h>

int main(void)
{
  char sPNGZXH[100];
  int PNGZXH_LEN = 11;

  sprintf(sPNGZXH, "%0*s",PNGZXH_LEN,"123");
  printf("%s\n", sPNGZXH);
  return 0;
}
  • HPUNIX 编译并运行
cc sp.c -o sp
./sp
00000000123
  • LINUX 编译并运行
gcc sp.c -o sp
./sp
        123
sprintf

 int sprintf( char *buffer, char const *format, … );

 sprintf 把它的结果作为一个 NUL 结尾的字符串存储在指定的 buffer 缓冲区而不是写入到流中。函数返回值是实际打印或存储的字符数。

警告

sprintf 是一个潜在的错误根源。缓冲区的大小并不是 sprintf 函数的一个参数,所以如果输出结果很长溢出缓冲区时,就可能改写缓冲区后面内存位置中的数据。要杜绝这个问题,可以采取两种策略。第 1 种是声明一个非常巨大的缓冲区,但这个方案很浪费内存,而且尽管大型绶冲区能够减少溢出的可能性,但并不能根除这种可能性。第 2 种方法是对格式进行分析,看看最大可能出现的值被转换后的结果输出将有多长。例如,在 4 位整型的机器上,最大的整数有 11 位(包括一个符号位),所以缓冲区至少能容纳 12 个字符(包括结尾的 NUL 字节)。字符串的长度并没有限制,但函数所生成的字符串的字符数目可以用格式化代码中一个可选的字段来限制。

格式代码

 sprintf 函数原型中的 format 字符串可能包含格式代码,它使参数列表的下一个值根据指定的方式进行格式化,至于其他的字符则原样逐字打印。格式代码由一个百分号开头,后面跟(1)零个或多个标志字符,用于修改有些转换的执行方式,(2)一个可选的最小字段宽度,(3)一个可选的精度,(4)一个可选的修改符,(5)转换类型。

3. 问题描述

 有了上面背景铺垫,再看代码在两个平台下的输出差异是明显的。HPUNIX 下的输出结果是当前我们一直使用且想要的。然而代码移植到 LINUX 平台后,原先正常的代码(看起来像)现在就不能用了(字符串右对齐使用 0 填充左边未使用的列,变成了空格)。

 首先看一下标准是如何描述标志字符和它们的含义(这里只列出我们关心的)。

代码参数含义
schar *打印一个字符串
标志含义
0当数值为右对齐时,缺省情况下是使用空格填充未使用的列。这个标志表示用零来填充,它可用于 d,i,u,o,x,X,e,E,f,g 和 G 代码。使用 d,i,u,o,x 和 X 代码时,如果给出了精度字段,零标志就被忽略。如果格式化代码中出现了负号标志,零标志也没有效果。
宽度含义
*宽度在 format 字符串中未指定,但是会作为附加整数值参数放置于要被格式化的参数之前。
总结

 通过认真研读标准标志 0 并不适用于 s 代码,LINUX 只是严格按标准以正确的格式输出值,反而 HPUNIX 下的输出结果是错误的。产生这种错误的根源:是典型过度依赖某些编译器特性(HPUNIX 下的 cc)写下了不可移植的代码。 这会为以后程序的运行、移植埋下隐患。

参考
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值