Related
I have this project where I need to create an array, split it into two, and then sort each array using its own thread, then merge the two arrays back into a single array. Thankfully, the final array doesn't need to be sorted itself.
I wrote this code:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
//Change length of main input array
const int arrLen = 20;
const int mid = arrLen/2;
void swap(int *a, int *b){
int temp = *a;
*a = *b;
*b = temp;
}
void *sort(void *arg){
int min_pos;
for(int a=0; a<arrLen-1; a++){
min_pos = a;
for(int b=a+1; b<arrLen; b++){
if(array[b] < array[min_pos]){
min_pos = b;
}
}
swap(&array[min_pos], &array[a]);
}
}
void printResult(int array[]){
for(int k=0; k<arrLen/2; k++){
printf("Num%d: %d\n", k, array[k]);
}
}
//Adds random values to each position in a given array
void arrayCreator(int array[]){
int random;
for(int i=0; i<arrLen; i++){
random = rand() % 100;
array[i] = random;
}
}
void main(){
printf("Program Initialized...\n");
int mainArray [arrLen], firstHalf[mid], secondHalf[mid], random;
time_t t;
srand((unsigned) time(&t));
//Populate mainArray
for(int i=0; i<arrLen; i++){
mainArray[i] = rand()%100;
}
printf("Array created...\n");
//Split mainArray[] into two seperate arrays
for(int p=0; p<arrLen; p++){
if(p<mid){
firstHalf[p]=mainArray[p];
}
else{
secondHalf[p-mid]=mainArray[p];
}
}
pthread_t tid1;
pthread_t tid2;
pthread_create(&tid1, NULL, sort, (void*)firstHalf);
//pthread_create(&tid2, NULL, sort, secondHalf);
printResult(firstHalf);
//printResult(secondHalf);
}
How can I get the sort function to access my array?
First, I would look at the signature of the standard function qsort and provide the same interface for your sort function. I'd also rename it into bubble_sort since it's doing bubble sorting. Your function would then look like this:
void bubble_sort(void *ptr, size_t arrLen, size_t elem_size,
int (*comp)(const void *, const void *)) {
char *array = ptr; // to be able to do pointer arithmetic
for (size_t a = 0; a < arrLen - 1; a++) {
for (size_t b = a + 1; b < arrLen; b++) {
if (comp(array + a * elem_size, array + b * elem_size) > 0) {
swap(array + a * elem_size, array + b * elem_size, elem_size);
}
}
}
}
elem_size is the size of the elements you want to sort, which will be set to sizeof(int) later.
comp is a function taking two void* and returns an int where ...
< 0 means *a < *b
> 0 means *a > *b
== 0 means *a == *b
swap also takes the elem_size parameter to be able to swap objects of any size. Implemented, it could look like this:
void swap(void *a, void *b, size_t elem_size) {
char tmp[elem_size];
memcpy(tmp, a, elem_size);
memcpy(a, b, elem_size);
memcpy(b, tmp, elem_size);
}
Now, you can't start a thread by calling bubble_sort directly. A thread start routine only takes a single void* as an argument so we need some way of passing all the necessary arguments on. We can package them in a struct like this:
struct sort_thread_args {
pthread_t tid;
void *array;
size_t arrLen;
size_t elem_size;
int (*comp_func)(const void *, const void *);
};
void *sort_thread(void *arg) { // the thread start routine
struct sort_thread_args *ta = arg;
bubble_sort(ta->array, ta->arrLen, ta->elem_size, ta->comp_func);
return NULL;
}
As can be seen above, the struct contains all the necessary information to call bubble_sort + the thread id, which I've stored there for convenience only. It's not needed by the thread itself.
The above functions are type agnostic so you can use these to sort any contiguous array, either by calling bubble_sort directly or in a thread.
Since you'll be using this to sort arrays of int, we need a comparison function for ints which does what was described above:
int comp_int(const void *va, const void *vb) {
const int *a = va;
const int *b = vb;
if (*a < *b) return -1;
if (*a > *b) return 1;
return 0;
}
Then for sorting half the array in one thread and the other half in another thread, I suggest that you don't copy the contents of mainArray to new arrays. Just leave the content in mainArray and tell each thread where to start sorting and how many elements it should sort.
Example:
#define Size(x) (sizeof(x) / sizeof *(x))
int main() {
srand((unsigned)time(NULL));
puts("Program Initialized...");
int arrLen = 35; // just an example to show that it handes uneven amounts too
int mainArray[arrLen];
// Populate mainArray
for (size_t i = 0; i < Size(mainArray); i++) {
mainArray[i] = rand() % 100;
}
puts("Array created...");
// print the created array
for (size_t i = 0; i < Size(mainArray); ++i) printf("%2d ", mainArray[i]);
putchar('\n');
puts("Running sorting threads...");
struct sort_thread_args ta[2]; // 2 for 2 threads
for (size_t i = 0; i < Size(ta); ++i) {
// element index for where this thread should start sorting:
size_t base = i * Size(mainArray) / Size(ta);
// prepare the arguments for this thread:
ta[i] = (struct sort_thread_args) {
.array = mainArray + base, // points at the first element for this thread
.arrLen = (i + 1) * Size(mainArray) / Size(ta) - base,
.elem_size = sizeof(int),
.comp_func = comp_int // the comparison function we made above
};
// and start the thread:
pthread_create(&ta[i].tid, NULL, sort_thread, &ta[i]);
}
puts("Joining threads");
for (size_t i = 0; i < Size(ta); ++i) {
pthread_join(ta[i].tid, NULL);
}
puts("Result:");
for (size_t i = 0; i < Size(mainArray); ++i) printf("%2d ", mainArray[i]);
putchar('\n');
}
Demo
Simply assign arg to your array variable in order to convert from void * to int *.
void *sort(void *arg){
int min_pos;
int *array = arg;
for(int a=0; a<arrLen-1; a++){
min_pos = a;
for(int b=a+1; b<arrLen; b++){
if(array[b] < array[min_pos]){
min_pos = b;
}
}
swap(&array[min_pos], &array[a]);
}
}
I wish to sort a second array as per the first array. e.g.
first = {1,8,7,2,4}
second = {9,7,2,10,3}
I want first to be unchanged and second to be sorted in the same relative order as the first. i.e. the lowest value is at index 0, the second lowest value is at index 3, third lowest value is at index 4 etc etc
second = {2,10,9,3,7}
I have already tried some code for the following
#include <stdio.h>
typedef struct
{
int num;
int pos;
}ArrType;
ArrType arrA[5] = {{1,0},{8,1},{7,2},{2,3},{4,4}};
ArrType arrB[5] = {{9,0},{7,1},{2,2},{10,3},{3,4}};;
int cmparr(const void *a, const void *b)
{
ArrType *tmpa, *tmpb;
tmpa = (ArrType*) a;
tmpb = (ArrType*) b;
return(arrA[tmpa->pos].num - arrA[tmpb->pos].num);
}
int main(void)
{
int i;
qsort(arrB,5, sizeof(ArrType), cmparr);
for (i=0; i<5; i++)
{
printf ("%d ",arrB[i].num);
}
return (0);
}
The actual output is
9 10 3 2 7
I am open to a different data structure, but arrB should only be sorted one time.
I have seen some solutions for this in C++, Javascipt and other languages. But there is not a solution in C.
Edit - These arrays would be quite large in the final program. I am looking for a single sorting operation. i.e. single call to qsort
You need to create the meta-data that matches the desired ordering (i.e an array of indexes). Then apply that meta-data to the second array.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int first[] = {1,8,7,2,4};
int second[] = {9,7,2,10,3};
int compare(const void * a, const void * b);
int binary_search(int array[], int min, int max, int target);
void print_array(int * array, int c);
int main()
{
int idx;
int c = sizeof(first)/sizeof(int);
int * sorted = NULL;
int * indexes = NULL;
int * result = NULL;
if (NULL == (sorted = malloc(sizeof(first)))) {
return -1;
}
memcpy(sorted, first, sizeof(first));
if (NULL == (indexes = malloc(sizeof(first)))) {
free(sorted);
return -1;
}
memset(indexes, -1, sizeof(first));
if (NULL == (result = malloc(sizeof(second)))) {
free(sorted);
free(indexes);
return -1;
}
memset(result, -1, sizeof(second));
// 1st: Sort the reference array
qsort (sorted, c, sizeof(int), compare);
// 2nd: Record the position of each sorted element in the original array (this is your meta-data)
for (idx=0; idx<c; idx++) {
indexes[idx] = binary_search(sorted, 0, c, first[idx]);
}
// 3rd sort the target array
memcpy(sorted, second, sizeof(second));
qsort (sorted, c, sizeof(int), compare);
// 4th apply the stored positions to the sorted target array
for (idx = 0; idx < c; idx++) {
result[idx] = sorted[indexes[idx]];
}
print_array(result, c);
free(result);
free(indexes);
free(sorted);
return 0;
}
int compare(const void * a, const void * b)
{
return ( *(int*)a - *(int*)b );
}
int binary_search(int array[], int min, int max, int target)
{
int mid;
while (min <= max)
{
mid = min + (max - min)/2;
if (target > array[mid])
min = mid + 1;
else if (target < array[mid])
max = mid - 1;
else
return mid;
}
return -1;
}
void print_array(int * array, int c)
{
for(int i = 0; i < c; i++) {
printf("%d ", array[i]);
}
printf("\n");
}
Demo
Here is my approach, it uses qsort twice and arrC contains the result.
#include <stdio.h>
typedef struct
{
int num;
int pos;
}ArrType;
ArrType arrA[5] = {{1,0},{8,1},{7,2},{2,3},{4,4}};
int arrB[5] = {9,7,2,10,3};;
int arrC[5];
int cmpInt(const void *a, const void *b)
{
return(*a - *b);
}
int cmp(const void *a, const void *b)
{
ArrType *tmpa, *tmpb;
tmpa = (ArrType*) a;
tmpb = (ArrType*) b;
return(tmpa->num - tmpb->num);
}
int main(void)
{
int i;
qsort(arrA,5, sizeof(ArrType), cmp);
qsort(arrB,5, sizeof(ArrType), cmpInt);
for (i=0; i<5; i++)
{
arrC[arrA[i].pos] = arrB[i];
}
return (0);
}
Since C doesn't have a lambda compare (which could be used to sort an array of indexes according to first[]), the code below sorts an array of pointers ap[] to the elements of first[] using qsort(). Using pointers eliminates the need to pass an array name as a parameter for the compare function, which in turn allows the compare function to work with qsort(). The expression (ap[i]-first) converts a pointer into an index. Next second[] is sorted, also using qsort(). Then ap[] is used as a set of ranks to reorder second[] in place and in O(n) time.
To explain reorder by rank versus reorder by index:
dst[rank[i]] = src[i]; /* reorder by rank */
dst[i] = src[index[i]]; /* reorder by index */
Example code:
#include <memory.h>
#include <stdio.h>
#include <stdlib.h>
/* compare for ptr to integer */
int cmppi(const void *p0, const void *p1){
return (*(int *)p0 - *(int *)p1);
}
/* compare for ptr to ptr to integer */
int cmpppi(const void *p0, const void *p1){
return (**(int **)p0 - **(int **)p1);
}
int main()
{
int first[] = {1, 8, 7, 2, 4};
int second[] = {9, 7, 2,10, 3};
int **ap; /* array of pointers */
int *tmpp;
int tmpi;
size_t i, j;
/* allocate and generate array of pointers to first[] */
ap = (int **)malloc(sizeof(first)/sizeof(first[0])*sizeof(int *));
for(i = 0; i < sizeof(first)/sizeof(first[0]); i++)
ap[i] = &first[i];
/* sort ap */
qsort(ap, sizeof(first)/sizeof(first[0]), sizeof(int *), cmpppi);
/* sort second */
qsort(second, sizeof(second)/sizeof(second[0]), sizeof(int), cmppi);
/* reorder ap and second in place using ap as rank (O(n) time) */
for (i = 0; i < sizeof(second) / sizeof(second[0]); i++){
while(i != (j = ap[i] - first)){
tmpp = ap[i]; /* swap(ap[i], ap[j]) */
ap[i] = ap[j];
ap[j] = tmpp;
tmpi = second[i]; /* swap(second[i], second[j] */
second[i] = second[j];
second[j] = tmpi;
}
}
/* display second[] */
for (i = 0; i < sizeof(second) / sizeof(second[0]); i++)
printf("%3d", second[i]);
printf("\n");
free(ap);
return 0;
}
Let's say I have to create an array of structs that is allocated on the heap and return a pointer that points to this array of structs.
typedef struct Pair {
int x;
int y;
} Pair;
Pair** foo(int n, int m, int length)
{
Pair* arr = malloc(sizeof(*arr) * length);
for (int i = 0; i < length; ++i) {
arr[i].x = n++;
arr[i].y = m++;
}
return &arr;
}
When I compile a program containing this function, it warns me that I am returning the address of a local variable. I assume this is because the pointer is initialised within the function (i.e. on the stack), therefore it counts as a local variable.
When I compile it, ignoring this warning, and run it anyway, the program crashes when the returned pointer is accessed.
I have tried allocating the pointer dynamically:
Pair** ptr = malloc(sizeof(**ptr));
ptr = &arr;
...
return ptr;
but the program still crashes when this pointer is accessed. How can I create this array within a function and return a pointer to this array so that it can be safely accessed?
This array is initialized on the stack but the pointer (arr) is a local variable, so the caller, main, cannot access it. You do not need to use the address of the pointer. You can access the array with the pointer itself.
Pair* foo(int n, int m, int length)
{
Pair* arr = malloc(sizeof(*arr) * length);
for (int i = 0; i < length; ++i) {
arr[i].x = n++;
arr[i].y = m++;
}
return arr;
}
If you want an array of structs, the code:
#include <stdio.h>
#include <stdlib.h>
typedef struct {
int x;
int y;
} Pair;
static Pair* foo(int n, int m, int length) {
Pair* arr = malloc(sizeof(*arr) * length);
for (int i = 0; i < length; ++i) {
arr[i].x = n++;
arr[i].y = m++;
}
return arr;
}
int main(void) {
Pair *z = foo(111, 222, 3);
for (int i = 0; i < 3; ++i)
printf("z[%d]= { %d, %d }\n", i, z[i].x, z[i].y);
free(z);
return 0;
}
gives the output:
z[0]= { 111, 222 }
z[1]= { 112, 223 }
z[2]= { 113, 224 }
If you want an pointer to an array of structs, you can change your function signature from Pair** to be Pair*.
If you still want an pointer to an array of pointers, then allocate memory for a Pair struct for each index of arr.
for(int i = 0; i < length; ++i){
arr[i] = malloc(sizeof(Pair));
...
}
Instead of returning &arr, you can declare arr as
Pair** arr = malloc(sizeof(Pair*) * length);
Because arr is a local variable, it will be free when foo end. So you don't have access for arr after. To solve this you should declare array pointer in heap:
Pair** foo(int n, int m, int length)
{
Pair ** arr = (Pair**)malloc(sizeof(Pair*));
*arr = malloc(sizeof(Pair) * length);
for (int i = 0; i < length; ++i) {
(*arr)[i].x = n++;
(*arr)[i].y = m++;
}
return arr;
}
This question already has answers here:
How to access a local variable from a different function using pointers?
(10 answers)
Closed 6 years ago.
I'm trying to create a function that concatenates 2 arrays and then returns the sum array back.
I've been using the following code:
#include "stdio.h";
struct array {
int length;
int *array;
};
struct array add(struct array a, struct array b) {
int length = a.length + b.length;
int sum[length];
for (int i = 0; i < length; ++i) {
if (i < a.length) {
sum[i] = a.array[i];
} else {
sum[i] = b.array[i - a.length];
}
}
struct array c;
c.length = length;
c.array = sum;
return c;
}
int main() {
int a[] = {1, 2, 3};
struct array s1;
s1.array = a;
s1.length = sizeof(a) / sizeof(a[0]);
int b[] = {4, 5, 6};
struct array s2;
s2.array = b;
s2.length = sizeof(b) / sizeof(b[0]);
struct array sum = add(s1, s2);
for (int i = 0; i < sum.length; ++i) {
printf("%d\n", sum.array[i]);
}
return 0;
}
The output is:
1,
17,
6356568,
1959414740,
1,
1959661600
What am I doing wrong?
These three lines are very problematic:
int sum[length];
...
c.array = sum;
return c;
In the first you declare the local variable sum. In the second you make c.array point to the local variable. And in the third line you return the pointer while the local variable goes out of scope.
Since the local variable goes out of scope it no longer exists, and the pointer to it is no longer valid. Using the pointer will lead to undefined behavior.
To solve this you need to allocate memory dynamically with e.g. malloc.
sum is a local variable to the add function. When you set c.array = sum;, then the pointer c.array points to this local variable.
After the function returns, local variables are destroyed. So this pointer is now a dangling pointer. But in main you then read through this pointer.
To fix this you'll need to make a fundamental change to the design of your program. For example, use dynamic allocation in all cases for a struct array.
Arrays in C simply are a contiguous area of memory, with a pointer to their start*. So merging them involves:
Find the length of the arrays A and B, (you will probably need to know the number of elements and the sizeof each element)
Allocating (malloc) a new array C that is the size of A + B.
Copy (memcpy) the memory from A to C,
Copy the memory from B to C + the length of A (see 1).
You might want also to de-allocate (free) the memory of A and B.
Example code snippet:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define ARRAY_CONCAT(TYPE, A, An, B, Bn) \
(TYPE *)array_concat((const void *)(A), (An), (const void *)(B), (Bn), sizeof(TYPE));
void *array_concat(const void *a, size_t an,const void *b, size_t bn, size_t s)
{
char *p = malloc(s * (an + bn));
memcpy(p, a, an*s);
memcpy(p + an*s, b, bn*s);
return p;
}
// testing
const int a[] = { 1, 1, 1, 1 };
const int b[] = { 2, 2, 2, 2 };
int main(void)
{
unsigned int i;
int *total = ARRAY_CONCAT(int, a, 4, b, 4);
for(i = 0; i < 8; i++)
printf("%d\n", total[i]);
free(total);
return EXIT_SUCCCESS;
}
Try this - corrected add function:
#include <stdlib.h>
struct array add(struct array a, struct array b) {
int length = a.length + b.length;
int * sum = (int*)calloc(length, sizeof(int));
for (int i = 0; i < length; ++i) {
if (i < a.length) {
sum[i] = a.array[i];
}
else {
sum[i] = b.array[i - a.length];
}
}
struct array c;
c.length = length;
c.array = sum;
return c;
}
stdlib is required to use calloc function.
That function allocate memory for length values of int type. To be sure that memory is allocated successfully, it is recommended to check value of pointer sum after allocation, e.g.:
int * sum = (int*)calloc(length, sizeof(int));
if( sum != NULL )
{
// use memory and return result
}
else
{
// do not use pointer (report about error and stop operation)
}
As Joachim mentioned, you are returning a local variable int sum[length]; This is a bad idea. The variable sum is returned to the stack after the function exits and can be overwritten by other stack variables.
One of the ways around that is to not declare an array inside the sum function in the first place. The sum_str is declared in main. You can pass the pointer to this structure to the sum function.
The updated code is below.
#include <stdio.h>
struct array {
int length;
int *array;
};
void add(struct array a, struct array b, struct array *sum_str) {
sum_str->length = a.length + b.length;
for (int i = 0; i < sum_str->length; ++i) {
if (i < a.length) {
sum_str->array[i] = a.array[i];
} else {
sum_str->array[i] = b.array[i - a.length];
}
}
}
int main() {
int a[] = {1, 2, 3};
struct array s1;
s1.array = a;
s1.length = sizeof(a) / sizeof(a[0]);
int b[] = {4, 5, 6};
struct array s2;
s2.array = b;
s2.length = sizeof(b) / sizeof(b[0]);
struct array sum_str;
int sum_a[6];
sum_str.array = sum_a;
add(s1, s2, &sum_str);
for (int i = 0; i < sum_str.length; ++i) {
printf("%d\n", sum_str.array[i]);
}
return 0;
}
Another way is to use dynamic memory allocation as described by other answers.
I have a struct:
struct points{
int i;
int x;
int y;
};
And I made an array of the struct and put elements in it. The i element indicates the label of a certain point. Suppose I have 1 2 3 as an input in the array. 1 corresponds to the label of the point (2, 3). Then I tried to sort the x elements:
for (a=0; a < i; a++){
for (b = 0; b < i; b++){
if (pt[b].x > pt[b+1].x){
temp1 = pt[b].x;
pt[b].x = pt[b+1].x;
pt[b+1].x = temp1;
}
}
}
It was sorted correctly. Now when I printed the i (label), it did not correspond with the x element when sorted. In short, only the x element moved. I want to make the i and y move with the x as it is sorted. What should I do?
Instead of just swapping x you need to swap all the data so that the entire array of structures gets sorted.
You could do this with a separate function, for clarity:
void swap_points(struct points *pa, struct points *pb)
{
const struct points tmp = *pa;
*pa = *pb;
*pb = tmp;
}
Then call that instead of the three inner-most lines of code in your sort.
You really should just use qsort() to do this, it's much simpler:
static int compare_points(const void *va, const void *vb)
{
const struct points *pa = va, *pb = vb;
return pa->i < pb->i ? -1 : pa->i > pb->i;
}
qsort(pt, i, sizeof pt[0], compare_points);
You are actually sorting the array, but only the values for i, not the whole structure!
You'll want to use C's qsort here:
#include <stdlib.h>
#include <stdio.h>
struct points
{
int i;
int x;
int y;
};
int compare(const struct points *a, const struct points *b)
{
if (a->i < b->i) return -1;
if (a->i == b->i) return 0;
if (a->i > b->i) return 1;
}
int main(void)
{
int i;
struct points p[3] = { { 4, 2, 1 }, { 1, 3, 5 }, { 2, 8, 1 } };
qsort(p, 3, sizeof(struct points),
(int (*)(const void*, const void*)) compare);
printf("{ ");
for (i=0; i<3; ++i)
{
printf("{ %d, %d, %d }", p[i].i, p[i].x, p[i].y);
if (i < 2) printf(", ");
}
printf(" }\n");
}
See http://www.cplusplus.com/reference/cstdlib/qsort/
You have to copy the other elements in the struct too.
I suppose you to write a function which replace the values of an element, like this:
void copyPoints(point1* a, point2* b)
{
int temp = a->i;
a->i = b->i;
b->i = temp;
temp = a->x;
a->x = b->x;
b->x = temp;
temp = a->y;
a->y = b->y;
b->y = temp;
}
Then modify the code like this:
for (a=0; a < i; a++)
{
for (b = 0; b < i; b++)
{
if (pt[b].x > pt[b+1].x)
copyPoints(&(pt[b]),&(pt[b+1]));
}
}