3
votes

I have this simple bubblesort program that when compiled on macOs works correctly, but when compiled on linux (with gcc), gives a segmentation fault at runtime. I would love to understand why.

#include <stdio.h>
#include "alg_utils.h"

void bubble_sort(int *, int);

int main() {
    int array[10] = {5, 10, 77, 1, -2, -61, 18, 14, 57, 7};
    bubble_sort(array, 10);
    print_array(array, 10);
    return 0;
}

void bubble_sort(int *array, int len) {
    int i, j;

    for (i=0; i < len; i++) {
        for (j=0; j < len; j++) {
            if (array[j] < array[j-1])
               swap(&array[j], &array[j-1]);
        }
    }
}

On mac:

~/Projects/Algorithms: gcc Bubblesort.c
~/Projects/Algorithms: ./a.out
  -2   0   1   5   7  10  14  18  57  77%

On linux:

root#f95445bcd4e7:~/algos$ gcc Bubblesort.c
root#f95445bcd4e7:~/algos$ ./a.out
Segmentation fault

The alg_utils.h only has the definition of the swap() and print_array() functions. Nothing crazy.

void print_array(int *, int);
void swap(int *, int *);

void swap(int *a, int *b) {
    int temp = *b;
    *b = *a;
    *a = temp;
}

void print_array(int *array, int len) {
    int i;
    for (i=0; i < len; i++) {
        printf("%4d", array[i]);
    }
}

When I change the main() with main(int argc, char *argv[]) it works on linux too.

Linux (with main(int argc, char *argv[])

 root#f95445bcd4e7:~/algos$ gcc Bubblesort.c
 root#f95445bcd4e7:~/algos$ ./a.out
 -2   1   1   5   7  10  14  18  57  77

So I thought: linux doesn't like main with no params...but then a simple hello world like this runs just fine.

#include <stdio.h>
int main() {
    printf("hello world\n");
    return 0;
}

So, i'm confused. What is it? Maybe alg_utils? Maybe different c implementations? I've tried compiling with -std=c99 (and other combinations) to no avail.

Does somebody have any clue? Thank you in advance

3
What would be array[j-1] for j=0 ?Eugene Sh.
yeah dude ur right the algorithm was messed up, i fixed it now. It doesn't crash anymore on linux. Still, the question remains. How did it even work before on mac (and on linux with argc / argv [which I don't even use])?onVal
That's because you've invoked UB and undefined means anything might happen, including that the program seems to workIngo Leonhardt
clarification of the above comment - UB is commonly standing fot undefined behaviorEugene Sh.

3 Answers

4
votes
for (i=0; i < len; i++) {
        for (j=0; j < len; j++) {
            if (array[j] < array[j-1])
               swap(&array[j], &array[j-1]);
        }
}

In your for loop you are trying to access array[j-1] so when the value of j=0 you are actually trying to access memory location which is out of the array & this memory location might not be allocated to you so this results into the segmentation fault.

Now C compiler behaves differently according to many factors like OS, Version of the compiler etc. According to the best of my knowledge Mac OS must be keeping some padding before and after the array so that even if you access those memory locations your program won't crash (Again I am being too brief here & there is more to it!). Linux on the other hand must not be providing you that padding and is allocating the exact amount of memory that you need! And hence when you access the memory that is not allocated it gives you segmentation fault & your program crashes!

Check this link about the padding that I am talking about:- https://en.wikipedia.org/wiki/Buffer_overflow_protection

EDIT :- I forgot to mention this, your program works when you have written main with parameters is because there is some space reserved for argv[] and luckily when you access array[j-1] you must be accessing the memory location which is available to you i.e. argv[]. (Again this explanation is very brief & there are many technical aspects to it!)

Hope this helps :)

2
votes

don't do this:

for (i=0; i < len; i++) {
    for (j=0; j < len; j++) {
        if (array[j] < array[j-1])
           swap(&array[j], &array[j-1]);
    }
}

use this instead:

for (i=0; i < len; i++) {
    for (j=i+1; j < len; j++) {
        if (array[i] < array[j])
           swap(&array[i], &array[j]);
    }
}

this approach does not access elements beyond array limits, works twice faster because it does not analyse already sorted part of the array.

0
votes

If you so much want to be able to access and more importantly modify memory that requires you to use negative array indexing, you must make sure that this memory is allocated. You can use this macro to create a pointer to an array with x-many bytes forwards and backwards.

#define ARRAY(type, name, nItems)   type _##name##_source[nItems * 2] = {0}, \
                                    *name = _##name##_source + nItems

ARRAY0(char, arr, 4);

arr[-1] = 'h';
arr[0] = 'e';
arr[1] = 'y';

printf("%s\n", &arr[-1]);

If you don't allocate, it is undefined behavior. In most cases if you don't write to that memory it will be okay, because that memory most certainly still belongs to the process, especially with argc and argv used.