-3

I am attempting to write merge sort in c. I am having trouble getting the pointers working correctly when passing them to Merge and copying them. My array is getting corrupted with garbage data.

void Merge(long long int **numbers, int low1, int high1, int low2, int high2, int count)
{
    //int count = sizeof(*numbers)/sizeof(numbers[0]);
    long long int *nums1;
    nums1 = (long long int *)malloc(sizeof(long long int)*count);
    long long int *nums2;
    nums2 = (long long int *)malloc(sizeof(long long int)*count);
    memcpy(nums1,numbers,count);
    memcpy(nums2,numbers,count);
    //nums1 = (long long int *)numbers;
    //nums2 = (long long int *)numbers;

    int i = 0, j = 0, k = 0;

    while(i < high1-low1 && j < high2-low2)
    {
        //comparison
        if(nums1[i] <= nums2[j])
        {
            numbers[k] = &(nums1[i]); i++;
        }
        else
        {
            numbers[k] = &(nums2[j]); j++;
        }
        k++;
    }
    while(i < high1-low1)
    {
        numbers[k] = &(nums1[i]);
        i++;
        k++;
    }
    while(j < high2-low2)
    {
        numbers[k] = &(nums2[j]);
        j++;
        k++;
    }

    return;
}

/*
DESCRIPTION:
    Sorts the array using the merge sort algorithm

INPUT:
    numbers - unsorted list of numbers
    low - starting index
    high - ending index

Output:
    sorted array
*/

void Mergesort(long long int *numbers, int low, int high, int count)
{
    int low1, low2, high1, high2;
    if(high-low > 1)
    {
        low1 = low;
        high1 = (high-low)/2 +low;
        low2 = high1;
        high2= high;

        Mergesort(numbers, low1, high1, count);
        Mergesort(numbers, low2, high2, count);
        Merge(numbers,low1, high1, low2, high2, count);
    }
    return;
}

/*
DESCRIPTION:
    Determines whether all of the elements in a list are distinct.
    The list is first sorted (in place) and then elements that are
    side by side are compared to determine if they are unique.

INPUT:
    numbers - unsorted list of numbers
    count - the size of the numbers array
    operations - pointer to in which to store the number of operations

Output:
    TRUE if every element in numbers is unique, otherwise FALSE
*/
int unique3(long long int *numbers, int count, long long int *operations) {
    long long int basic_operations = 0;
    //sort array
    int i;
            for(i = 0; i < count; i++)
            {
                printf("%d ", numbers[i]);
                printf("\n");
            }
    Mergesort(numbers, 0, count-1, count);
    printf("\n\n\n Here \n\n\n");
            for(i = 0; i < count; i++)
            {
                printf("%d ", numbers[i]);
                printf("\n");
            }
    //int i;
    for(i=0;i < count; i++)
    {
        basic_operations++;
        if (numbers[i] == numbers[i+1])
            return FALSE;
    }

    *operations = basic_operations;
    return TRUE;
}


/*
DESCRIPTION:
    This function reads a list of integers from a file and stores
    them in an array as long long ints.  The array and its size are
    stored in pointers passed as arguments.  Upon failure, a value
    -1 is stored in address pointed to by return_size.

INPUT:
    file_name - file to read values from
    return_numbers - pointer to an address where the numbers are stored
    return_size - pointer to an integer to store the size of the array

Output:
    void
*/
void read_array(char *file_name, long long int **return_numbers, int *return_size) {
    FILE *file; // file pointer used to read in file_name
    char *buffer = NULL; // string of characters, set initially to null
    size_t buffer_len = 0; // used to determine the number of characters read in
    ssize_t char_count; // store the number of characters read in

    int count;
    int ii;
    long long int *numbers;

    file = fopen(file_name, "r"); // open the file for reading
    if (file == NULL) {
        // error opening the file, print error message
        perror(file_name);

        // set pointers to show failed state
        *return_numbers = NULL;
        *return_size = -1;
        return;
    }

    // read in the first line to determine how many numbers are in the list
    if ((char_count = getline(&buffer, &buffer_len, file)) == -1) {
        // error openting the file, print error message
        perror("error getting count");

        fclose(file);

        // set pointers to show failed state
        *return_numbers = NULL;
        *return_size = -1;
        return;
    }

    // use atoi to convert the string to a number
    count = atoi(buffer);

    // dynamically allocate an array to contain count number of long long ints
    numbers = (long long int *)malloc(sizeof(long long int)*count);

    // read in count numbers
    for(ii=0; ii < count; ii++) {
        // read in a number delimited by a comma
        if ((char_count = getdelim(&buffer, &buffer_len, (int)',', file)) == -1) {
            // if char_count == -1, there was an error reading
            perror("error reading entry");

            free(buffer);
            free(numbers);
            fclose(file);

            // set pointers to show failed state
            *return_numbers = NULL;
            *return_size = -1;
            return;
        }

        // remove the comma from the end of the string
        buffer[char_count-1] = '\0';

        // convert the string to a long long int
        numbers[ii] = atoll(buffer);
    }

    // free up the buffer and close the file
    free(buffer);
    fclose(file);

    // set up pointer values
    *return_numbers = numbers;
    *return_size = count;
}

