Array declaration fails while trying to declare int array - c

I've been learning & coding sorting algorithms for some time and recently I've coded merge sort in C, and I've also coded a sort_test function to test the function that I write. In the sort test function, I'm declaring an array and assigning random values to it, but when the array size gets to 1,000,000 the program crashes. Why is that happening?
sort_test.c
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "merge_sort.h"
#include "sort_test.h"
// test size
#define MIN 10
#define MAX 1000000
// int comparator
int cmpInt(const void *elem1,const void * elem2){
int e1 = *(int *)elem1; // i-1
int e2 = *(int *)elem2; // i
if(e2 < e1){
return -1;
} else if(e2 > e1){
return 1;
} else {
return 0;
}
}
// double comparator
int cmpDouble(const void *elem1,const void *elem2){
double e1 = *(double *)elem1;
double e2 = *(double *)elem2;
if(e2 < e1){
return -1;
} else if(e2 > e1){
return 1;
} else {
return 0;
}
}
void initSeed(){
srand(time(NULL));
}
void intSortTest(){
initSeed();
for(size_t i = MIN;i <= MAX;i *=10){
int arr[i];
for(size_t j = 0; j < i;j++){
arr[j] = rand();
}
// sorting the array
mergesort(arr,0,i);
// checking if sorted array hold the
// condition i[0] <= i[1] ... <= i[n].
for(size_t j = 1;j < i;j++){
int *e1 = &arr[j-1];
int *e2 = &arr[j];
assert(cmpInt(e2,e1) <= 0);
}
printf("INT TEST : %7d\tPASSED\n",i);
}
printf("\n");
}
void doubleSortTest(){
initSeed();
for(int i = MIN; i <= MAX; i *= 10){
double arr[i];
for(int j = 0 ; j < i;j++){
arr[j] = (double)(rand() % 100) + 1.0;
}
// perform sort
//insertion_sort(arr,sizeof (double),i,cmpDouble);
for(int j = 1; j < i;j++){
double *e1 = &arr[j-1];
double *e2 = &arr[j];
assert(cmpDouble(e2,e1) <= 0);
}
printf("Double Test : %5d\tPASSED\n",i);
}
printf("\n");
}
sort_test.h
#ifndef SORT_TEST_H
#define SORT_TEST_H
void initSeed();
void intSortTest();
void doubleSortTest();
int cmpDouble(const void *elem1,const void *elem2);
int cmpInt(const void *elem1,const void * elem2);
#endif // SORT_TEST_H
merge_sort.h
#ifndef MERGE_SORT_H
#define MERGE_SORT_H
void mergesort(int *arr,int start,int end);
void merge(int *arr,int start,int med,int end);
#endif // MERGE_SORT_H
merge_sort.c
#include <stdio.h>
#include "sort_test.h"
#include "merge_sort.h"
int main(){
intSortTest();
return 0;
}
void mergesort(int *arr,int start,int end){
if(start < end){
int median = (end + start) / 2;
mergesort(arr,start,median);
mergesort(arr,median+1,end);
merge(arr,start,median,end);
}
}
void merge(int *arr,int start,int median,int end){
int i = start; int j = median+1;
int copy[end+1];
int cIndex = 0;
while(i <= median && j <= end) {
if(arr[j] <= arr[i]){
copy[cIndex++] = arr[j++];
} else {
copy[cIndex++] = arr[i++];
}
}
while(i <= median){
copy[cIndex++] = arr[i++];
}
while(j <= end){
copy[cIndex++] = arr[j++];
}
for(int k = 0; k < cIndex; k++){
arr[start++] = copy[k];
}
}

