C - recursive function loses tracks of array's address? - arrays

I am implementing a binary search. I seem to have solved my problem but I'd like to understand what was going on.
Here is what I was originally doing for my search:
void find_index(double *list, int val, int low, int high, int *target)
{
int mid;
if (list[low] == val)
{
target[0] = low;
return;
}
if (list[high] == val)
{
target[0] = high;
return;
}
mid = (int)((double)(low + high) / 2.0);
if (list[mid] == val || mid == low || mid == high)
{
target[0] = mid;
return;
}
if (list[low] < val && val < list[mid])
{
high = mid;
find_index(list, val, low, high, target);
}
if (list[mid] < val && val < list[high])
{
low = mid;
find_index(list, val, low, high, target);
}
return;
}
In the code above, list is a sorted array of doubles. I allocated it dynamically in the main,
double *list = malloc((N+1)*sizeof(double));
and val is the value to be searched (guaranteed to be in list). The main calls func1, which calls find_index. Here's the story:
Initially I allocated statically a size1 array, int target[1] into func1 and
passed it to find_index. I got a segmentation fault. Tracking the origin of the error with valgrind I found that, in func1, I was using an uninitialized value AND THAT THE UNINITIALIZED VALUE WAS CREATED IN func1. target is the only array I allocate in such function so it had to be it.
I changed the declaration of find_index. Specifically, int *target became
int target[1] as I seem to understand from this question that this is the proper way of passing statically allocated arrays (despite it deals with 2D arrays). I got segmentation fault again and the same message from valgrind.
I declared target dynamically in the main int *target = malloc(1*sizeof(int));, passed it to func1 and then to find_index. The way I passed it to both functions is the same as how I was originally doing, i.e., both functions takes as argument a int *target. It worked, no complain from valgrind.
However I am suspicious now so I decided to change the type of find_index to int and declared target as a scalar into find_index itself. To my understanding of recursions, it should be safe.
However, I do not understand what was going on during steps 1-3 and I'd like to, so to prevent future mistakes. My only guess is that, during the numerous recursive calls that find_index implements, it loses track of the address of the array I originally allocated. Is this something that can happen? Under what circumstances? Could it be related to how I pass the array to the functions and/or to where I originally declared?
I have searched but could not find a truly related question.
EDIT: minimal reproducible example shows the situation is more involved
The minimal reproducible example I could build follows:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <math.h>
#include <time.h>
void func1(double **list, double **another_list, int N);
void func2(double **list, double **another_list, int N);
void find_index(double *list, int val, int low, int high, int *target);
void q_sort_with_doublelst(double *ary,double *lst,int low,int high);
int q_partition_with_doublelst(double *ary,double *lst,int low,int high);
void swap_with_doublelst(double *ary,double *lst,int i,int j);
////////////////////////////////////////////////////////////////////////
// quick sort
void q_sort_with_doublelst(double *ary,double *lst,int low,int high)
{
int pivotloc;
if(low<high)
{
pivotloc=q_partition_with_doublelst(ary,lst,low,high);
q_sort_with_doublelst(ary,lst,low,pivotloc-1);
q_sort_with_doublelst(ary,lst,pivotloc+1,high);
}
}
int q_partition_with_doublelst(double *ary,double *lst,int low,int high)
{
int i,pivotloc;
int pivotkey;
swap_with_doublelst(ary,lst,low,(low+high)/2);
pivotkey=ary[low];
pivotloc=low;
for(i=low+1;i<=high;i++)
{
if(ary[i]<pivotkey) swap_with_doublelst(ary,lst,++pivotloc,i);
}
swap_with_doublelst(ary,lst,low,pivotloc);
return pivotloc;
}
void swap_with_doublelst(double *ary,double *lst,int i,int j)
{
int tmp_ary;
double tmp_lst;
tmp_ary=ary[i]; tmp_lst=lst[i];
ary[i]=ary[j]; lst[i]=lst[j];
ary[j]=tmp_ary; lst[j]=tmp_lst;
}
////////////////////////////////////////////////////////////////////////
void func2(double **list, double **another_list, int N)
{
int i, j;
double x;
for(i=0; i<=N; i++)
{
j = (int) lrand48()%(N+1);
x = lrand48();
list[0][i] = j;
list[1][i] = x;
j = (int) lrand48()%(N+1);
x = lrand48();
another_list[0][i] = j;
another_list[1][i] = x;
}
}
void func1(double **list, double **another_list, int N)
{
int i, j;
int binary_index[1];
double x, y;
for(i=0; i<=N; i++)
{
find_index(list[0], i, 0, N, binary_index);
j = binary_index[0];
x = another_list[0][j];
y = another_list[1][j];
}
return;
}
void find_index(double *list, int val, int low, int high, int *target)
{
int mid;
if (list[low] == val)
{
target[0] = low;
return;
}
if (list[high] == val)
{
target[0] = high;
return;
}
mid = (int)((double)(low + high) / 2.0);
if (list[mid] == val || mid == low || mid == high)
{
target[0] = mid;
return;
}
if (list[low] < val && val < list[mid])
{
high = mid;
find_index(list, val, low, high, target);
}
if (list[mid] < val && val < list[high])
{
low = mid;
find_index(list, val, low, high, target);
}
return;
}
int main (int argc, char **argv)
{
int i, N;
int seed=time(0);
srand48(seed);
N = 1000000;
double **list = (double **)malloc(2*sizeof(double *));
list[0] = (double *)malloc((N+1)*sizeof(double)); // contains only ints
list[1] = (double *)malloc((N+1)*sizeof(double)); // contains double
double **another_list = (double **)malloc(2*sizeof(double *));
another_list[0] = (double *)malloc((N+1)*sizeof(double)); // contains only ints
another_list[1] = (double *)malloc((N+1)*sizeof(double)); // contains double
for(i=0; i<=N; i++) list[0][i] = i;
while(1 < 2)
{
func2(list, another_list, N);
q_sort_with_doublelst(list[0], list[1], 0, N);
func1(list, another_list, N);
}
free(list[0]);
free(list[1]);
free(list);
free(another_list[0]);
free(another_list[1]);
free(another_list);
return 0;
}
I tried to reproduce the error by deterministically filling list[0] with the for loop in the main (line 173), so to have it already sorted. It turns out it does not crash. The code I am posting is more similar to my true code, but now I am afraid (as comments suggest) that I have a problem with recursions. A summary of what the minimal example does (very similarly to my code):
Fill list[0] with random integers in [0, N]. Fill list1 with random double.
Fill another_list in the same way.
Sort list[0] by preserving the relation with list[1].
Binary-search list[0] and use the index found to take values from list[1] and another_list.
There is a difference between this example and my true code: here list[0] is not guaranteed to contain the value I am searching. Further, here list[0] may contain duplicates. In principle the search is written with the idea that the value searched needs not to be present and the idea that there are no duplicates. Thus, I would say that if the value is not present, the search should give me the closest value found, but I have no clue to what happens if there are duplicates. I guess it should give me the index of one the possible duplicates, but I am definitely unsure. In my original code there are no duplicates.
I am afraid the present code crashes because of the presence of duplicates. Still, it would be great if someone can identify a different problem. Let me stress that valgrind gives me the same exact message: the error should with target, as it is the only array allocated in func1.

