Schwern
Schwern

Reputation: 165318

Strange memory corruption in C

In answering another question I wrote up what should be some simple code to initialize and print a 2D array. But something very strange is happening.

#include <stdio.h>
#include <stdlib.h>

void print_row( const int *row, size_t num_cols ) {
    printf("%p\n", (void*)row);
    for( size_t col_num = 0; col_num < num_cols; col_num++ ) {
        printf(" %2d ", row[col_num]);
    }
    puts("");
}

int **make_board( const size_t num_rows, const size_t num_cols ) {
    int **board = malloc( sizeof(int) * num_rows );

    for( size_t row_num = 0; row_num < num_rows; row_num++ ) {
        int *row = calloc( num_cols, sizeof(int) );
        board[row_num] = row;
        print_row(row, num_cols);
    }

    return board;
}

void print_board( int **board, const size_t num_rows, const size_t num_cols ) {
    for( size_t row_num = 0; row_num < num_rows; row_num++ ) {
        const int *row = board[row_num];
        print_row(row, num_cols);
    }
}

int main() {
    size_t num_rows = 6;
    size_t num_cols = 4;
    puts("Making the board");
    int **board = make_board(num_rows, num_cols);
    puts("Printing the board");
    print_board(board, num_rows, num_cols);
}

Running it, I occasionally get one corrupted row, but only ever in print_board never in make_board.

cc -Wall -Wshadow -Wwrite-strings -Wextra -Wconversion -std=c99 -pedantic -g   -c -o test.o test.c
cc   test.o   -o test

Making the board
0x7fc4e6d00370
  0   0   0   0 
0x7fc4e6d001e0
  0   0   0   0 
0x7fc4e6d001f0
  0   0   0   0 
0x7fc4e6d00200
  0   0   0   0 
0x7fc4e6d00210
  0   0   0   0 
0x7fc4e6d00220
  0   0   0   0 
Printing the board
0x7fc4e6d00370
  0   0   0   0 
0x7fc4e6d001e0
 -422575600  32708  -422575584  32708 
0x7fc4e6d001f0
  0   0   0   0 
0x7fc4e6d00200
  0   0   0   0 
0x7fc4e6d00210
  0   0   0   0 
0x7fc4e6d00220
  0   0   0   0 

If I link in packages, like glib-2, the corruption happens more frequently.

cc -Wall -Wshadow -Wwrite-strings -Wextra -Wconversion -std=c99 -pedantic -g `pkg-config --cflags glib-2.0`   -c -o test.o test.c
cc `pkg-config --libs glib-2.0`   test.o   -o test

The memory locations are all correct. All rows are fine during initialization. There's no compiler warnings. -fsanitize=address finds no errors.

What might be causing that one row to become corrupted? Can anyone even repeat the problem?

$ cc --version
Apple LLVM version 8.0.0 (clang-800.0.42.1)
Target: x86_64-apple-darwin17.6.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin

$ uname -a
Darwin Windhund.local 17.6.0 Darwin Kernel Version 17.6.0: Tue May  8 15:22:16 PDT 2018; root:xnu-4570.61.1~1/RELEASE_X86_64 x86_64 i386 MacBookPro8,1 Darwin

Upvotes: 2

Views: 115

Answers (1)

Christian Gibbons
Christian Gibbons

Reputation: 4370

You are allocating your array of int pointers as an array of ints resulting in some unexpected behavior. Simple enough fix, fortunately.

Replace this line:

int **board = malloc( sizeof(int) * num_rows );

With this line:

int **board = malloc( sizeof(int *) * num_rows );

Or, to avoid these kind of mistakes in the future, as pointed out by Jonathan Leffler below in the comments, you can perform the sizeof operator on the dereferenced variable you are trying to allocate so you don't have to worry about getting the type correct:

int **board = malloc( sizeof(*board) * num_rows );

Upvotes: 5

Related Questions