当前位置:网站首页>sprintf 格式代码使用不规范在不同平台下的表现
sprintf 格式代码使用不规范在不同平台下的表现
2022-06-23 03:58:00 【半点闲】
作者:高玉涵
时间:2022.6.22 10:09
博客:blog.csdn.net/cg_i
大人,时代变了

像很多人一样我学习 C 语言是由 1978 年 Dennis M. Ritchie 和 Brian 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 填充左边未使用的列,变成了空格)。
首先看一下标准是如何描述标志字符和它们的含义(这里只列出我们关心的)。
| 代码 | 参数 | 含义 |
|---|---|---|
| s | char * | 打印一个字符串 |
| 标志 | 含义 |
|---|---|
| 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)写下了不可移植的代码。 这会为以后程序的运行、移植埋下隐患。
参考
边栏推荐
- STC 32-bit 8051 MCU development example tutorial I development environment construction
- 手机无线充电双线圈15W方案SOC英集芯IP6809
- markdown给图片加背景色
- MCS:离散随机变量——Bernoulli分布
- When I was young, I thought my father was omnipotent
- What if win11 cannot record audio? Solution of win11 unable to input sound
- About information disclosure and defense
- 面对新的挑战,成为更好的自己--进击的技术er
- Win软件 - (Net-Framework)已处理证书链,但是在不受信任提供程序信任的根证书中终止
- Meteorological mapping software panoply tutorial (updated from time to time)
猜你喜欢

JVM原理之完整的一次GC流程

MCS:连续随机变量——Student’s t分布

MDM数据清洗功能开发说明

GDB data reading in GDAL (III) of GIS

数据库连接异常:create connection error, url: jdbc:mysql://ip/数据库名, errorCode 0, state 08S01问题处理

Shifu, the open source development platform of the Internet of things, is open for internal testing! Release of the first version of technical documents

SIFT feature point extraction

Memory model of JVM principle

What do Niu B programmers pay attention to when "creating an index"?

read 文件一个字节实际会发生多大的磁盘IO?
随机推荐
104. simple chat room 7: use socket to transfer objects
人脸识别 确定阈值
App hangs~
抽奖 ddd 代码
关于DOS/DDOS攻击和防御
第十六届东北地区大学生程序设计竞赛(热身赛)B-String Value(字符串dp)
基于SSM框架的借阅图书管理系统
Go language - use of packages
CF [1700d] D. River locks (DP, bisection, Mathematics)
WebRTC[47] - WebRTC 保存 YUV 数据的常用方式
账号多开是什么意思?为什么要账号多开?如何安全实现?
H5 适配全面屏
STC 32 Bit 8051 Single Chip Computer Development Example Tutorial one development environment
小时候 觉得爸爸就是天 无所不能~
Markdown add background color to the picture
[microservices | Nacos] list of issues related to the Nacos version
MCS: discrete random variable
Implementation of pyGame music related functions
Jetpack compose menubar Desktop Menu from door opening to entry
Mysql入门学习(三)之视图