是什么导致我的Brainfuck transpiler的输出C文件中出现中止陷阱?

2024-05-04 18:20:36 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在基于Brainfuck的Wikipedia page中描述的翻译,开发一个C到Brainfuck的transpiler。我测试过的每个程序都能完美地工作,直到最后。开始时,我分配一个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}")

这个脑力操程序是来自here的Sierpinski三角形。我用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) 

1条回答
网友
1楼 · 发布于 2024-05-04 18:20:36

这样做:

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的调用

相关问题 更多 >