Wat veroorzaakt een segmentatiefout (core dump) in C?

Ik probeer een Hamming-codeprogramma in C te schrijven. Ik krijg echter steeds een Segmentation Fault (Core Dumped)-fout wanneer ik probeer de ./a.out uit te voeren na het compileren. Het compileert zonder fouten en ik begrijp dat deze fout kan optreden bij het adresseren van vrijgekomen ruimte of het wijzigen van een letterlijke tekenreeks. Ik geloof niet dat ik een van die dingen doe, ik heb gewoon een simpele matrix die ik invul en kruiselings controleer. Elk inzicht in het probleem wordt op prijs gesteld, ik heb de code die ik tot nu toe heb hieronder achtergelaten:

Dit is voor een huiswerkprobleem waarbij een Hamming-codeprogramma moet worden gemaakt om data.dat-invoer en uitvoer naar een bestand een gesorteerde lijst van enen en nullen te verwerken

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
FILE *fp;
FILE *fpOut;
int main(int argc, char *argv[])
{
        fp = fopen(argv[1], "r");
        fpOut = fopen("sortedCodeWords.dat", "w");
        int temp;
        int numI = 0;
        int count = 0;
        char matrix1[7];
        if(fp == NULL)
                printf("File can not be opened");
        else
        {
                char ch = getc(fp);
                while(ch != EOF)
                {
                        matrix1[2] = ch;
                        ch = getc(fp);
                        matrix1[4] = ch;
                        ch = getc(fp);
                        matrix1[5] = ch;
                        ch = getc(fp);
                        matrix1[6] = ch;
                        ch = getc(fp);
                        ch = getc(fp);
                        if(ch == '\n')
                        {
                                for(int i = 2; i < 7; i++)
                                {
                                        if(matrix1[i] == '1')
                                            numI++;
                                        i++;
                                }
                                if(numI % 2 == 0)
                                        matrix1[0] = 0;
                                else
                                        matrix1[0] = 1;
                                numI = 0;
                                for(int i = 1; i < 7; i++)
                                {
                                        if(matrix1[i] == '1')
                                                numI++;
                                        if(matrix1[i+1] == '1')
                                                numI++;
                                        i++;
                                        i++;
                                }
                                if(numI % 2 == 0)
                                        matrix1[1] = 0;
                                else
                                        matrix1[1] = 1;
                                numI = 0;
                                for(int i = 4; i < 7; i++)
                                {
                                        if(matrix1[i] == '1')
                                                numI++;
                                }
                                if(numI % 2 == 0)
                                        matrix1[3] = 0;
                                else
                                        matrix1[3] = 1;
                                numI = 0;
                                for (int i = 0; i < 7; i++)
                                {
                                        fprintf(fpOut, "%s", matrix1[i]);
                                }
                                fprintf(fpOut, "\n");
                                ch = getc(fp);
                        }
                        count++;
                }
        }
}

Ik verwacht een uitvoer naar een bestand. Ik kreeg deze fout niet altijd, maar toen ik overstapte van een 2D-array naar 1D-array, krijg ik nu deze fout (ik veranderde omdat ik me realiseerde dat dit niet nodig was)


Antwoord 1, autoriteit 100%

Twee dingen die ik zie. Ten eerste meng je chars met ints in de matrixarray. Ten tweede drukt u de elementen van de matrix af naar een bestand met het “%s”-formaat. “%s” verwacht een null-terminated string waar je chars en ints doorgeeft. Dit zorgt ervoor dat de printf probeert toegang te krijgen tot geheugen dat buiten het bereik is, dus de fout.


Antwoord 2, autoriteit 93%

Ik heb het niet gecompileerd, maar het enige dat me opvalt, is dat het lijkt alsof je mogelijk van het einde van je array gaat waar je naar i<7gaat, maar in één keer een index van i+1gebruiken.