It is because you are allocating the arrays on the stack. Try the following code instead.
void intSortTest(){
initSeed();
for(size_t i = MIN;i <= MAX;i *=10){
int *arr = malloc(i*sizeof(int)); // <-- changed this
for(size_t j = 0; j < i;j++){
arr[j] = rand();
}
// sorting the array
mergesort(arr,0,i);
// checking if sorted array hold the
// condition i[0] <= i[1] ... <= i[n].
for(size_t j = 1;j < i;j++){
int *e1 = &arr[j-1];
int *e2 = &arr[j];
assert(cmpInt(e2,e1) <= 0);
}
printf("INT TEST : %7d\tPASSED\n",i);
free(arr); // <-- added this
}
printf("\n");
}
EDIT
Also the merge algorithm is incorrect. More precisely, you have a problem with the value list boundaries.
When you define the start and end index of a value list, the values are in arr[start] to arr[end-1], not arr[end]. The number of values is then end-start. With this convention, you have an empty list when start == end.
As a consequence, the function mergesort becomes:
void mergesort(int *arr,int start,int end){
if (start+1 >= end)
return; // a list with 0 or 1 values is already sorted
int median = (end + start) / 2;
mergesort(arr,start,median);
mergesort(arr,median,end);
merge(arr,start,median,end);
}
The merge function then become as follow:
void merge(int *arr,int start,int median,int end){
int i = start; int j = median;
int *copy = malloc((end-start)*sizeof(int)); // use malloc for huge arrays
int cIndex = 0;
while(i < median && j < end) { // not i <= median && j <= end
if(arr[j] <= arr[i]){
copy[cIndex++] = arr[j++];
} else {
copy[cIndex++] = arr[i++];
}
}
while(i < median){ // not i <= median
copy[cIndex++] = arr[i++];
}
while(j < end){ // not j <= median
copy[cIndex++] = arr[j++];
}
for(int k = 0; k < cIndex; k++){
arr[start++] = copy[k];
}
free(copy);
}
As you can see, there are only minor differences.
With this code, your program runs without error.

Now that the code is visible, it is fairly easy to see that you are indeed blowing the stack as I suggested in one of my many comments.
In merge(), you have:
int copy[end+1];
as well as in intSortTest() having:
int arr[i];
where i reaches 1,000,000.
When end is 1,000,000 — it is set from i — you have an array of one million int values being sorted, and a copy with another one million int values (plus 1), so you attempt to place two million 4-byte int values on the stack — and 8,000,000 bytes blows the stack limits. Since 800,000 bytes (the previous size) fits on the stack in both Unix and Windows, it isn't 100% clear which you are using. There isn't much margin for error on Unix/Linux; the limit is thoroughly blown on Windows because neither 4 MB array fits on the stack.
The recommended fix is to use dynamic memory allocation (malloc() et al) instead of stack allocation — in both the sort test function and in the main merge() code.

Related

How to fix "Program received signal SIGSEGV, Segmentation fault." while trying to access an array

