Что вызывает прерывание прерывания в выходном файле C для моего Brainfuck transpiler?

#python #c #malloc #sigabrt #brainfuck

#python #c #malloc #sigabrt #brainfuck

Вопрос:

Я работаю над переводом C в Brainfuck transpiler на основе перевода, описанного на странице 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() {ntchar* 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 — это треугольник Серпинского, отсюда. Я проверил это с помощью этого онлайн-интерпретатора.

 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. Вы передаете точно такое же значение указателя free , которому вы передали malloc ? Или вы сначала увеличили его кучу?

2. Вы меняете ptr много раз, а затем передаете его free . Как это может работать?

3. char *tmp = malloc(...), *ptr = tmp; ....code.... free(tmp); казалось бы, логичный подход к определению того, насколько сильно вы удаляли этот указатель от его источника.

4. @WhozCraig Это решило мою проблему, спасибо!

Ответ №1:

Сделайте это:

 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 , если хотите.