动态内存分配(malloc):为什么即使我没有分配所需的内存,也会打印整个字符串?

动态内存分配(malloc):为什么即使我没有分配所需的内存,也会打印整个字符串?,c,memory-management,malloc,dynamic-memory-allocation,c-strings,C,Memory Management,Malloc,Dynamic Memory Allocation,C Strings,即使将字符串大小设为1,输入的任何字符串都会被完全打印,为什么会发生这种情况?我认为多余的元素会被忽略 #include <stdio.h> #include <stdlib.h> #include<string.h> int main () { int i; char * buffer; printf ("How long do you want the string? "); scanf ("%d", &i); buffe

即使将字符串大小设为1,输入的任何字符串都会被完全打印,为什么会发生这种情况?我认为多余的元素会被忽略

#include <stdio.h>
#include <stdlib.h>
#include<string.h>

int main ()
{
  int i;
  char * buffer;

  printf ("How long do you want the string? ");
  scanf ("%d", &i);

  buffer = (char*) malloc (i+1);
  if (buffer==NULL) exit (1);

  printf("\n enter string");
  scanf("%s",buffer);                 
  printf ("\n string: %s\n",buffer);
  free (buffer);

  return 0;
}
#包括
#包括
#包括
int main()
{
int i;
字符*缓冲区;
printf(“您希望字符串有多长?”);
scanf(“%d”、&i);
缓冲区=(字符*)malloc(i+1);
如果(buffer==NULL)退出(1);
printf(“\n输入字符串”);
scanf(“%s”,缓冲区);
printf(“\n字符串:%s\n”,缓冲区);
自由(缓冲);
返回0;
}

C不执行边界检查。在数组末尾之外写入只会产生未定义的行为。在对一个特定的C运行时实现进行的经验测试中,字符串的长度与正确的行为产生相同的结果。这并不能保证


(实际上,在大多数现代体系结构上:如果您访问一个您绝对无权访问的地址,这将引发一个例外;然而,这种检查只有一个广泛的解决方案,并且
malloc
尝试紧密地打包分配的空间,以最大限度地减少浪费的RAM;假设您执行阵列acc时没有边界检查所有发生的事情是,您正在存储并随后从您的进程拥有的内存中检索字节;但不能保证您在一般情况下拥有它,而且,即使您在特定的体系结构上这样做,也很可能会覆盖其他内容;因此,您所做的工作依赖于未定义的行为)

C不执行边界检查。在数组末尾之外写入只会产生未定义的行为。在对C运行时的一个特定实现进行的经验测试中,字符串的长度会产生与正确行为相同的结果。这不能保证


(实际上,在大多数现代体系结构上:如果您访问一个您绝对无权访问的地址,这将引发一个例外;然而,这种检查只有一个广泛的解决方案,并且
malloc
尝试紧密地打包分配的空间,以最大限度地减少浪费的RAM;假设您执行阵列acc时没有边界检查所有发生的事情是,您正在存储并随后从您的进程拥有的内存中检索字节;但不能保证您在一般情况下拥有它,而且,即使您在特定的体系结构上这样做,也很可能会覆盖其他内容;因此,您所做的工作依赖于未定义的行为)

因为存在缓冲区溢出,这会调用未定义的行为。任何情况都可能发生。可能发生的事情之一是字符串被完全打印。可能发生的其他事情是程序崩溃,或者黑客攻击用户的计算机时使用此未定义的行为闯入


这就是为什么初学者会评论缓冲区溢出“如果你运气不好,你的程序就会崩溃”,而更有经验的程序员会说“如果你运气好,你的程序就会崩溃”。你不走运。

因为你有一个缓冲区溢出,这会调用未定义的行为。任何事情都可能发生。可能发生的事情之一是字符串被完全打印。可能发生的其他事情是你的程序崩溃,或者黑客攻击用户的计算机时使用此未定义的行为闯入


这就是为什么初学者会评论缓冲区溢出“如果你运气不好,你的程序就会崩溃”,而更有经验的程序员会说“如果你运气好,你的程序就会崩溃”。你运气不好。

为了补充已经给出的优秀答案,下面是一个实践中缓冲区溢出的实例:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main ()
{
  int i;
  char * buffer;
  static char * oops = "oops!";

  printf ("How long do you want the string? ");
  scanf ("%d", &i);

  buffer = (char*) malloc (i+1);
  if (buffer==NULL) exit (1);

  char * other_variable = (char*) malloc (strlen(oops) + 1);

  printf ("\n memory location of buffer: %u\n", (unsigned int)&buffer[0]);
  printf ("\n memory location of other variable: %u\n", (unsigned int)&other_variable[0]);

  printf("\n enter string without spaces longer than %u characters>", (unsigned int)&other_variable[0] - (unsigned int)&buffer[0]);
  scanf("%s",buffer);
  memcpy(other_variable, oops, strlen(oops) + 1);
  printf ("\n string: %s\n",buffer);
  free (buffer);

  return 0;
}
相反,以下示例显示了缓冲区中的缓冲区溢出如何覆盖另一个重要变量:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main ()
{
  int i;
  char * buffer;
  static char * important_command = "Important Command!";

  printf ("How long do you want the string? ");
  scanf ("%d", &i);

  buffer = (char*) malloc (i+1);
  if (buffer==NULL) exit (1);

  char * other_variable = (char*) malloc (strlen(important_command) + 1);
  memcpy(other_variable, important_command, strlen(important_command) + 1);

  printf ("\n memory location of buffer: %u\n", (unsigned int)&buffer[0]);
  printf ("\n memory location of other variable: %u\n", (unsigned int)&other_variable[0]);

  printf("\n enter string without spaces longer than %u characters>", (unsigned int)&other_variable[0] - (unsigned int)&buffer[0]);
  scanf("%s",buffer);
  printf ("\n Contents of buffer: %s\n", buffer);
  printf ("\n Contents of other variable (should be '%s'): %s\n",important_command, other_variable);
  free (buffer);

  return 0;
}
现在您可以想象,如果这是一个SQL命令或类似的命令,那么这可能会对应用程序的操作造成灾难性的影响,并会导致重大的安全风险