I'm doing quick sorting with different methods of selecting pivots, I don't see any problems in my code, the functions work while testing separated, but when I put them together, they don't work most of the time.
I've tried moving the files to another path, and changing the way I access the array.
void quick_sort(uint32_t arr[], int first, int last, int pivot_opt)
{
int i, j;
uint32_t pivot = pivot_select(arr, last, pivot_opt);
i = first;
j = last;
do
{
comparations_count++;
while (arr[i] < pivot) i++; // Counting elements smaller than pivot
comparations_count++;
while (arr[j] > pivot) j--; // Counting elements greater than pivot
comparations_count++;
if (i <= j)
{
exchanges_count++;
swap(&arr[i++], &arr[j--]); // Placing smaller elements in the left, and greater elements in the right without touching the pivot
}
comparations_count++;
} while (i <= j);
comparations_count++;
if (first < j)
quick_sort(arr, first, j, pivot_opt); // Sorting smaller elements of array
comparations_count++;
if (i < last)
quick_sort(arr, i, last, pivot_opt); // Sorting greater elements of array
}
uint32_t pivot_select(uint32_t arr[], int last, int pivot_opt)
{
uint32_t pivot = 0;
int random_index = 0;
switch (pivot_opt)
{
case 0:
pivot = arr[last]; // Choosing the pivot as the last element in the array
break;
case 1:
random_index = rand()%(last); // Choosing the pivot as a random element of array
pivot = arr[random_index];
break;
case 2:
pivot = median(arr, last); // Choosing the pivot as avg of three random indexes of the array
break;
default:
break;
}
return pivot;
}
uint32_t median(uint32_t arr[], int n)
{
if (n <= 3)
{
return arr[0]; // If the array have 3 or less elements, choose as pivot first element
}
else
{
int index[3] = {0}; // Index of 3 elements of original array
int last_index = 0; // Last chosen index, to verify if index was selected
int i = 0;
while(i < 3) // Selecting 3 random index
{
int current_index = (rand()%(n));
if (current_index == last_index)
i--;
else
{
index[i++] = current_index;
last_index = current_index;
}
}
uint32_t array[3] = {arr[index[0]], arr[index[1]], arr[index[2]]}; // Creating array with the elements on random indexes
insertion_sort(array, 3); // Sorting the array
return array[1]; // Returning the pivot as the middle element of array
}
}
I'm getting this error on median function
Program received signal SIGSEGV, Segmentation fault.
0x0000555555555546 in median (arr=<error reading variable: Cannot access memory at address 0x7fffff7fefd8>, n=<error reading variable: Cannot access memory at address 0x7fffff7fefd4>) at /media/storage/Codes/Data Structure/Recursive_Sorting/main.c:107
107 {
I put all the libraries I'm using so I don't miss one.
Code for testing:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/time.h>
#include <time.h>
#define size 1000
uint32_t comparations_count;
uint32_t exchanges_count;
int main(int argc, char** argv)
{
uint32_t array[size];
fill(array, size);
permute_array(array, size);
comparations_count = 0;
exchanges_count = 0;
quick_sort(array, 0, size-1, 2);
return 0;
}
void fill(uint32_t arr[], uint32_t n)
{
for (size_t i = 0; i < n; i++) // Filling the array in ascending order
arr[i] = i;
}
void swap(uint32_t *a, uint32_t *b)
{
// Swapping two elements
uint32_t t = *a;
*a = *b;
*b = t;
}
void permute_array(int a[], size_t n)
{
// Adapted from:
// https://www.geeksforgeeks.org/shuffle-a-given-array-using-fisher-yates-shuffle-algorithm/
srand(time(NULL)); // Init random seed
for (size_t i = n - 1; i > 0; i--) // Permute array
{
size_t j = rand() % (i+1); // Pick a random index from 0 to i
swap(&a[i], &a[j]); // Swap arr[i] with the element at random index
}
}
void insertion_sort(uint32_t arr[], size_t n)
{
uint32_t current_index = 0;
uint32_t current_value = 0;
for (size_t i = 1; i < n; i++) {
current_index = i;
current_value = arr[i];
while (current_index > 0) {
if (current_value < arr[current_index - 1]) {
swap(&arr[current_index], &arr[current_index-1]);
current_index--;
}else { break; }
}
}
}
Let's start with this one:
while (arr[i] < pivot) i++;
What if all the elements are less than pivot, your i will be out of bounds, change the condition to while(arr[i] < pivot && i <= j) i++;
Consider this one:
while (arr[j] > pivot) j--;
What if all the elements are greater than pivot, your j will be out of bounds (a negative number), change the condition here too.
According to my opinion, the above-mentioned areas are causing problems.
Happy debugging!

How can I find why my merge sorting algorithm crash when sorting an array of 1 million element?

I'm a French student and trying to calculate the execution time of the Merge Sort algorithm for different size of array.
I also want to write the different execution time in a .csv file. But when my program tries to sort an array with 1 million elements the process returns -1073741571 (0xC00000FD) in Code::Blocks. So if you could point me to a way to find a solution I would be very grateful!
Here is my code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
void genTab(int *tab, int n) {
int i;
for (i = 0; i < n; i++) {
tab[i] = rand() % 100;
}
}
void fusion(int *tab, int deb, int mid, int fin) {
int i = deb;
int j = mid + 1;
int k = deb;
int temp[fin + 1];
while ((i <= mid) && (j <= fin)) {
if (tab[i] <= tab[j]) {
temp[k] = tab[i];
i++;
} else {
temp[k] = tab[j];
j++;
}
k++;
}
while (i <= mid) {
temp[k] = tab[i];
i++;
k++;
}
while (j <= fin) {
temp[k] = tab[j];
k++;
j++;
}
for (i = deb; i <= fin; i++) {
tab[i] = temp[i];
}
}
void triFusion(int *tab, int i, int j) {
if (i < j) {
triFusion(tab, i, (int)((i + j) / 2));
triFusion(tab, (int)((i + j) / 2 + 1), j);
fusion(tab, i, (int)((i + j) / 2), j);
}
}
void reset(int *tab1, int *tab2, int n) {
for (int i = 0; i < n; i++) {
tab2[i] = tab1[i];
}
}
int main() {
srand(time(NULL));
clock_t start, end;
int nbrTest[15] = {
1000, 5000, 10000, 50000, 80000, 100000, 120000, 140000,
150000, 180000, 200000, 250000, 300000, 450000, 1000000
};
FILE *fp;
char *tpsExecution = "exeTime.csv";
fp = fopen(tpsExecution, "w");
fprintf(fp, "Array Size; Merge Time");
for (int i = 0; i < 15; i++) {
int n = nbrTest[i];
printf("Calculating time for an array of %d \n", n);
int *tab = malloc(sizeof(int) * n);
genTab(tab, n);
int *copie = malloc(sizeof(int) * n);
reset(tab, copie, n);
start = clock();
triFusion(tab, 0, n - 1);
end = clock();
float tpsFusion = (float)(end - start) / CLOCKS_PER_SEC;
reset(tab, copie, n);
printf("writing in the file\n");
fprintf(fp, "\n%d;%f", n, tpsFusion);
free(tab);
free(copie);
}
fclose(fp);
return 0;
}
int temp[fin+1]; may exceed the space limit for the stack. You should allocate it with malloc instead, and free it with free.
If you want to exclude malloc and free from the timed code, the allocation could be performed outside the timed code and passed in as work space.
(Note: posted after the answer from #Eric Postpischil).
The function
void fusion(int * tab, int deb, int mid, int fin)
Has the line
int temp[fin+1];
and the value of fin comes through another function from the number of elements n to be sorted
triFusion(tab, 0, n-1);
and as an automatic variable, breaks the stack when n is large.
I suggest replacing the line with
int *temp = malloc((fin+1) * sizeof *temp);
if(temp == NULL) {
puts("malloc");
exit(1);
}
// ...
free(temp);
fusion() is always allocating the full size of the array for temp, even when only a small fraction of temp is being used. You could change this to:
int k = 0;
...
int temp[fin+1-deb];
...
tab[i]=temp[i-deb];
still this will exceed stack space if n is large. So as suggested in the other answers:
int k = 0;
...
int *temp = malloc((fin+1-deb)*sizeof(int));
...
tab[i]=temp[i-deb];
...
free(temp)
or better still, do a one time allocation of a second array in main or in a "helper" function, the include a pointer to the second array in the merge sort functions.

Counting swaps in Quicksort in C

I am trying to count the number of swaps that occur in my quicksort in C. However, I am getting values that are incorrect and not sure where I went wrong. I am using a structures as my arrays to be sorted.
struct anArray{
int numbers[maxSize];
int swaps;
};
/* Partition function */
int partition(struct anArray *array, int start, int end){
if(start == end){
return start;
}
int pivot = array->numbers[end];
int low = start - 1;
int high = end;
for(;;){
do{
low++;
} while(array->numbers[low] < pivot);
do{
high--;
} while(array->numbers[high] > pivot);
/* Detector for when the cells meet */
if(low >= high){
swap(array, low, end);
return low;
}
}
/* Swapping the values */
swap(array, low, high);
}
This is my partition function used to "separate" the arrays.
void quickSort(struct anArray *array, int start, int end){
if(end - start <= 0){ return; }
else{
int pivot = array->numbers[end];
int partitionPoint = partition(array, start, end);
quickSort(array, start, partitionPoint - 1);
quickSort(array, partitionPoint + 1, end);
}
}
This is my quicksorting function. It's a recursive function.
My swap function increments counter by 1 every time it's called.
In my main, I set
myArray->swaps = counter;
But the number of times the swaps occurs isn't right. For example, if I sort an array that goes from 1 to 9, the number of swaps should be 0 but I get 9. I've tried incrementing counter when it's in the partition function only but it gives me the same result.
Is there something wrong with my partition function?
Thank you very much
Edit 1:
Here's my swap function.
void swap(struct anArray *array, int first, int second){
int temp = array->numbers[first];
array->numbers[first] = array->numbers[second];
array->numbers[second] = temp;
counter++;
}
I've tried using
void swap(struct anArray *array, int first, int second, int swapCount)
and then have swapCount be array->swaps when calling the swap function, and incrementing it by 1 but it gives me the same answer.
Here's a part of my main.
int main(){
struct anArray *ascending = (struct anArray*)malloc(10 * sizeof(struct anArray));
int ascend[maxSize] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
initArray(ascending, ascend);
quickSort(ascending, 0, maxSize - 1);
ascending->swaps = counter;
printf("Test: Unique random values\nSorted: [ ");
for(int i = 0; i < maxSize; i++){
printf("%i ", ascending->numbers[i]);
}
printf("]\nSwaps: %i\nComps: \n\n", ascending->swaps);
The other parts of my main are just other arrays to be sorted. The initArray is used to set the values of array->numbers and also reset array->swaps to 0.
Your quicksort code seems pretty good. I didn't examine it rigorously, but it passed a simple test, so I didn't investigate further. (Edit: Based on your feedback, I created a third version in my second update that shows that the sort has an issue for larger data inputs).
The main bug was the malloc at the top of main. We do not want an array of the struct anArray:
struct anArray *ascending = malloc(10 * sizeof(struct anArray));
That is, we do not want (e.g.) 10 structs, we want a single struct and to fill in 10 ints that go into the numbers field that is in that single struct.
The initArray function was not posted, so I had to guess/deduce what it might be. Based on the above bug, I'm not sure that numbers would have been initialized correctly.
From the code fragments posted, I was able to piece together a whole program. I've created two versions:
One with the bugs annotated [but not fixed] that compiles cleanly.
And, a second that is fully cleaned up, working, and generalized for arbitrary array sizes [please pardon the gratuitous style cleanup]
Here is [something close to] your original code with the bugs annotated:
#include <stdio.h>
#include <stdlib.h>
// NOTE/BUG: this was not defined and _fixed_ defines should be all caps
#define maxSize 10
struct anArray {
int numbers[maxSize];
int swaps;
};
int counter;
void
initArray(struct anArray *array,const int *src)
{
for (int idx = 0; idx < maxSize; ++idx)
array->numbers[idx] = src[idx];
array->swaps = 0;
}
void
swap(struct anArray *array, int first, int second)
{
int temp = array->numbers[first];
array->numbers[first] = array->numbers[second];
array->numbers[second] = temp;
counter++;
}
/* Partition function */
int
partition(struct anArray *array, int start, int end)
{
if (start == end) {
return start;
}
int pivot = array->numbers[end];
int low = start - 1;
int high = end;
for (;;) {
do {
low++;
} while (array->numbers[low] < pivot);
do {
high--;
} while (array->numbers[high] > pivot);
/* Detector for when the cells meet */
if (low >= high) {
swap(array, low, end);
return low;
}
}
/* Swapping the values */
swap(array, low, high);
}
void
quickSort(struct anArray *array, int start, int end)
{
if (end - start <= 0) {
return;
}
else {
// NOTE/BUG: pivot is _not_ used
int pivot = array->numbers[end];
int partitionPoint = partition(array, start, end);
quickSort(array, start, partitionPoint - 1);
quickSort(array, partitionPoint + 1, end);
}
}
int
main(void)
{
// NOTE/BUG: we do _not_ want an array of the struct, but an array of int
// that is allocated for "number" _inside_ the struct
struct anArray *ascending = malloc(10 * sizeof(struct anArray));
int ascend[maxSize] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
// NOTE/BUG: this was not defined
initArray(ascending, ascend);
quickSort(ascending, 0, maxSize - 1);
ascending->swaps = counter;
printf("Test: Unique random values\nSorted: [ ");
for (int i = 0; i < maxSize; i++) {
printf("%i ", ascending->numbers[i]);
}
printf("]\nSwaps: %i\nComps: \n\n", ascending->swaps);
return 0;
}
Here is a cleaned up and working version. I've generalized it so it can take an arbitrarily long array. I've also done a bit of style and code cleanup:
#include <stdio.h>
#include <stdlib.h>
typedef struct {
int *numbers;
int size;
int swaps;
} Array;
Array *
initArray(const int *src,int size)
{
Array *array = malloc(sizeof(Array));
array->numbers = malloc(size * sizeof(int));
array->size = size;
// store in reverse order so the sort will actually do something
for (int idx = 0; idx < size; ++idx)
array->numbers[size - 1 - idx] = src[idx];
array->swaps = 0;
return array;
}
void
freeArray(Array *array)
{
free(array->numbers);
free(array);
}
void
swap(Array *array, int first, int second)
{
int temp = array->numbers[first];
array->numbers[first] = array->numbers[second];
array->numbers[second] = temp;
array->swaps += 1;
}
/* Partition function */
int
partition(Array *array, int start, int end)
{
if (start == end)
return start;
int pivot = array->numbers[end];
int low = start - 1;
int high = end;
for (;;) {
do {
low++;
} while (array->numbers[low] < pivot);
do {
high--;
} while (array->numbers[high] > pivot);
/* Detector for when the cells meet */
if (low >= high) {
swap(array, low, end);
return low;
}
}
/* Swapping the values */
swap(array, low, high);
}
void
quickSort(Array *array, int start, int end)
{
if (end - start <= 0)
return;
//int pivot = array->numbers[end];
int partitionPoint = partition(array, start, end);
quickSort(array, start, partitionPoint - 1);
quickSort(array, partitionPoint + 1, end);
}
int
main(void)
{
// NOTE/BUG: we do _not_ want an array of the struct, but an array of int
// that is allocated for "number" _inside_ the struct
int original[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int size = sizeof(original) / sizeof(original[0]);
Array *ascending = initArray(original, size);
quickSort(ascending, 0, ascending->size - 1);
printf("Test: Unique random values\nSorted: [ ");
for (int i = 0; i < ascending->size; i++) {
int expected = original[i];
int actual = ascending->numbers[i];
printf("%d%s ", actual, (actual == expected) ? "" : "???");
}
printf("]\nSwaps: %i\nComps: \n\n", ascending->swaps);
freeArray(ascending);
return 0;
}
UPDATE:
What does the line int size = sizeof(original) / sizeof(original[0]); do exactly?
Does it give me an integer for size which I set to be the size of how many numbers I can hold in an array?
Yes, that is common/idiomatic trick to get the count of the number of elements of a fixed size array:
int array[] = { 1, 2, 3 };
size_t count = sizeof(array) / sizeof(array[0]);
Here, sizeof(array) is 3 times the size [in bytes] of the individual elements [which are int, which is 4 bytes], so we have 3 * 4 or 12.
sizeof(array[0]) is the size of the single, first element of the array, which is [again] an int, so this is 4.
So, when we divide the two, we have 12 / 4 or 3, which is the number of elements.
If so, wouldn't the amount of numbers I can hold be really small if sizeof(original[0]) happens to be very large?
No, because of the division. It doesn't care how large the element size [in bytes] is, because the ratio always produces the number of elements.
The sizeof(arr) / sizeof(arr[0]) trick is useful to get the count when we do: int arr[] = { ... };
If we do:
#define ARRCOUNT 3
int arr[ARRCOUNT] = { 1, 2, 3 };
We already know the count (i.e. it is ARRCOUNT).
The [slight] advantage to the sizeof/sizeof trick is that if we had incorrectly defined ARRCOUNT as 4 by mistake, it would still compile, link, and run, but would produce incorrect results [because there were only 3 elements].
This is a common enough trick that we can define a generic macro [that we can reuse by putting it a .h file]:
#define ARRAY_COUNT(arr_) (sizeof(arr_) / sizeof(arr_))
UPDATE #2:
I've tried your code (even tried copying and pasting it) but my swaps is still showing 9 despite my array to be sorted is just going from { 1 to 10}. Not sure why this keeps occurring.
I believe [now] you have a bug in the sort itself.
I've produced another version that has much more extensive test data generation and comparison.
At a minimum, because of the way the tests are structured, the first element of the sorted array should always have a value of 1.
The test that fails is the one that does a random shuffle of the original array before sending it in to be sorted.
You can add other tests as needed. The array needn't be so large to show the problem. For example, the following single test is enough to produce the error:
bigtest(100,237,1);
Anyway, here is the enhanced diagnostic code:
#include <stdio.h>
#include <stdlib.h>
#define MAXLEN 60
typedef struct {
int *numbers;
int size;
int swaps;
} Array;
Array *
initArray(const int *src,int size,int randshuf)
{
int idx;
Array *array = malloc(sizeof(Array));
array->numbers = malloc(size * sizeof(int));
array->size = size;
array->swaps = 0;
// store in reverse order so the sort will actually do something
switch (randshuf) {
case 0: // reverse the numbers
for (idx = 0; idx < size; ++idx)
array->numbers[size - 1 - idx] = src[idx];
break;
default: // do _crude_ random shuffle
for (idx = 0; idx < size; ++idx)
array->numbers[idx] = 0;
for (idx = 0; idx < size; ++idx) {
while (1) {
int ridx = rand() % size;
if (array->numbers[ridx] == 0) {
array->numbers[ridx] = src[idx];
break;
}
}
}
break;
}
return array;
}
void
freeArray(Array *array)
{
free(array->numbers);
free(array);
}
void
swap(Array *array, int first, int second)
{
int temp = array->numbers[first];
array->numbers[first] = array->numbers[second];
array->numbers[second] = temp;
array->swaps += 1;
}
/* Partition function */
int
partition(Array *array, int start, int end)
{
if (start == end)
return start;
int pivot = array->numbers[end];
int low = start - 1;
int high = end;
for (;;) {
do {
low++;
} while (array->numbers[low] < pivot);
do {
high--;
} while (array->numbers[high] > pivot);
/* Detector for when the cells meet */
if (low >= high) {
swap(array, low, end);
return low;
}
}
/* Swapping the values */
swap(array, low, high);
}
void
quickSort(Array *array, int start, int end)
{
if (end - start <= 0)
return;
//int pivot = array->numbers[end];
int partitionPoint = partition(array, start, end);
quickSort(array, start, partitionPoint - 1);
quickSort(array, partitionPoint + 1, end);
}
void
print_orig(const int *orig,int count)
{
int len = 0;
printf("Test: Original numbers (%d):\n",count);
for (int idx = 0; idx < count; ++idx) {
len += printf(" %10d ", orig[idx]);
if (len >= MAXLEN) {
printf("\n");
len = 0;
}
}
if (len > 0)
printf("\n");
}
int
print_array(Array *array,const int *orig,const char *reason)
{
int len = 0;
int cmp;
int err = -1;
printf("Test: Array Values (%s):\n",reason);
for (int idx = 0; idx < array->size; ++idx) {
int actual = array->numbers[idx];
if (orig != NULL) {
int expected = orig[idx];
cmp = (actual == expected);
}
else
cmp = 1;
len += printf(" %10d%c", actual, cmp ? ' ' : '?');
if (len >= MAXLEN) {
printf("\n");
len = 0;
}
if (cmp)
continue;
if (err < 0)
err = idx;
}
if (orig != NULL)
printf("\nSwaps: %i\nComps: \n\n", array->swaps);
else {
if (len > 0)
printf("\n");
}
return err;
}
void
bigtest(int count,int randgap,int randshuf)
// count -- number of elements (negative means random)
// randgap -- gap between element values (negative means random)
// randshuf -- 0=simple reverse, 1=random shuffle
{
int *orig;
Array *array;
printf("\n");
for (int idx = 1; idx <= 80; ++idx)
printf("-");
printf("\n");
printf("COUNT: %d, RANDGAP: %d, RANDSHUF: %d\n",count,randgap,randshuf);
// get number of elements
if (count < 0)
count = (rand() % count) + 1;
// get element gap (e.g. 1 --> {1, 2, 3}, 2 --> { 1, 3, 5 }
if (randgap < 0)
randgap = (rand() % randgap) + 1;
printf("COUNT: %d, RANDGAP: %d, RANDSHUF: %d\n",count,randgap,randshuf);
// get original array
orig = malloc(sizeof(int) * count);
// fill in original array
do {
int val = 1;
// simple gap
if (randgap >= 0) {
if (randgap == 0)
randgap = 1;
for (int idx = 0; idx < count; ++idx, val += randgap)
orig[idx] = val;
break;
}
// random gap
int gap;
for (int idx = 0; idx < count; ++idx, val += gap) {
orig[idx] = val;
gap = (rand() % randgap) + 1;
}
} while (0);
print_orig(orig,count);
array = initArray(orig,count,randshuf);
print_array(array,NULL,"Shuffled");
quickSort(array, 0, array->size - 1);
print_array(array,orig,"Sorted");
freeArray(array);
free(orig);
}
int
main(void)
{
bigtest(10,0,0);
bigtest(-100,23,0);
bigtest(-1000,-2337,0);
bigtest(-1000,-2337,1);
return 0;
}

Quickselect C-implementation returning unexpected values

I've implemented the following quick-select algorithm in C using pointers instead of arrays to allow for dynamic memory allocation.
#include <stdio.h>
#include <stdlib.h>
int random(int min, int max)
{
return rand() % (max + 1 - min) + min;
}
int quickselect(int *a, int l, int k)
{
int piv = *(a+random(0, l));
int *less = malloc(sizeof(int)*l), *great = malloc(sizeof(int)*l); //allocate to the max possible size
int l_len = 0, g_len = 0;
for (int i = 0; i <= l; i++)
if (*(a+i) < piv)
*(less+l_len++) = a[i]; //set "array indice" (dereferneced pointer) and increment array len
else if (*(a+i) > piv)
*(great+g_len++) = a[i];
less = realloc(less, sizeof(int)*l_len); //re-allocate appropiate to the proper size
great = realloc(great, sizeof(int)*g_len);
if (k <= l_len)
return quickselect(less, l_len, k);
else if (k > l - g_len)
return quickselect(great, g_len, k - (l - g_len));
else
return piv;
}
int main()
{
int a[] = {5,2,6,3,7,1,0};
printf("%d", quickselect(a, sizeof(a)/sizeof(*a), 1));
return 0;
}
When run, I'm getting unexpected values ranging from the negative millions to the positive millions. The randomness of the function makes this hard to debug. Any help?

segfault in merge sort implementation

I'm working on a C implementation as an exercise (I'm a student). I have the logic fine (I've used the implementation itself before), but I get a segfault when actually running it. I've looked for a long time, and I can't understand what's causing it. Here is my complete code:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define ARRAY_CAPACITY 50
void do_sort(int* list);
void merge_sort(int* list_of, int* buffer_of, int start, int end);
void do_sort(int* list)
{
int capacity = ((ARRAY_CAPACITY) / 2);
int buffer[capacity];
merge_sort(list, buffer, 0, ARRAY_CAPACITY);
}
void merge_sort(int* list_of, int* buffer_of, int start, int end)
{
printf("%s", "hi!");
int i, t;
if((end - start) < 2) return;
int mid = (start + end) / 2;
merge_sort(list_of, buffer_of, start, mid);
merge_sort(list_of, buffer_of, mid, end);
int left = 0;
int right = mid;
for(i = 0; i < ARRAY_CAPACITY; i++)
{
buffer_of[i] = list_of[i];
}
for(t = start; t < end; t++)
{
if((left < (mid - start)) && (right == end || buffer_of[left] < list_of[right]))
{
list_of[t] = buffer_of[left];
left++;
}
else
{
list_of[t] = list_of[right];
right++;
}
}
}
int main()
{
srand(time(NULL));
int number_array[ARRAY_CAPACITY];
int i;
for(i = 0; i < ARRAY_CAPACITY; i++)
{
number_array[i] = (rand() % 100);
}
printf("%d\n", number_array[3]);
int j, m;
printf("%s\n", "Pre-Sorted Array: ");
for(j = 0; j < ARRAY_CAPACITY; j++)
{
printf("%d ", number_array[j]);
}
do_sort(number_array);
for(m = 0; m < ARRAY_CAPACITY; m++)
{
printf("%d ", number_array[m]);
}
printf("\n");
}
The output is as follows:
50 (this is a random number, but it always prints successfully)
Pre-Sorted Array:
Segmentation fault
So the segfault triggers when I try to loop to print the pre sorted array, but I've just proven that the array values were properly set, so I can't fathom this error. Help?
You have the following code:
void merge_sort(int* list_of, int* buffer_of, int start, int end)
{
...
for(i = 0; i < ARRAY_CAPACITY; i++)
{
buffer_of[i] = list_of[i];
}
...
That code will get called, at one point, with the following arguments:
list_of is an array of 50 integers.
buffer_of is an array of 25 integers.
start is 0.
end is 50.
You will copy 50 elements of list_of into buffer_of, but buffer_of has only room for 25 elements.

Resources