void timing(long long int *numbers, int count, int (*algorithm)(long long int *, int, long long int *), long long int *operations, int *unique, long int *ms_time) {
    struct timeval start_tv;
    struct timeval end_tv;

    gettimeofday(&start_tv, NULL);
    *unique = algorithm(numbers, count, operations);
    gettimeofday(&end_tv, NULL);

    *ms_time = (end_tv.tv_sec - start_tv.tv_sec)*1000000L + (end_tv.tv_usec - start_tv.tv_usec);
}

int main(int argc, char *argv[]) {
    if(argc != 2) {
        printf("Invalid Number of Arguments\n");
        printf("Usage:\n");
        printf("%s <file_name>\n", argv[0]);
        printf("\tfile_name - name of the input file\n");
        printf("It is assumed that the first line of the input file contains\n");
        printf("the count of the numbers in the file. The second line is assume\n");
        printf("to be a comma separated list of integers.\n");
    }
    else {
        int count;
        long long int *numbers;
        long long int operations;
        int is_unique;
        long int ms_time;
        read_array(argv[1], &numbers, &count);

        if(count >= 0){
            printf("%d ", count);

            timing(numbers, count, unique1, &operations, &is_unique, &ms_time);
            printf("(%d,%lld,%ld) ", is_unique, operations, ms_time);

            timing(numbers, count, unique2, &operations, &is_unique, &ms_time);
            printf("(%d,%lld,%ld) ", is_unique, operations, ms_time);

            timing(numbers, count, unique3, &operations, &is_unique, &ms_time);
            printf("(%d,%lld,%ld)\n", is_unique, operations, ms_time);
        }
    }

    return 0;
}

Here is my array before and after calling the function.

Before 10 9 8 7 6 5 4 1 2 0

After

37668400 37668408 37668416 37668424 37668496 37668504 37668512 37668520 37668528 0

Note: the code outside of the merge, mergesort and unique3 are all a skeleton given to our class***

  • You might want to start by comparing the declaration of the argument `numbers` and the declarations of the variables `nums1` and `nums2`. I'm surprised this code compiles cleanly, if at all. – Some programmer dude Oct 24 '14 at 18:03
  • 2
    Also, and not that it matters (probably) in this case, but [in C you should not cast the return of `malloc`](http://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc). – Some programmer dude Oct 24 '14 at 18:04
  • 2
    `memcpy(nums1,numbers,count);` Is `numbers` `long long int *` ? and size is `count * sizeof(long long int)` – BLUEPIXY Oct 24 '14 at 18:05
  • Have you tried stepping through it with `gdb`? – Eric Fode Oct 24 '14 at 18:06
  • 2
    Take a look at `memcpy(nums1,numbers,count);` perhaps `memcpy(nums1,*numbers,count);` would be better? Also, **always compile with WARNINGS ENABLED, -Wall -Wextra at minimum** – David C. Rankin Oct 24 '14 at 18:07
  • @DavidC.Rankin I tried your suggestions. It now halts the program in the middle of the function. I am using warnings, I've changed so much around trying to get it to not change the values in my array. I know the code isn't clean yet, but it will be soon. Sorry for the ugly code! – linux_security Oct 24 '14 at 18:14
  • Oh, and the contents of `nums1` and `nums2` are not pointers, so don't treat them as such (like the casting). And you have a memory leak since you allocate that memory of the heap but you never free it. – Some programmer dude Oct 24 '14 at 18:14
  • 3
    First rule about warnings: Don't use casting to silence them! The warnings are there for a reason, and tells you that you are doing something that you should not do, and often can lead to [*undefined behavior*](http://en.wikipedia.org/wiki/Undefined_behavior). Instead find the *root cause* of the warnings and fix that instead. – Some programmer dude Oct 24 '14 at 18:15
  • I am using gdb, and when i added ' memcpy(nums1,*numbers,count);' it now crashes at memcpy – linux_security Oct 24 '14 at 18:16
  • @linux_security. The issue I see is you pass `long long int **numbers` as an argument, declare `long long int *nums1;` and then after allocating do `memcpy(nums1,numbers,count);` where `nums1` is a pointer and `numbers` is a collection of pointers. I don't know what you are loading into `numbers` on the call, but it looks wonky. If you are calling with `&something` in the call, then it's not an issue. I recommend posting a **complete** version of the code so we can compile/test. – David C. Rankin Oct 24 '14 at 18:18
  • numbers is a list of integers read in from a file. They are stored in numbers, and need to be copied into two temporary arrays to sort the numbers dynamic array. – linux_security Oct 25 '14 at 03:05

1 Answers1

1

You have this function declaration:

void Merge(long long int **numbers, int low1, int high1, int low2, int high2, int count)

Here you declare numbers as a pointer to a pointer to long long int, that could be either a pointer to an array or a two-dimensional array.

You call it like this:

void Mergesort(long long int *numbers, int low, int high, int count)
{
    ...
    Merge(numbers,low1, high1, low2, high2, count);
    ...
}

But what you pass here is a pointer to long long int, one indirection (pointer) less than the function Merge expects. This will lead to undefined behavior.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • I changed the declaration to 'void Merge(long long int *numbers, int low1, int high1, int low2, int high2, int count)' and i still receive random numbers in my resulting array. i can't seem to see where the garbage is getting into my array. – linux_security Oct 26 '14 at 05:16
  • Somehow memcpy wasn't getting the ints at numbers into nums1 and nums2, i changed it to a for loop and the ints are correctly getting into nums1 and nums2, but i'm getting several duplicate 10's in the final array rather than the sorted array from the original ints – linux_security Oct 26 '14 at 05:26