您的问题的真正答案是:您应该改用
fgets()
函数(如果需要,然后使用
sscanf()
解析它)

#包括
#包括
#包括
int main()
{
#定义num_长度10
int i;
字符*缓冲区;
缓冲区=(char*)malloc(num_长度);
printf(“您希望字符串有多长?”);
fgets(缓冲区、数量长度、标准输入);
sscanf(缓冲区、%d、&i);
自由(缓冲);
缓冲区=(字符*)malloc(i+1);
如果(buffer==NULL)退出(1);
printf(“\n输入小于%i个字符的字符串>”,i);
fgets(缓冲器,i+1,标准输入);
printf(“\n字符串:%s\n”,缓冲区);
自由(缓冲);
返回0;
}

为了补充已经给出的优秀答案,下面是一个实践中缓冲区溢出的实例:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main ()
{
  int i;
  char * buffer;
  static char * oops = "oops!";

  printf ("How long do you want the string? ");
  scanf ("%d", &i);

  buffer = (char*) malloc (i+1);
  if (buffer==NULL) exit (1);

  char * other_variable = (char*) malloc (strlen(oops) + 1);

  printf ("\n memory location of buffer: %u\n", (unsigned int)&buffer[0]);
  printf ("\n memory location of other variable: %u\n", (unsigned int)&other_variable[0]);

  printf("\n enter string without spaces longer than %u characters>", (unsigned int)&other_variable[0] - (unsigned int)&buffer[0]);
  scanf("%s",buffer);
  memcpy(other_variable, oops, strlen(oops) + 1);
  printf ("\n string: %s\n",buffer);
  free (buffer);

  return 0;
}
相反,以下示例显示了缓冲区中的缓冲区溢出如何覆盖另一个重要变量:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main ()
{
  int i;
  char * buffer;
  static char * important_command = "Important Command!";

  printf ("How long do you want the string? ");
  scanf ("%d", &i);

  buffer = (char*) malloc (i+1);
  if (buffer==NULL) exit (1);

  char * other_variable = (char*) malloc (strlen(important_command) + 1);
  memcpy(other_variable, important_command, strlen(important_command) + 1);

  printf ("\n memory location of buffer: %u\n", (unsigned int)&buffer[0]);
  printf ("\n memory location of other variable: %u\n", (unsigned int)&other_variable[0]);

  printf("\n enter string without spaces longer than %u characters>", (unsigned int)&other_variable[0] - (unsigned int)&buffer[0]);
  scanf("%s",buffer);
  printf ("\n Contents of buffer: %s\n", buffer);
  printf ("\n Contents of other variable (should be '%s'): %s\n",important_command, other_variable);
  free (buffer);

  return 0;
}
现在您可以想象,如果这是一个SQL命令或类似的命令,那么这可能会对应用程序的操作造成灾难性的影响,并会导致重大的安全风险


您的问题的真正答案是:您应该改用
fgets()
函数(如果需要,然后使用
sscanf()
解析它)

#包括
#包括
#包括
int main()
{
#定义num_长度10
int i;
字符*缓冲区;
缓冲区=(char*)malloc(num_长度);
printf(“您希望字符串有多长?”);
fgets(缓冲区、数量长度、标准输入);
sscanf(缓冲区、%d、&i);
自由(缓冲);
缓冲区=(字符*)malloc(i+1);
如果(buffer==NULL)退出(1);
printf(“\n输入小于%i个字符的字符串>”,i);
fgets(缓冲器,i+1,标准输入);
printf(“\n字符串:%s\n”,缓冲区);
自由(缓冲);
返回0;
}

您应该使用变量或define,而不是10。当代码变大且您已经有一段时间没有使用它时,您会惊讶地发现,丢失需要更改的值是多么容易
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main ()
{
  #define num_length 10

  int i;
  char * buffer;
  buffer = (char*) malloc (num_length);

  printf ("How long do you want the string? ");
  fgets(buffer, num_length, stdin);
  sscanf (buffer, "%d", &i);
  free(buffer);

  buffer = (char*) malloc (i+1);
  if (buffer==NULL) exit (1);

  printf("\n enter string less than %i characters>", i);
  fgets(buffer, i+1, stdin);
  printf ("\n string: %s\n",buffer);
  free (buffer);

  return 0;
}