sprintf缓冲区大小

3
我是一名初级程序员,通常可以解决自己的问题。这次我成功解决了问题,但仍然困扰着我。一个朋友建议我向社区寻求帮助。
我正在尝试在C语言中打印数字。我有一个使用sprintf的函数来实现这个功能。这些数字应该永远不会超过2位数,所以我使用了一个2字符缓冲区。但不知怎么的,这导致了一个无限循环,因为它修改了传递给sprintf的变量之一,但增加缓冲区大小可以解决这个问题。
以下是出错的代码:
#include <stdio.h>

void printarray(int array[][4]) {
  int y;
  int z;
  char buf[2];
  for (y=0; y<4; y++) {
    for (z=0; z<4; z++) {
      sprintf(buf, "%d", array[y][z]);
      printf("buf is %s, y is %d and z is %d\n",buf,y,z);
    }   
  }
}

int main() {
  int arr[4][4] = {  {1,2,3,4},{5,6,7,8},{9,10,11,12},{13,14,15,0}  };  

  printarray(arr);

  return 0;
}

一旦y达到2,它就会被重置为0,因此进入无限循环。将buf[2]更改为buf[8]可解决此问题。

2
当buf溢出时,无限循环发生的原因是因为sprintf将buf [2]设置为'\0'。可能在堆栈上,buf后面跟着z(按小端序)。因此,z的低字节被设置为0,由于高字节已经为0,因此每次都将z设置为0。 - jstanley
3个回答

13

你忘记了NUL终止符。 在C语言中,字符串需要一个额外的字符作为终止符,所以char buf[2]应该改为char buf[3]以容纳10到99之间的数字。

顺便提一下,你的代码展示了为什么sprintf很危险,因为它可能会写入超出输出缓冲区的内容并启用堆栈溢出攻击。更好的选择是使用snprintf


3

C字符串以空字符结尾。如果你有两个字符(例如“10”),你需要一个大小为2+1的缓冲区来存储空字符。

sprintf()将此添加到缓冲区的末尾;在当前情况下,由于没有提供足够的空间,实际上存在缓冲区溢出。

现代、更安全的方法是使用snprintf(),它需要提供缓冲区的长度。


2
我假设 sprintf 在生成字符串末尾添加了一个 \0。例如,如果您打印数字 99,则在缓冲区中会得到 "99\0",因此对于长度为 2 的缓冲区会导致问题。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接