4
int main(void)
{
    char name1[5];
    int count;
    printf("Please enter names\n");
    count = scanf("%s",name1);
    printf("You entered name1 %s\n",name1);
    return 0;
}

当我输入超过 5 个字符时,它会打印我输入的字符,超过 5 个,但 char 数组声明为:

char name1[5];

为什么会这样

4

6 回答 6

12

因为字符存储在“存储空间”之后的地址上。这是非常危险的,可能导致崩溃。

例如,假设您输入 name: Michael 并且 name1 变量从 0x1000 开始。

name1: M       i     c      h      a      e      l     \0
      0x1000 0x1001 0x1002 0x1003 0x1004 0x1005 0x1006 0x1007
      [................................]

分配的空间显示为 [...] 这意味着从 0x1005 开始的内存被覆盖。

解决方案:

仅复制 5 个字符(包括末尾的 \0)或在复制之前检查输入字符串的长度。

于 2013-07-15T14:57:24.080 回答
6

这是未定义的行为,您正在写入超出分配内存的范围。任何事情都可能发生,包括看似正常工作的程序。

C99 草案标准部分J.2 未定义行为说:

在以下情况下,行为未定义:

并包含以下项目符号:

数组下标超出范围,即使一个对象显然可以使用给定的下标访问(如在给定声明 int a[4][5] 的左值表达式 a[1][7] 中)(6.5.6)。

这适用于更一般的情况,因为 E1[E2] 与 (*((E1)+(E2))) 相同。

于 2013-07-15T14:59:46.757 回答
2

这是未定义的行为,你不能指望它。它只是碰巧工作,它可能无法在另一台机器上工作。

为避免缓冲区溢出,请使用

fgets(name1, sizeof(name1) - 1, stdin);

或在 C11 中

gets_s(name1, sizeof(name1) - 1);
于 2013-07-15T14:59:57.543 回答
2

另一个让事情更清楚的例子:

 #include <stdio.h> 

 int array[5] ;

 int main ( void )
 {

  array[-1] = array[-1] ; // sound strange ??

  printf ( "%d" , array[-1] ) ; // but work !!
  return 0 ;
 }

在这种情况下,数组位于地址中,并且您在该地址之前或之后获得数字,但这是未定义的行为,除非您知道自己在做什么。指针与 ++ 或 -- 一起使用!

于 2013-07-15T16:11:12.850 回答
1

从其他答案中可以清楚地看出,这对您的程序构成了某种漏洞。

从中可以学到什么?让我们假设:

 int func(void) 
{
 char buffer[1];
 ...

在 C 编译器的几乎所有实现中,此处生成的代码都会创建一个本地堆栈区域,并使您能够通过buffer. 在这个堆栈上还保存着其他重要的数据,例如:函数返回给它的调用者后要执行的下一个代码行的地址。

因此,理论上你可以:

  • 在输入函数中输入大量代码,
  • 创建一个代码,定义(以二进制代码)一个做一些丑陋的新函数,
  • 如果您将其写入缓冲区边界之外,则使用新函数将具有的地址覆盖正确的返回地址(在堆栈上)。

这称为缓冲区溢出漏洞,您可以在此处(以及许多其他地方)阅读。

于 2013-07-15T15:15:06.887 回答
1

是的,它在 C 中是允许的,因为没有边界检查。

于 2013-07-15T16:53:29.543 回答