Misschien bouwen met AddressSanitizer ingeschakeld en kijken welke runtime-waarschuwingen je krijgt als je het mogelijke probleem hierboven hebt gecontroleerd. (Het zou gewoon een kwestie moeten zijn van het toevoegen van de volgende vlaggen aan je gcc-opdracht …
-g -fsanitize=adres -fno-omit-frame-pointer


Antwoord 3, autoriteit 100%

Je moet leren hoe je een debugger gebruikt.

Ik neem het risico om aan te nemen dat je hier aan Linux werkt en het debugproces voor je code doorloopt.

Eerst compileren we met foutopsporingsinformatie ingeschakeld.

james@debian:~/code$ gcc foo.c -g

Laten we het nu doen met twee tools, valgrinden gdb


valgrindin het bijzonder is een behoorlijk goede tool, het vangt het op wanneer je het geheugen verknoeit. Het is vrijwel verplicht voor het opsporen van geheugenlekken, maar kan voor veel Seg-fouten worden gebruikt. Het beste van alles is dat het gemakkelijk te gebruiken is en geen interactie vereist.

james@debian:~/code$ valgrind ./a.out foo.dat
==1857== Memcheck, a memory error detector
==1857== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==1857== Using Valgrind-3.14.0 and LibVEX; rerun with -h for copyright info
==1857== Command: ./a.out foo.dat
==1857==
==1857== Conditional jump or move depends on uninitialised value(s)
==1857==    at 0x109381: main (foo.c:61)
==1857==
==1857== Invalid read of size 1
==1857==    at 0x4837C38: __GI_strlen (in /usr/lib/i386-linux-gnu/valgrind/vgpreload_memcheck-x86-linux.so)
==1857==    by 0x48A4786: vfprintf (vfprintf.c:1638)
==1857==    by 0x48AAC57: fprintf (fprintf.c:32)
==1857==    by 0x109437: main (foo.c:91)
==1857==  Address 0x1 is not stack'd, malloc'd or (recently) free'd
==1857==
==1857==
==1857== Process terminating with default action of signal 11 (SIGSEGV)
==1857==  Access not within mapped region at address 0x1
==1857==    at 0x4837C38: __GI_strlen (in /usr/lib/i386-linux-gnu/valgrind/vgpreload_memcheck-x86-linux.so)
==1857==    by 0x48A4786: vfprintf (vfprintf.c:1638)
==1857==    by 0x48AAC57: fprintf (fprintf.c:32)
==1857==    by 0x109437: main (foo.c:91)
==1857==  If you believe this happened as a result of a stack
==1857==  overflow in your program's main thread (unlikely but
==1857==  possible), you can try to increase the size of the
==1857==  main thread stack using the --main-stacksize= flag.
==1857==  The main thread stack size used in this run was 8388608.
==1857==
==1857== HEAP SUMMARY:
==1857==     in use at exit: 688 bytes in 2 blocks
==1857==   total heap usage: 3 allocs, 1 frees, 4,784 bytes allocated
==1857==
==1857== LEAK SUMMARY:
==1857==    definitely lost: 0 bytes in 0 blocks
==1857==    indirectly lost: 0 bytes in 0 blocks
==1857==      possibly lost: 0 bytes in 0 blocks
==1857==    still reachable: 688 bytes in 2 blocks
==1857==         suppressed: 0 bytes in 0 blocks
==1857== Rerun with --leak-check=full to see details of leaked memory
==1857==
==1857== For counts of detected and suppressed errors, rerun with: -v
==1857== Use --track-origins=yes to see where uninitialised values come from
==1857== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
Segmentation fault

Ik lees net de eerste fout, het signaleert een probleem op regel 61 en zegt dat if(matrix1[i] == '1')niet-geïnitialiseerd geheugen gebruikt.

Als je je code met dit in gedachten leest, valt het op dat matrix1[1]nooit wordt geïnitialiseerd, dus er is een bug. Dit is echter niet de Seg-fout.

Een andere fout wordt gemarkeerd op regel 91, die lijkt op de bug, maar die moeilijk te begrijpen is. Dus laten we gdb uitschakelen.


james@debian:~/code$ gdb a.out
GNU gdb (Debian 8.2.1-2) 8.2.1
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "i686-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from a.out...done.
(gdb) run foo.dat
Starting program: /home/james/code/a.out foo.dat
Program received signal SIGSEGV, Segmentation fault.
__strlen_ia32 () at ../sysdeps/i386/i586/strlen.S:51
51      ../sysdeps/i386/i586/strlen.S: No such file or directory.

run foo.datvoert het programma uit. We krijgen snel uw segmentatiefout met wat info.

(gdb) info stack
#0  __strlen_ia32 () at ../sysdeps/i386/i586/strlen.S:51
#1  0xb7e28787 in _IO_vfprintf_internal (s=0x4052c0, format=0x402037 "%s",
    ap=0xbffff52c "\360\021@") at vfprintf.c:1638
#2  0xb7e2ec58 in __fprintf (stream=0x4052c0, format=0x402037 "%s")
    at fprintf.c:32
#3  0x00401438 in main (argc=2, argv=0xbffff614) at foo.c:91
(gdb) frame 3
#3  0x00401438 in main (argc=2, argv=0xbffff614) at foo.c:91
91                          fprintf(fpOut, "%s", matrix1[i]);
(gdb)

info stackDrukt de execution-stapel af. We geven niet om de fout bij een systeembestandsniveau, we geven om de fout in Main.
frame 3schakelt naar frame # 3, waar de hoofdfunctie zich bevindt. Op dit punt heb je misschien al de bug gezien, maar we kunnen dieper graven als het niet voor de hand liggend is.

(gdb) info locals
i = 0
ch = 10 '\n'
temp = <optimized out>
numI = 0
count = 2
matrix1 = "\001\000\061\001\060\061\060"
(gdb)

info localsGeeft alle lokale variabelen weer. Op dit punt hebben we een vrij complete momentopname van wat er aan de hand is, maar gewoon om het uit te spellen …

(gdb) print matrix1[0]
$1 = 1 '\001'
(gdb)

Op dit punt moet je wat C kennen. matrix[0]is gewoon geen geschikt argument voor printf("%s"). Het verwacht een karakteraanwijzer naar een betekenisvolle waarde, maar u geeft het het nummer 1, wat resulteert in een Seg-fout.

Terugkijkend op de fout die we van valgrind kregen, is het nu gemakkelijker te begrijpen, door hetzelfde te zeggen als we hebben afgeleid met behulp van gdb: dat we het nummer 1 probeerden te lezen als een geheugenadres. ..

==1857==    by 0x109437: main (foo.c:91)
==1857==  Address 0x1 is not stack'd, malloc'd or (recently) free'd

Je programma zal fouten blijven vertonen nadat je dit hebt opgelost, daar ben ik zeker van, en toekomstige programma’s zullen soortgelijke fouten hebben, maar met deze twee tools zou je de meeste problemen moeten kunnen opsporen.


Antwoord 4, autoriteit 50%

Als u enen en nullen wilt afdrukken, moet u toewijzen

matrix[i] = '1';

in plaats van

matrix[i] = 1;

Other episodes