首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >sprintf精密类型转换导致编译警告

sprintf精密类型转换导致编译警告
EN

Stack Overflow用户
提问于 2022-04-29 12:05:22
回答 2查看 173关注 0票数 2

我有下面的C++代码,它发出如下所示的编译警告。

案例1:

代码语言:javascript
运行
复制
char temp_buffer[80];
double **data;
....
sprintf(temp_buffer, "%.*g", sizeof(temp_buffer), **data);

汇编警告:

代码语言:javascript
运行
复制
extension.C:1031:41: warning: field precision specifier '.*' expects argument of type 'int', but argument 3 has type 'long unsigned int' [-Wformat=]
 1031 |                 sprintf(temp_buffer, "%.*g", sizeof(temp_buffer), **data);
      |                                       ~~^~   ~~~~~~~~~~~~~~~~~~~
      |                                         |    |
      |                                         int  long unsigned int

我正在通过将sizeof类型转换为int来修复警告,如下所示。

案例2:

代码语言:javascript
运行
复制
char temp_buffer[80];
double **data;
...
sprintf(temp_buffer, "%.*g", (int)sizeof(temp_buffer), **data);

但现在我收到了以下警告:

代码语言:javascript
运行
复制
extension/extension.C:1031:39: warning: \u2018%.*g\u2019 directive writing between 1 and 87 bytes into a region of size 80 [-Wformat-overflow=]
 1031 |                 sprintf(temp_buffer, "%.*g", (int)sizeof(temp_buffer), **data);
      |                                       ^~~~
extension/extension.C:1031:38: note: assuming directive output of 86 bytes
 1031 |                 sprintf(temp_buffer, "%.*g", (int)sizeof(temp_buffer), **data);
      |                                      ^~~~~~
In file included from /usr/include/stdio.h:873,
                 from /opt/python/python-3.9/include/python3.9/Python.h:25,
                 from /codemill/dhamodha/projects/base/src/lib/despython/desnumpyinit.h:19,
                 from extension/extension.C:8:
/usr/include/bits/stdio2.h:36:34: note: \u2018__builtin___sprintf_chk\u2019 output between 2 and 88 bytes into a destination of size 80
   36 |   return __builtin___sprintf_chk (__s, __USE_FORTIFY_LEVEL - 1,
      |          ~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   37 |       __bos (__s), __fmt, __va_arg_pack ());

我的假设是,“*”在精度上内部强制long unsigned intint。但是如果是这样的话,如果我引入sizeof类型转换,那么新的警告就不会发生了。

有人能帮我弄明白怎么回事并解决警告吗?

案例3:

假设我将temp_buffer修改为大小为8,那么我会看到两个警告:

代码语言:javascript
运行
复制
extension/extension.C:1033:20: warning: \u2018%.*g\u2019 directive writing between 1 and 310 bytes into a region of size 8 [-Wformat-overflow=]
 1033 |   sprintf(temp_b, "%.*g", sizeof(temp_buffer), **data);
      |                    ^~~~
extension/extension.C:1033:19: note: assuming directive output of 12 bytes
 1033 |   sprintf(temp_b, "%.*g", sizeof(temp_buffer), **data);
      |                   ^~~~~~
In file included from /usr/include/stdio.h:873,
                 from /opt/python/python-3.9/include/python3.9/Python.h:25,
                 from /codemill/dhamodha/projects/base/src/lib/despython/desnumpyinit.h:19,
                 from extension/extension.C:8:
/usr/include/bits/stdio2.h:36:34: note: \u2018__builtin___sprintf_chk\u2019 output between 2 and 311 bytes into a destination of size 8
   36 |   return __builtin___sprintf_chk (__s, __USE_FORTIFY_LEVEL - 1,
      |          ~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   37 |       __bos (__s), __fmt, __va_arg_pack ());
      |       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
EN

Stack Overflow用户

发布于 2022-04-29 12:19:06

您可以在使用前指定宽度:

代码语言:javascript
运行
复制
int width = sizeof(temp_buffer);
sprintf(temp_buffer, "%.*g", width, **data);

这一警告将消失,但这并不能解决根本问题。如果您想避免潜在的缓冲区溢出,一个合理的选项是使用正确的大小:

代码语言:javascript
运行
复制
sprintf(temp_buffer, "%.*g", (int)(sizeof(temp_buffer)) - 8, **data);

在您的原始代码中,您的意思是您希望小数位数是缓冲区的大小,这就没有留下符号、科学符号和nul字节的空间。您需要传递一个足够小的十进制部件大小,以便所有这些都能够满足目标缓冲区的要求。

尽管编译器提到了7个字节,但我相信8个字节更合适,2个用于符号,1个用于点,1个用于e,3个用于指数,1个用于nul字节。

虽然我认为有一个足够大到有最长可能表示的缓冲区可能更安全,但考虑到双可能高达758字节的最长可能表示(尽管并非所有编译器都是这样),一个可以占用800个字节的缓冲区将能够处理这个问题,假设上面的字节计数与gcc 11.3的编译一致。

因此,正确的方法是使用安全得多的snprintf,它允许将最大缓冲区大小作为参数传递:

代码语言:javascript
运行
复制
snprintf(buffer, sizeof buffer, "%.*g", (int)sizeof buffer - 8, **data);

对于不同的用例,该行为未定义。没有强制转换,因为您没有将正确的参数类型传递给...参数第三参数 (在大多数情况下,unsigned long的大小大于int),因为编译器没有被授权发出任何警告。它确实可以被认为是礼貌的,例如,clang没有发出警告,按照上面链接的未定义行为的标准定义,这是非常好的。

要知道为什么在某些情况下有两种不同的警告,而在其他情况下只有一种是编译器特有的,我认为试图找出原因是推测性的。

关于警告中发出的溢出值,我感到很好奇,并提出了一个问题,答案是明确的:

票数 2
EN
查看全部 2 条回答
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/72057715

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档