Search code examples
csegmentation-faultheap-memorypass-by-referencec-strings

Malloc is not allocating enough memory, despite being hardcoded to allocate exactly two characters


I have a function that, given a pointer to a char array string (Passed by reference), a char character (passed by value), and an unsigned short int length (passed by reference), is supposed to, if string has already been allocated, re-allocate string one char longer in size ((length + 2) * sizeof(char)), and then increment length by one, setting the last two characters equal to character and the null terminator, respectively. If string has not yet been allocated, it instead allocates string with two characters in length (2 * sizeof(char)), before initializing the characters. However, when it sets the last character (index of length), it segfaults.

void appendCharacterToString(char** string, char character, unsigned short int * length) {

    if (*string != NULL) {
        *string = (char* ) realloc(*string, (*length + 2) * sizeof(char));
        ++*length;
    }
    else {
        *string = (char* ) malloc(2 * sizeof(char));
        *length = 1;
    }

    *string[*length - 1] = character;
    *string[*length] = 0;
}

When running it in gdb, I was able to determine that the initially allocated array, despite being hardcoded to allocate two chars worth of memory, only allocate one char, which, strangely enough, is already initialized to null.

Program received signal SIGSEGV, Segmentation fault.
appendCharacterToString (string=0x7fffffffe0d8,
    character=110 'n', length=0x7fffffffe0d0)
    at hangmanstringutils.c:24
24          *string[*length] = 0;
(gdb)
(gdb) print string
$1 = (char **) 0x7fffffffe0d8
(gdb) print *string
$2 = 0x5555555592a0 "n"
(gdb) print *length
$3 = 1
(gdb) continue
Continuing.

Program terminated with signal SIGSEGV, Segmentation fault.
The program no longer exists.
(gdb) break hangmanstringutils.c:12
Breakpoint 1 at 0x555555555612: file hangmanstringutils.c, line 14.
(gdb) run
Starting program: /home/joseph/code/hangman/hangman
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/usr/lib/libthread_db.so.1".

Breakpoint 1, appendCharacterToString (
    string=0x7fffffffe0d8, character=110 'n',
    length=0x7fffffffe0d0) at hangmanstringutils.c:14
14          if (*string != NULL) {
(gdb) print *string
$4 = 0x0
(gdb) print *length
$5 = 0
(gdb) break 19
Breakpoint 2 at 0x55555555565a: file hangmanstringutils.c, line 19.
(gdb) continue
Continuing.

Breakpoint 2, appendCharacterToString (
    string=0x7fffffffe0d8, character=110 'n',
    length=0x7fffffffe0d0) at hangmanstringutils.c:19
19              *string = (char* ) malloc(2 * sizeof(char));
(gdb) print *string
$6 = 0x0
(gdb) print *length
$7 = 0
(gdb) break 21
Breakpoint 3 at 0x555555555677: file hangmanstringutils.c, line 23.
(gdb) continue
Continuing.

Breakpoint 3, appendCharacterToString (
    string=0x7fffffffe0d8, character=110 'n',
    length=0x7fffffffe0d0) at hangmanstringutils.c:23
23          *string[*length - 1] = character;
(gdb) print *string
$8 = 0x5555555592a0 ""
(gdb) print *length
$9 = 1
(gdb) print *string[0]
$10 = 0 '\000'
(gdb) print *string[1]
Cannot access memory at address 0x2c0
(gdb)

Solution

  • print *string[1] does not access the character at index 1 of *string. It is *(string[1]), not (*string)[1]. It accesses the character pointed to by string[1], and string[1] denotes the bytes in memory after *string. You want print (*string)[1] in the debugger command. Similarly, in the source code, you want:

    (*string)[*length - 1] = character;
    (*string)[*length] = 0;