Related

Small Triangles, Large Triangles code not working

So, I had been trying to write the code for the Small Triangles, Large Triangles problem of C in Hackerrank. Before, I state what problem I'm facing, I'll attach the question-
I only wrote the sort_by_area, swap and area functions here. The rest of it was given and unchangeable. The code I've written is getting executed properly but the structures aren't getting sorted correctly. Here is the expected output & my output-
I just cannot figure out why it is getting such weirdly swapped. If anyone could help, would mean a lot.
My code is-
#include <stdlib.h>
#include <math.h>
struct triangle
{
int a;
int b;
int c;
};
typedef struct triangle triangle;
void sort_by_area(triangle* tr, int n) {
int i, j, swapped;
for (i = 0; i < n-1; i++)
{
swapped = 0;
for (j = 0; j < n-i-1; j++)
{
if (area(tr[j].a, tr[j].b, tr[j].c) > area(tr[j+1].a, tr[j+1].b, tr[j+1].c))
{
swap(&tr[j], &tr[j+1]);
swapped = 1;
}
}
if (swapped == 0)
break;
}
}
void swap(struct triangle **xp, struct triangle **yp)
{
struct triangle *temp = *xp;
*xp = *yp;
*yp = temp;
}
int area(int a, int b, int c){
int p=(a+b+c)/2;
int q=p*(p-a)*(p-b)*(p-c);
return sqrt(q);
}
int main()
{
int n;
scanf("%d", &n);
triangle *tr = malloc(n * sizeof(triangle));
for (int i = 0; i < n; i++) {
scanf("%d%d%d", &tr[i].a, &tr[i].b, &tr[i].c);
}
sort_by_area(tr, n);
for (int i = 0; i < n; i++) {
printf("%d %d %d\n", tr[i].a, tr[i].b, tr[i].c);
}
return 0;
}```
Enable all warnings
This quickly led to swap() swapping pointers and not data.
// Corrected - swap data
void swap(struct triangle *xp, struct triangle *yp) {
struct triangle temp = *xp;
*xp = *yp;
*yp = temp;
}
Function order
Move area(), swap() definitions before calling them.
Area
(int) sqrt(q) may return the same value for different qs.
Example: (int) sqrt(100), (int) sqrt(110), (int) sqrt(120)
All return 10. Sorting will not certainly sort according to area.
Simple return the square of the area. Mathematically, sorting by area squared same as area.
int area_squared(int a, int b, int c){
int p=(a+b+c)/2;
int q=p*(p-a)*(p-b)*(p-c);
// return sqrt(q);
return q;
}
Although one could code using double, let us stay with integers.
Watch out for a+b+c as odd as odd/2 forms a truncated quotient.
Perhaps return the square of the area, scaled each side by 2?
int area_squared2(int a, int b, int c){
a *= 2; b *= 2; c *= 2;
// a+b+c is certianly even
int p=(a+b+c)/2;
int q=p*(p-a)*(p-b)*(p-c);
return q;
}
A remaining concern is int overflow. Consider long long math.
long long area_squared2LL(int a, int b, int c){
long long aa = a * 2LL;
long long bb = b * 2LL;
long long cc = c * 2LL;
long long pp = (aa+bb+cc)/2;
long long qq = pp*(pp-aa)*(pp-bb)*(pp-cc);
return qq;
}
Tip: Allocate by referenced data, not type
Easier to code right, review and maintain.
// triangle *tr = malloc(n * sizeof(triangle));
triangle *tr = malloc(sizeof *tr * n);
if (tr == NULL) {
// use tr
...
free(tr);
tr = NULL;
}

error in C using malloc : corrupted size vs prev_size

I found an answer for python but I didn't understand it.
The code is a modified merge sort. It is working fine for a small number of inputs I checked upto 10. But when I run it through an online judge, when number of inputs were high (500) it gave me this error:
Error in 'a.out': corrupted size vs. prev_size: 0x0000000000d5b8b0
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x777e5)[0x7f3b83a5b7e5]
/lib/x86_64-linux-gnu/libc.so.6(+0x80dfb)[0x7f3b83a64dfb]
/lib/x86_64-linux-gnu/libc.so.6(cfree+0x4c)[0x7f3b83a6853c]
a.out[0x4009d1]
a.out[0x400ac7]
a.out[0x400a87]
a.out[0x400aa4]
a.out[0x400a87]
a.out[0x400bc7]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0)[0x7f3b83a04830]
a.out[0x4005b9]
======= Memory map: ========
and it goes for another 15 lines. Why am I getting this error? Is it because of some mistake I am making during dynamic allocation of memory using malloc?
Here's my code:
#include <stdio.h>
#include <stdlib.h>
void *Merge(int *A,int l,int m,int r,int *B,int *F);
void *Merge(int *A,int l,int m,int r,int *B,int *F){
int i=l,j=m,k=0,*C,x,y=l,z,cSize,temp,*D,*E;
cSize = r-l;
C = (int *) malloc (cSize * sizeof(int));
D = (int *) malloc (cSize * sizeof(int));
E = (int *) malloc (cSize * sizeof(int));
while (k < cSize){
if((j==r) || ((i!=m) && ((A[j]*B[i]) >= (A[i]*B[j])))){
C[k] = A[i];
D[k] = B[i];
E[k] = F[i];
i++;
k++;
}
if((i>=m) || ((j!=r) && ((A[j]*B[i]) < (A[i]*B[j])))){
C[k] = A[j];
D[k] = B[j];
E[k] = F[j];
j++;
k++;
}
}
for(x=0;x<k;x++){
A[y] = C[x];
B[y] = D[x];
F[y] = E[x];
y++;
}
free(C);
free(D);
free(E);
}
void *MergeSort(int *A,int left,int right,int *B,int *C);
void *MergeSort(int *A,int left,int right,int *B,int *C){
int mid,i,j,k=0,l=0,*R,*L;
if(right - left == 1){
A[left] = A[left];
}
if(right-left > 1){
mid = (left+right)/2;
MergeSort(A,left,mid,B,C);
MergeSort(A,mid,right,B,C);
Merge(A,left,mid,right,B,C);
}
}
int main(){
int n,i=0,newNumt,newNumo,*a,*b,*c;
scanf("%d",&n);
a = (int *) malloc (n * sizeof(int));
b = (int *) malloc (n * sizeof(int));
c = (int *) malloc (n * sizeof(int));
for(i=0;i<n;i++){
scanf("%d %d",&a[i],&b[i]);
c[i]= i+1;
}
MergeSort(a,0,n,b,c);
for(i=0;i<n;i++){
printf("%d\n",c[i]);
}
return 0;
}
This is an old post, but several issues appear left unaddressed, so the following attempts to address some of those you specifically mentioned in your post.
As mentioned in the comments the nature of the issues causing your main stated problem was found by setting -Wall during compile, then running it through a debugger, with up to 20 sets of integer pairs entered upon the prompt.
Below is your complete code with several modifications. Some are only suggestions, but others are responses to compile warnings, some more important than others. These are all commented.
Addressing one of your primary questions: Why am I getting this error? Is it because of some mistake I am making during dynamic allocation of memory using malloc?:
As mentioned my #Jonathan Leffler, it is not likely a problem with memory allocation, but the attempt to access memory that has not been allocated.
The noteworthy run-time error relating to this was seen when running your unmodified code, and is marked with a comment in the Merge() function, where the index k is incremented to a value larger than it should, causing a Dereference of out-of-bounds pointer error. The quick fix was to make the two if sections mutually exclusive by adding an else to the second one. This modification does prevent the run-time error, but is not necessarily the right (or only) change needed.
One other suggestion that I did not address in the code is the selection of variable names. As written many are too cryptic and do not add readability or understanding to what the code is attempting to do. The suggestion would be to use variable names that would allow another person (or even your self, when 3 years from now you are looking at this again.) to immediately understand what the purpose of the variable is.
Read the comments for changes to understand why they are there...
#include <stdio.h>
#include <stdlib.h>
void *Merge(int *A,int l,int m,int r,int *B,int *F);
void *MergeSort(int *A,int left,int right,int *B,int *C);
int main()
{
// int n,i=0,newNumt,newNumo,*a,*b,*c;
int n,i=0,*a,*b,*c; //removed unused newNumt & newNumo
printf("\nEnter number of integer pairs:\n");//user input instructions
scanf("%d",&n);
a = calloc (n, sizeof(int));//see comment in MergeSort for similar
b = calloc (n, sizeof(int));//suggested change for malloc/calloc
c = calloc (n, sizeof(int));
for(i=0;i<n;i++)
{
printf("\n%d) Enter two integer values:\n", i);//user input instructions
scanf("%d %d",&a[i],&b[i]);
c[i]= i+1;
}
MergeSort(a,0,n,b,c);
for(i=0;i<n;i++)
{
printf("%d\n",c[i]);
}
return 0;
}
void *Merge(int *A, int l, int m, int r, int *B, int *F)
{
//int i=l,j=m,k=0,*C,x,y=l,z,cSize,temp,*D,*E;
int i=l,j=m,k=0,*C,x,y=l,cSize,*D,*E;//removed unused z and temp
cSize = r-l;
// C = (int *) malloc (cSize * sizeof(int));
// D = (int *) malloc (cSize * sizeof(int));
// E = (int *) malloc (cSize * sizeof(int));
C = calloc (cSize, sizeof(int)); //it is not recommended to cast the return
D = calloc (cSize, sizeof(int)); //of malloc/calloc/realloc in C
E = calloc (cSize, sizeof(int)); //changed malloc to calloc to initialize
//variable memory to zero before use
// Only one or the other of the following two if statements should be executed per loop,
// by running both an access violation occurs causing crash. (eg. when k is incremented twice
// before being tested.)
while (k < cSize)//k is tested only once per loop...
{
if((j==r) || ((i!=m) && ((A[j]*B[i]) >= (A[i]*B[j]))))
{
C[k] = A[i];
D[k] = B[i];
E[k] = F[i];
i++;
k++;//if k == csize-1, it will be incremented to k == csize, then go into the next section
}
else if((i>=m) || ((j!=r) && ((A[j]*B[i]) < (A[i]*B[j])))) //added else
{
C[k] = A[j]; //Dereference of out-of-bounds pointer occurs here when k is too large.
D[k] = B[j];
E[k] = F[j];
j++;
k++;// ... but possibly increment twice!
}
}
for(x=0;x<k;x++)
{
A[y] = C[x];
B[y] = D[x];
F[y] = E[x];
y++;
}
free(C);
free(D);
free(E);
return 0; //function prototype requires a return to quiet the warnings
//Only void function prototypes do not require a return statement
}
void *MergeSort(int *A,int left,int right,int *B,int *C)
{
//int mid,i,j,k=0,l=0,*R,*L;
int mid = 0; //removed all unused variables and initialized mid
if(right - left == 1)
{
A[left] = A[left];
}
if(right - left > 1)
{
mid = (left + right)/2; // integer rounding
MergeSort(A, left, mid, B, C);
MergeSort(A, mid, right, B, C);
Merge(A, left, mid, right, B, C);
}
return 0; //function prototype requires a return to quiet the warnings
//Only void function prototypes do not require a return statement
}

Code Bug: Segmentation Fault [EVERYTIME]

I am writing this C/C++ program that is suppose to find the mean, median, and mode of a varied size array. Although, I keep getting a Segmentation Fault regardless of the input. What is wrong with my code? Any suggestions always appreciated! :)
Here is the code:
#include <stdio.h>
//#include <string.h>
//#include <math.h>
#include <stdlib.h>
Prototypes:
void sort(double*[],int);
static int min(double,double[],int);
double mean(double[],int);
double median(double[],int);
double mode(double[],int);
int numberOf(double,double[],int);
Main Function:
int main() {
int i;
scanf(" %d ",&i); //10
double arr[i]; //array that contains all the values and will be sortted
for (int j=0; j<i; j++) { //64630 11735 14216 99233 14470 4978 73429 38120 51135 67060
scanf(" %lf ",&arr[j]);
}
printf("%.1lf\n%.1lf\n%.0lf",mean(arr,i),median(arr,i),mode(arr,i));
return 0;
}
Sort Function:
The end result should update the array arr from the call in the Median Function. Changes the used values in the original array to -1 until that is the entire array.
void sort(double* arr[],int l) {
double arr2[l];
for (int i=0; i<l; i++) {
int j;
if (i)
j = min(arr2[i-1], *arr, l);
else
j = min(0, *arr, l);
arr2[i] = *arr[j];
*arr[j] = -1;
}
for (int i=0; i<l; i++) {
*arr[i] = arr2[i];
}
}
Min Function (helper function for the Sort Function):
Finds the minimum value amongst the array elements that is greater than or equal to minLookingTo
Returns the position the value is in.
static int min(double minLookingTo,double arr[],int l) {
int minP;
double minA = minLookingTo;
for (int i=0; i<l; i++) {
if (arr[i] == -1)
continue;
if (minLookingTo<=arr[i] && arr[i]<=minA) {
minP = i;
minA = arr[i];
}
}
return minP;
}
Mean Function:
Returns the mean of the inputted array with the length l
double mean(double arr[],int l){
double total = 0;
for (int i=0; i<l; i++) {
total += arr[i];
}
return total/l;
}
Median Function:
Uses the Sort Function. Assuming that works, returns the median.
double median(double arr[],int l){
sort(&arr,l);
double d = arr[(l/2)+1];
double dd = arr[(l/2)];
if (l%2!=0)
return d;
return (d+dd)/2;
}
Mode Function:
Uses the NumberOf Function to determine the array element with the maximum amount of repeats. Returns the lowest value of the highest (equal) repeats.
double mode(double arr[],int l){
int maxA;
int maxP;
for (int i=0;i<l;i++) {
int j = numberOf(arr[i],arr,l);
if (j>maxA) {
maxA = j;
maxP = i;
}
else if (j==maxA && arr[maxP]>arr[i])
maxP = i;
}
double d = arr[maxP];
return d;
}
NumberOf Function:
Helper function for the Mode Function. Returns the amount of elements with the looking value.
int numberOf(double looking,double arr[],int l) {
int amount = 0;
for (int i=0; i<l; i++)
if (looking == arr[i])
amount++;
return amount;
}
I tracked your segmentation fault to your sort() routine called by median(). Rather than fix sort(), I substituted qsort() from the library to convince myself that's the problem:
// Median Function:
// Uses the Sort Function. Assuming that works, returns the median.
int comparator(const void *p, const void *q) {
double a = *((double *) p);
double b = *((double *) q);
return (a > b) - (a < b); // compare idiom
}
double median(double array[], int length) {
// sort(array, length);
qsort(array, length, sizeof(double), &comparator);
double d = array[length / 2];
if (length % 2 != 0) {
return d;
}
double dd = array[(length / 2) - 1];
return (d + dd) / 2;
}
For the example list of numbers provided, after correcting the rest of the code, this returns a median of 44627.5
Other fixes:
You're missing a final newline here:
printf("%.1lf\n%.1lf\n%.0lf",mean(arr,i),median(arr,i),mode(arr,i));
You should probably initialize the variables in mode():
double mode(double array[], int length) {
int maxA = INT_MIN;
int maxP = -1;
for (int i = 0; i < length; i++) {
int j = numberOf(array[i], array, length);
if (j > maxA) {
maxA = j;
maxP = i;
} else if (j == maxA && array[maxP] > array[i]) {
maxP = i;
}
}
return array[maxP];
}
Your code has a series of errors. Some of them:
You don´t need (in this case) to use spaces in scanf. This is causing a reading error.
You don't need to pass an array address to a function in order to alter its values. Arrays are always passed by reference. So change your function from void sort(double*[],int); to void sort(double[],int);, make the necessary corrections inside the function and call it using sort(arr,l); instead of sort(&arr,l);
Your min() function declares an uninitialized variable minP, so this variable contains garbage from your memory. The for() loop isn't entering none of the both if() conditions, so your function ends and returns the still uninitialized variable minP. This random value is then used to access an index in your array: j = min(0, arr, l); min returns an random number and then arr2[i] = arr[j]; accessing forbidden memory region, which is causing your segmentation fault error. The same problem is occurring with the variables maxP and maxA in the mode() function.
You must always be careful when accessing your arrays to not go beyond its bounds and always be sure that variables will be initialized when using them. And as others have commented, I also highly recommend you to learn how to debug your programs, since this will help you to analyze its execution and trace bugs.

C - Custom qsort not working

I am trying to make a qsort type of function that has the same paramenters. I also wrote 3 functions to compare int, float and characters. For some reason it does not work in any case.
I don't know whether this is a problem regarded my qsortx function or not, but I checked it several times and it should work perfectly fine. I am not sure what the problem is, or what I am doing wrong. I am currently learning the function pointers and I might not have got everything right related to it. Thanks in advance.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void qsortx(void*, int, int, int (*)(const void*, const void*));
int intcmp();
int floatcmp();
int charcmp();
int main()
{
int i,n;
char items[]={'c', 'a', 'b'};
n = 3;
for (i=0;i<n;++i) {
printf("%c ", items[i]);
}
printf("\n");
qsortx(items, n, sizeof(char), charcmp);
for (i=0;i<n;++i) {
printf("%c ", items[i]);
}
printf("\n");
return 0;
}
void qsortx (void *tp, int length, int pace, int(*fp)(const void* a, const void* b)) {
int switched,i,j;
void *p;
p=(void*)malloc(pace);
switched = 1;
while (1) {
if (switched == 0) {
return;
}
switched = 0;
for (i=0; i<length-1;++i) {
for (j=0;j<length-1;++j) {
printf("%c %c", tp+i, tp+j);
if (fp(tp+i, tp+j) > 0) {
memcpy(p, tp+i, pace);
memcpy(tp+i, tp+j, pace);
memcpy(tp+j, p, pace);
switched++;
}
}
}
}
}
int intcmp(const void* a, const void* b) {
return *(int*)a - *(int*)b;
}
int floatcmp(const void* a, const void* b) {
return *(float*)a - *(float*)b;
}
int charcmp(const void* a, const void* b) {
return *(char*)a - *(char*)b;
}
You have multiple problems related to pointer arithmetic and element sizes. You also have a logic error in your sort (which I guess you know is a unidirectional shaker sort). Here's a version of the qsortx() function that fixes these deficiencies:
void qsortx (void *tp, int length, int pace, int(*fp)(const void* a, const void* b)) {
if (length > 1) {
char *bound = ((char *) tp) + (length * pace);
char *p = malloc(pace);
char *item1p;
for (item1p = tp; item1p < (bound - pace); item1p += pace) {
char *item2p;
for (item2p = item1p + pace; item2p < bound; item2p += pace) {
if (fp(item1p, item2p) > 0) {
memcpy(p, item1p, pace);
memcpy(item1p, item2p, pace);
memcpy(item2p, p, pace);
}
}
}
free(p);
}
}
Note that:
All pointer arithmetic is performed on values of type char *.
The element size (pace) must be taken into account as you step through the input array, else you just scramble your data.
The innermost loop should start at the element after the one being considered in the next-outer loop.
switched = 1 is a better choice than switched ++ because it cannot overflow, and all you care about is zero vs. non-zero. (Update: but switched is no longer relevant.)
(Update) It is incorrect to exit early in the event that a pass through the item1p loop results in zero swaps. Just because one element is already in its correct place does not mean that all the subsequent elements are also in their correct places. I updated my code above to remove that behavior.
(Update) As chux observed, the temporary space reserved for swapping elements was not freed. I added an appropriate free(p).
(Update) I also made sorting conditional on the array length being greater than 1, which avoids undefined behavior associated with bound - pace in the event that the length is zero.
here is the pseudo code and implementation of the quicksort (qsort) algorithm, with some accessory code, as defined in the http://www.codingbot.net/2013/01/quick-sort-algorithm-and-c-code.html web page:
Note that this algorithm is slightly different from qsort()
in that there is a different parameter list and certain other details.
However, the basic algorithm is the same.
function quicksort('array')
if length('array') ≤ 1
return 'array' // an array of zero or one elements is already sorted
select and remove a pivot value 'pivot' from 'array'
create empty lists 'less' and 'greater'
for each 'x' in 'array'
if 'x' ≤ 'pivot'
then append 'x' to 'less'
else
append 'x' to 'greater'
endif
end for
return concatenate(quicksort('less'), 'pivot', quicksort('greater') );
notice that qsort is a partition sort, using recursion.
#include<stdio.h>
#include<conio.h>
void quick_sort(int arr[20],int,int);
int main()
{
int arr[20],n,i;
clrscr();
printf("Enter the number of elements in the Array: ");
if( 1 != scanf(" %d",&n) )
{
perror( "scanf for count of elements" );
exit(1);
}
printf("\nEnter %d elements:\n\n",n);
for(i=0 ; i<n ; i++)
{
printf(" Array[%d] = ",i);
if( 1 != scanf(" %d",&arr[i]) )
{
perror( "scanf for element values" );
exit(2);
}
}
quick_sort(arr,0,n-1);
printf("\nThe Sorted Array is:\n\n");
for(i=0 ; i<n ; i++)
{
printf(" %4d",arr[i]);
}
getch();
}
void quick_sort(int arr[20],int low,int high)
{
int pivot; // used in partitioning the array
int j; // loop index
int temp; // for swapping
int i; // loop index
if(low<high)
{
pivot = low;
i = low;
j = high;
while(i<j)
{
// find next item not in proper sequence
while((arr[i] <= arr[pivot]) && (i<high))
{
i++;
}
// find next item not in proper sequence
while(arr[j] > arr[pivot])
{
j--;
}
// following is where a callback function would be invoked
if(i<j)
{
temp=arr[i];
arr[i]=arr[j];
arr[j]=temp;
}
}
temp=arr[pivot];
arr[pivot] = arr[j];
arr[j]=temp;
// following is where recursion is used to perform sort on sub partitions
quick_sort(arr,low,j-1);
quick_sort(arr,j+1,high);
}
}
this is a much better algorithm for your purposes.
however, it only handles integers, so you would need to
add the comparison function as a 4th parameter to quicksort()
and modify the code to use your comparison function
#include <stdio.h>
#include <stdlib.h>
void swap(int *x,int *y);
int choose_pivot(int i,int j );
void quicksort(int list[],int m,int n);
void display(int list[],const int n);
int main()
{
const int SIZE = 10;
int list[SIZE];
int i = 0;
/* generates random numbers and fill the list */
for(i = 0; i < SIZE; i++ )
{
list[i] = rand();
}
printf("The list before sorting is:\n");
display(list,SIZE);
/* sort the list using quicksort algorithm */
quicksort(list,0,SIZE-1);
printf("The list after sorting:\n");
display(list,SIZE);
}
void swap(int *x,int *y)
{
// for integer swaps, 3 exclusive OR operations would be much faster
// and not require a temp variable
int temp;
temp = *x;
*x = *y;
*y = temp;
}
int choose_pivot(int i,int j )
{
return((i+j) /2);
}
void quicksort(int list[],int m,int n)
{
int key,i,j,k;
if( m < n)
{
k = choose_pivot(m,n);
swap(&list[m],&list[k]);
key = list[m];
i = m+1;
j = n;
while(i <= j)
{
while((i <= n) && (list[i] <= key))
{
i++;
}
while((j >= m) && (list[j] > key))
{
j--;
}
if( i < j)
{
swap(&list[i],&list[j]);
}
}
/* swap two elements */
swap(&list[m],&list[j]);
/* recursively sort the lesser list */
quicksort(list,m,j-1);
quicksort(list,j+1,n);
}
}
void display(int list[],const int n)
{
int i;
for(i=0; i<n; i++)
{
printf("%d\t",list[i]);
}
}

C Mergesort Segfault

Situation
I was trying to implement a more interesting mergesort that creates a random length array with random values and then randomizes them, but after debugging and compiling it segfaults. I don't know why it segfaults, but I'm sure it's related to memory allocation.
Question
Why does this code cause a segfault?
Code
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
// Declare some stuff up front
int array_size(int *array);
int print_array(int *array);
//Some decade old main function coming at you
int main() {
//Concerned with the integrity of my rand
srand( (unsigned)time( NULL ));
//A global, random length array between 1 and 100?
int *array;
array = malloc(sizeof(*array) * ((rand() % 100) + 1));
init_array(*array);
getchar();
return 0;
}
int init_array(int *array) {
//Base case
array[0] = 1;
//random values for i in array
int i;
for(i = 1; i <= array_size(array); i++) {
array[i] = rand() % array_size(array) + 1;
}
//randomize the random values in the random length array
for (i = 0; i < (array_size(array) - 1); i++)
{
unsigned int swapA = (rand() % array_size(array)) + 1;
int a = array[swapA];
array[swapA] = array[i];
array[i] = a;
}
//output random array, then mergeSort the array
print_array(array);
sort_array(array);
return 0;
}
//Get my array.Length
int array_size(int *array) {
return sizeof(array)/sizeof(array[0]);
}
//Output array
int print_array(int *array) {
int i;
for(i = 0; i < (array_size(array) + 1); i++) {
printf("%d\n", array[i]);
}
return 0;
}
//merge the array after sorting
void merge_array(int *array, int low, int split, int high) {
int sorted[high-low+1];
int a = 0;
int b = low;
int c = split + 1;
//iterate from beginning to middle and from middle to end in parallel
while(b <= split && c <= high)
{
if(array[b] < array[c])
{
sorted[a++] = array[b++];
}
else
{
sorted[a++] = array[c++];
}
}
while(b <= split) sorted[a++] = array[b++];
while(c <= high) sorted[a++] = array[c++];
int i;
for(i = 0; i < a; i++) {
array[i+low] = sorted[i];
}
print_array(array); //Print sorted array
}
//Sort the array
int sort_array(int *array, int low, int high) {
int split = ( low + high ) / 2;
if( low < high ) {
sort_array(array, low, split);
sort_array(array, split + 1, high);
merge_array(array, low, split, high);
}
}
return sizeof(array)/sizeof(array[0]);
The above statement evaluates to 1 (assuming sizeof(int *) = sizeof(int), as pointed out by H2CO3).
Try something like this,
int main() {
//Concerned with the integrity of my rand
srand( (unsigned)time( NULL ));
//A global, random length array between 1 and 100?
int *array;
int number_of_elements = (rand() % 100) + 1;
array = malloc(sizeof(*array) * num_of_elements);
init_array(*array, num_of_elements);
getchar();
return 0;
}
Pass the number of elements as arguments to init_array instead of calculating it every time.
This seems to be the problem:
//Get my array.Length
int array_size(int *array) {
return sizeof(array)/sizeof(array[0]);
}
You essentially return sizeof(int*)/sizeof(int), which is not what you want. This whole thing appears because arrays decay into pointers when passed to functions.
You should read the Arrays and Pointers section in the comp.lang.c FAQ for edification.
What happens when you run your program with /WALL? What warnings are being spat out? Why?
What happens when you step through your program with a debugger attached? What is the value of each variable at each line? Why?
There are several problems with your code:
You don't check the result of malloc to see if it returned NULL.
You are passing the dereference of array to init_array, i.e. you are sending the first int of the array to init_array which then promptly dereferences it. Since malloc returns garbage data, you're dereferencing a random number inside of init_array.
array_size is not magic. If you do not track the size of your arrays in C, you cannot retrospectively find out how big you wanted them to be. You need to remember the size of the array and pass it to init_array.

Resources