我正在使用崩溃处理程序在C中创建一些东西,该处理程序将后跟踪打印到文件中。在一个for循环中连接一个char **长度的字符串,整数从0(起始)增加到1886352492,而不是1.跟踪打印正常。
char **stack;
char stackTrace[] = "";
for (int i = 0; i < levels /*500*/; i++) {
printf("debug %i\n", i); //I prints 0 the first time and 1886352492 after, then terminates the program.
sprintf(stackTrace, "%s%s\n", stackTrace, stack[i]);
printf(stackTrace);
}
答案 0 :(得分:2)
如果您将数字1886352492转换为十六进制,则会得到0x706f746c。请注意这四个字节的ASCII值是多少?分别是'p','o','t'和'l'。
问题是你正在初始化一个长度为0的字符串(如果计算空终止符,则为1):
char stackTrace[] = "";
所以当你尝试在这里写字符串时,如果你写的字符串长于0(很可能),你就会溢出它:
sprintf(stackTrace, "%s%s\n", stackTrace, stack[i]);
如果你的字符串足够长,你甚至可能会导致... Stack Overflow。 (ba dum chshhhhhh)
因此其他随机内存会被您的字符串覆盖,包括存储i
的内存。
解决方案:使用足够的内存声明stackTrace
变量,以存储您需要输入的字符串。
答案 1 :(得分:0)
您正在通过调用sprintf()
覆盖本地堆栈内存 - 这就是导致此UB(未定义行为)的原因。你可以清楚地看到为什么这被称为UB:你无法可靠地告诉&#34;会发生什么&#34;在运行程序之前:)
答案 2 :(得分:0)
未定义的行为。
sprintf()
如果在重叠的对象之间进行复制,则行为未定义。 C11§7.21.6.62
// v--------v and v--------v overlap
sprintf(stackTrace, "%s%s\n", stackTrace, stack[i]); // UB
char stackTrace[] = "";
太小@Charles Srstka。使用stackTrace[]
这样的内存声明char stackTrace[1000] = "";
不就足够了。代码需要以指定的方式附加。
而是声明一个缓冲区并通过跟踪到目前为止使用的字符串的结尾来追加它。
char **stack;
char stackTrace[10000];
stackTrace[0] = '\0';
char *st = stackTrace;
for (int i = 0; i < levels /*500*/; i++) {
printf("debug %i\n", i);
// In some fashion, perform a safe concatenation
size_t remaining_size = &stackTrace[sizeof stackTrace] - st;
sprintf(st, remaining_size, "%s\n", stack[i]);
// update `st` to the end of the string
st += strlen(st);
printf(stackTrace); // do not use stackTrace as a _format_
printf("%s", stackTrace);
}
出于堆栈跟踪的目的,在程序运行的早期分配堆栈跟踪缓冲区是个好主意@YePhIcK - 可能是static
。
// char stackTrace[10000];
static char stackTrace[10000];
出于堆栈跟踪的目的,使用安全追加是至关重要的,因为对堆栈跟踪的需求通常意味着某些事情可能不对。 snprintf()
是复杂的功能。使用strlcpy()/strlcat()
@Groo之类的非标准函数或同等函数是一个好主意。