如果我尝试访问malloc()区域以外的内存会发生什么?

use*_*466 5 c malloc memory-management

我已经分配了一些内存,char* memoryChunk = malloc ( 80* sizeof(char) + 1);是什么阻止我写入超过81个单元的内存位置?我该怎么做才能防止这种情况发生?

void testStage2(void) {
 char c_str1[20] = "hello";
 char* ut_str1;
 char* ut_str2;

 printf("Starting stage 2 tests\n");
 strcat(c_str1, " world");
 printf("%s\n", c_str1); // nothing exciting, prints "hello world"

 ut_str1 = utstrdup("hello ");
 ut_str1 = utstrrealloc(ut_str1, 20);
 utstrcat(ut_str1, c_str1);
 printf("%s\n", ut_str1); // slightly more exciting, prints "hello hello world"

 utstrcat(ut_str1, " world");
 printf("%s\n", ut_str1); // exciting, should print "hello hello world wo", 'cause there's not enough room for the second world
}

char* utstrcat(char* s, char* suffix){
 int i = strlen(s),j;
 int capacity = *(s - sizeof(unsigned) - sizeof(int));
 for ( j =0; suffix[j] != '\0'; j++){
  if ((i+j-1) == 20)
   return s;
  s[i+j] = suffix[j];
 }
 //strcpy(s, suffix);
 s[i + j] = '\0';
 return s;
}// append the suffix to s
Run Code Online (Sandbox Code Playgroud)

str*_*ger 16

是什么阻止我写入超过81个单元的内存位置?

没有.但是,这样做会导致未定义的行为.这意味着任何事情都可能发生,你不应该依赖它做两次相同的事情.99.999%的时间这是一个错误.

我该怎么做才能防止这种情况发生?

在访问(读取或写入)它们之前,请务必检查指针是否在边界内.始终确保字符串\0在传递给字符串函数时结束.

您可以使用诸如valgrind之类的调试工具来帮助您找到与越界指针和数组访问相关的错误.

stdlib的方法

对于您的代码,您可以使用utstrncat哪些操作utstrcat但是采用最大大小(即缓冲区的大小).

stdc ++的方法

您还可以创建数组结构/类或std::string在C++中使用.例如:

typedef struct UtString {
    size_t buffer_size;
    char *buffer;
} UtString;
Run Code Online (Sandbox Code Playgroud)

然后让你的功能对它进行操作.您甚至可以使用此技术进行动态重新分配(但这似乎不是您想要的).

缓冲结束标记方法

另一种方法是使用缓冲区标记结束,类似于字符串标记的结尾.当您遇到标记时,请不要写入该位置或其前面的位置(对于字符串标记的结尾)(或者您可以重新分配缓冲区以便有更多空间).

例如,如果你有"hello world\0xxxxxx\1"一个字符串(\0字符串标记\1的结尾,是缓冲区标记的结尾,x是随机数据).附加" this is fun"内容如下所示:

hello world\0xxxxxx\1
hello world \0xxxxx\1
hello world t\0xxxx\1
hello world th\0xxx\1
hello world thi\0xx\1
hello world this\0x\1
hello world this \0\1
*STOP WRITING* (next bytes are end of string then end of buffer)
Run Code Online (Sandbox Code Playgroud)

你的问题

您的代码存在问题:

  if ((i+j-1) == 20)
   return s;
Run Code Online (Sandbox Code Playgroud)

虽然您在超越缓冲区之前停止,但您没有标记字符串的结尾.

您可以使用过早break结束for循环而不是返回.这将导致for循环后的代码运行.这将设置字符串标记的结束并返回字符串,这是您想要的.

另外,我担心你的分配可能存在错误.你必须+ 1在字符串之前分配大小,对吗?有一个问题:unsigned通常不是1个字符; 你需要+ sizeof(unsigned)那个.我也会写utget_buffer_size,utset_buffer_size所以你可以更容易地做出改变.

  • 你低估了它成为一个bug的可能性; 在小数点后应该至少有几个9. (2认同)