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