是什么导致我的 Brainfuck 转译器的输出 C 文件中出现中止陷阱?
What is causing the abort trap in the output C file for my Brainfuck transpiler?
我正在开发 C 到 Brainfuck 的转译器,基于 Brainfuck Wikipedia page 中描述的翻译。我测试过的每个程序都能完美运行,直到最后。一开始,我分配了一个 30000 字节的数组 char* ptr = malloc(30000 * sizeof(char));
,最后我通过 free(ptr);
释放了它。我的转译器如下:
def make_tokens(chars):
return [char for char in chars if char in {">", "<", "+", "-", ".", ",", "[", "]"}]
def translate_instruction(i):
return {">": "++ptr;",
"<": "--ptr;",
"+": "++*ptr;",
"-": "--*ptr;",
".": "putchar(*ptr);",
",": "*ptr = getchar();",
"[": "while (*ptr) {",
"]": "}"}[i] + "\n"
def to_c(instructions):
with open("bfc.c", "w") as c_file:
for header in ("stdio", "stdlib", "string"):
c_file.write(f"#include <{header}.h>\n")
c_file.write("\nint main() {\n\tchar* ptr = malloc(30000 * sizeof(char));\n")
c_file.write("\tmemset(ptr, 0, 30000);\n")
indentation = 1
for i in make_tokens(instructions):
c_file.write("\t" * indentation + translate_instruction(i))
if i == "[": indentation += 1
elif i == "]": indentation -= 1
c_file.write("\tfree(ptr);\n}")
这个 Brainfuck 程序是 Sierpinski 的三角形,来自 here. I verified it with this 在线解释器。
to_c("""++++++++[>+>++++<<-]>++>>+<[-[>>+<<-]+>>]>+[
-<<<[
->[+[-]+>++>>>-<<]<[<]>>++++++[<<+++++>>-]+<<++.[-]<<
]>.>+[>>]>+
]""")
我的程序生成以下 C 代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
char* ptr = malloc(30000 * sizeof(char));
memset(ptr, 0, 30000);
++*ptr;
++*ptr;
++*ptr;
++*ptr;
++*ptr;
++*ptr;
++*ptr;
++*ptr;
while (*ptr) {
++ptr;
++*ptr;
++ptr;
++*ptr;
++*ptr;
++*ptr;
++*ptr;
--ptr;
--ptr;
--*ptr;
}
++ptr;
++*ptr;
++*ptr;
++ptr;
++ptr;
++*ptr;
--ptr;
while (*ptr) {
--*ptr;
while (*ptr) {
++ptr;
++ptr;
++*ptr;
--ptr;
--ptr;
--*ptr;
}
++*ptr;
++ptr;
++ptr;
}
++ptr;
++*ptr;
while (*ptr) {
--*ptr;
--ptr;
--ptr;
--ptr;
while (*ptr) {
--*ptr;
++ptr;
while (*ptr) {
++*ptr;
while (*ptr) {
--*ptr;
}
++*ptr;
++ptr;
++*ptr;
++*ptr;
++ptr;
++ptr;
++ptr;
--*ptr;
--ptr;
--ptr;
}
--ptr;
while (*ptr) {
--ptr;
}
++ptr;
++ptr;
++*ptr;
++*ptr;
++*ptr;
++*ptr;
++*ptr;
++*ptr;
while (*ptr) {
--ptr;
--ptr;
++*ptr;
++*ptr;
++*ptr;
++*ptr;
++*ptr;
++ptr;
++ptr;
--*ptr;
}
++*ptr;
--ptr;
--ptr;
++*ptr;
++*ptr;
putchar(*ptr);
while (*ptr) {
--*ptr;
}
--ptr;
--ptr;
}
++ptr;
putchar(*ptr);
++ptr;
++*ptr;
while (*ptr) {
++ptr;
++ptr;
}
++ptr;
++*ptr;
}
free(ptr);
}
clang
的编译和 运行 的输出是这样的:
*
* *
* *
* * * *
* *
* * * *
* * * *
* * * * * * * *
* *
* * * *
* * * *
* * * * * * * *
* * * *
* * * * * * * *
* * * * * * * *
* * * * * * * * * * * * * * * *
* *
* * * *
* * * *
* * * * * * * *
* * * *
* * * * * * * *
* * * * * * * *
* * * * * * * * * * * * * * * *
* * * *
* * * * * * * *
* * * * * * * *
* * * * * * * * * * * * * * * *
* * * * * * * *
* * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
a.out(91318,0x11b627e00) malloc: *** error for object 0x7fb354808883: pointer being freed was not allocated
a.out(91318,0x11b627e00) malloc: *** set a breakpoint in malloc_error_break to debug
Abort trap: 6
如您所见,程序运行得很好,直到结束。 free
是导致中止陷阱的原因,我不明白这是因为我是通过堆而不是堆栈分配数组的。 LLDB 在这里对我帮助不大。这太令人困惑了!有谁知道我做错了什么?
Process 93919 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
frame #0: 0x0000000100003f47 a.out`main at bfc.c:122:7
119 ++ptr;
120 ++*ptr;
121 }
-> 122 free(ptr);
123 }
Target 0: (a.out) stopped.
(lldb) n
a.out(93919,0x1000e7e00) malloc: *** error for object 0x100808883: pointer being freed was not allocated
a.out(93919,0x1000e7e00) malloc: *** set a breakpoint in malloc_error_break to debug
Process 93919 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGABRT
frame #0: 0x00007fff20340462 libsystem_kernel.dylib`__pthread_kill + 10
libsystem_kernel.dylib`__pthread_kill:
-> 0x7fff20340462 <+10>: jae 0x7fff2034046c ; <+20>
0x7fff20340464 <+12>: mov rdi, rax
0x7fff20340467 <+15>: jmp 0x7fff2033a6a1 ; cerror_nocancel
0x7fff2034046c <+20>: ret
Target 0: (a.out) stopped.
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGABRT
* frame #0: 0x00007fff20340462 libsystem_kernel.dylib`__pthread_kill + 10
frame #1: 0x00007fff2036e610 libsystem_pthread.dylib`pthread_kill + 263
frame #2: 0x00007fff202c1720 libsystem_c.dylib`abort + 120
frame #3: 0x00007fff201a2430 libsystem_malloc.dylib`malloc_vreport + 548
frame #4: 0x00007fff201a54c8 libsystem_malloc.dylib`malloc_report + 151
frame #5: 0x0000000100003f50 a.out`main at bfc.c:122:2
frame #6: 0x00007fff20389621 libdyld.dylib`start + 1
(lldb)
这样做:
char* ptr = malloc(30000 * sizeof(char));
memset(ptr, 0, 30000);
char *orig = ptr;
// Code
free(orig);
由于您要递增和递减指针 ptr
,您当然不能相信它指向与初始化时相同的位置。如果是这样,那可能性很小。
为了养成良好的习惯:
sizeof(char) 始终为 1,因此要么使用 malloc(30000 * sizeof *ptr)
(无论类型如何都有效)或简单地使用 malloc(30000)
使用 calloc
而不是 malloc
来保存对 memset
的调用
char *buffer = calloc(30000, sizeof *buffer);
char *ptr = buffer;
// Code
free(buffer);
但说实话。虽然确保始终释放资源通常是避免内存泄漏的好事,但在 main
函数的末尾通常不需要这样做。除非您正在编写嵌入式系统、操作系统或非常罕见和特殊的东西,否则您可以相信操作系统会在程序退出时为您释放所有分配的内存。对于此应用程序,您可以根据需要跳过对 free
的调用。
我正在开发 C 到 Brainfuck 的转译器,基于 Brainfuck Wikipedia page 中描述的翻译。我测试过的每个程序都能完美运行,直到最后。一开始,我分配了一个 30000 字节的数组 char* ptr = malloc(30000 * sizeof(char));
,最后我通过 free(ptr);
释放了它。我的转译器如下:
def make_tokens(chars):
return [char for char in chars if char in {">", "<", "+", "-", ".", ",", "[", "]"}]
def translate_instruction(i):
return {">": "++ptr;",
"<": "--ptr;",
"+": "++*ptr;",
"-": "--*ptr;",
".": "putchar(*ptr);",
",": "*ptr = getchar();",
"[": "while (*ptr) {",
"]": "}"}[i] + "\n"
def to_c(instructions):
with open("bfc.c", "w") as c_file:
for header in ("stdio", "stdlib", "string"):
c_file.write(f"#include <{header}.h>\n")
c_file.write("\nint main() {\n\tchar* ptr = malloc(30000 * sizeof(char));\n")
c_file.write("\tmemset(ptr, 0, 30000);\n")
indentation = 1
for i in make_tokens(instructions):
c_file.write("\t" * indentation + translate_instruction(i))
if i == "[": indentation += 1
elif i == "]": indentation -= 1
c_file.write("\tfree(ptr);\n}")
这个 Brainfuck 程序是 Sierpinski 的三角形,来自 here. I verified it with this 在线解释器。
to_c("""++++++++[>+>++++<<-]>++>>+<[-[>>+<<-]+>>]>+[
-<<<[
->[+[-]+>++>>>-<<]<[<]>>++++++[<<+++++>>-]+<<++.[-]<<
]>.>+[>>]>+
]""")
我的程序生成以下 C 代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
char* ptr = malloc(30000 * sizeof(char));
memset(ptr, 0, 30000);
++*ptr;
++*ptr;
++*ptr;
++*ptr;
++*ptr;
++*ptr;
++*ptr;
++*ptr;
while (*ptr) {
++ptr;
++*ptr;
++ptr;
++*ptr;
++*ptr;
++*ptr;
++*ptr;
--ptr;
--ptr;
--*ptr;
}
++ptr;
++*ptr;
++*ptr;
++ptr;
++ptr;
++*ptr;
--ptr;
while (*ptr) {
--*ptr;
while (*ptr) {
++ptr;
++ptr;
++*ptr;
--ptr;
--ptr;
--*ptr;
}
++*ptr;
++ptr;
++ptr;
}
++ptr;
++*ptr;
while (*ptr) {
--*ptr;
--ptr;
--ptr;
--ptr;
while (*ptr) {
--*ptr;
++ptr;
while (*ptr) {
++*ptr;
while (*ptr) {
--*ptr;
}
++*ptr;
++ptr;
++*ptr;
++*ptr;
++ptr;
++ptr;
++ptr;
--*ptr;
--ptr;
--ptr;
}
--ptr;
while (*ptr) {
--ptr;
}
++ptr;
++ptr;
++*ptr;
++*ptr;
++*ptr;
++*ptr;
++*ptr;
++*ptr;
while (*ptr) {
--ptr;
--ptr;
++*ptr;
++*ptr;
++*ptr;
++*ptr;
++*ptr;
++ptr;
++ptr;
--*ptr;
}
++*ptr;
--ptr;
--ptr;
++*ptr;
++*ptr;
putchar(*ptr);
while (*ptr) {
--*ptr;
}
--ptr;
--ptr;
}
++ptr;
putchar(*ptr);
++ptr;
++*ptr;
while (*ptr) {
++ptr;
++ptr;
}
++ptr;
++*ptr;
}
free(ptr);
}
clang
的编译和 运行 的输出是这样的:
*
* *
* *
* * * *
* *
* * * *
* * * *
* * * * * * * *
* *
* * * *
* * * *
* * * * * * * *
* * * *
* * * * * * * *
* * * * * * * *
* * * * * * * * * * * * * * * *
* *
* * * *
* * * *
* * * * * * * *
* * * *
* * * * * * * *
* * * * * * * *
* * * * * * * * * * * * * * * *
* * * *
* * * * * * * *
* * * * * * * *
* * * * * * * * * * * * * * * *
* * * * * * * *
* * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
a.out(91318,0x11b627e00) malloc: *** error for object 0x7fb354808883: pointer being freed was not allocated
a.out(91318,0x11b627e00) malloc: *** set a breakpoint in malloc_error_break to debug
Abort trap: 6
如您所见,程序运行得很好,直到结束。 free
是导致中止陷阱的原因,我不明白这是因为我是通过堆而不是堆栈分配数组的。 LLDB 在这里对我帮助不大。这太令人困惑了!有谁知道我做错了什么?
Process 93919 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
frame #0: 0x0000000100003f47 a.out`main at bfc.c:122:7
119 ++ptr;
120 ++*ptr;
121 }
-> 122 free(ptr);
123 }
Target 0: (a.out) stopped.
(lldb) n
a.out(93919,0x1000e7e00) malloc: *** error for object 0x100808883: pointer being freed was not allocated
a.out(93919,0x1000e7e00) malloc: *** set a breakpoint in malloc_error_break to debug
Process 93919 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGABRT
frame #0: 0x00007fff20340462 libsystem_kernel.dylib`__pthread_kill + 10
libsystem_kernel.dylib`__pthread_kill:
-> 0x7fff20340462 <+10>: jae 0x7fff2034046c ; <+20>
0x7fff20340464 <+12>: mov rdi, rax
0x7fff20340467 <+15>: jmp 0x7fff2033a6a1 ; cerror_nocancel
0x7fff2034046c <+20>: ret
Target 0: (a.out) stopped.
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGABRT
* frame #0: 0x00007fff20340462 libsystem_kernel.dylib`__pthread_kill + 10
frame #1: 0x00007fff2036e610 libsystem_pthread.dylib`pthread_kill + 263
frame #2: 0x00007fff202c1720 libsystem_c.dylib`abort + 120
frame #3: 0x00007fff201a2430 libsystem_malloc.dylib`malloc_vreport + 548
frame #4: 0x00007fff201a54c8 libsystem_malloc.dylib`malloc_report + 151
frame #5: 0x0000000100003f50 a.out`main at bfc.c:122:2
frame #6: 0x00007fff20389621 libdyld.dylib`start + 1
(lldb)
这样做:
char* ptr = malloc(30000 * sizeof(char));
memset(ptr, 0, 30000);
char *orig = ptr;
// Code
free(orig);
由于您要递增和递减指针 ptr
,您当然不能相信它指向与初始化时相同的位置。如果是这样,那可能性很小。
为了养成良好的习惯:
sizeof(char) 始终为 1,因此要么使用
malloc(30000 * sizeof *ptr)
(无论类型如何都有效)或简单地使用malloc(30000)
使用
的调用calloc
而不是malloc
来保存对memset
char *buffer = calloc(30000, sizeof *buffer);
char *ptr = buffer;
// Code
free(buffer);
但说实话。虽然确保始终释放资源通常是避免内存泄漏的好事,但在 main
函数的末尾通常不需要这样做。除非您正在编写嵌入式系统、操作系统或非常罕见和特殊的东西,否则您可以相信操作系统会在程序退出时为您释放所有分配的内存。对于此应用程序,您可以根据需要跳过对 free
的调用。