问题描述
我正在根据 Brainfuck 的 Wikipedia page 中描述的翻译开发 C 到 Brainfuck 的转译器。我测试过的每个程序都运行良好,直到最后。一开始,我分配了一个 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,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 程序是谢尔宾斯基三角形,来自 here。我通过 this 在线翻译进行了验证。
to_c("""++++++++[>+>++++<<-]>++>>+<[-[>>+<<-]+>>]>+[
-<<<[
->[+[-]+>++>>>-<<]<[<]>>++++++[<<+++++>>-]+<<++.[-]<<
]>.>+[>>]>+
]""")
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
char* ptr = malloc(30000 * sizeof(char));
memset(ptr,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,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,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,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
的调用。