I am loading some data into struct.
struct test {
char x[101];
char y[101];
int z;
};
Then I create memory for structs
struct test * data;
data = malloc(10 * sizeof(struct test));
And fill data like this
data[0].z = 123;
This works alright. But I wanna sort these structs. Like sort them depending on z attribute.
My idea was to create a new array and fill it with pointers which will point to the right struct.
Can somebody tell me how or if there is better way?
Thanks
This depends on your goal. If you are trying to figure out what would be most programmer efficient, then creating an ordering functor based on the z attribute of your struct, and sorting the list directly, is easiest.
However, if you are concerned with program efficiency, then it will be faster to sort pointers, as suggested by Fiddling Bits. However, you must keep in mind that this will give you a sorted list of pointers- your data array will still be in the same order.
For instance, if datapoints is your array of pointers, and you wanted to sort by the z value, you could define the comparison
int compare(const void *a, const void *b)
{
return ( (*(test *)a).z -(*(test *)b).z );
}
And then call
qsort( datapoints, 10, sizeof(test), compare);
Full documentation for qsort can be found here:
One way to do this is using qsort, the general sorting algorithm provided in the standard library for sorting sequences of user-defined data. With a goal of doing the following:
the source data must remain un-touched.
the "sorted" access must be provided via a list of pointers to the untouched data from above
this is one way to do that using the qsort() standard library function:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
struct test {
char x[101];
char y[101];
int z;
};
int cmp_test_ptrs(const void* arg1, const void* arg2)
{
struct test const * lhs = *(struct test const * const*)arg1;
struct test const * rhs = *(struct test const * const*)arg2;
return (lhs->z < rhs->z) ? -1 : (rhs->z < lhs->z);
}
int main()
{
srand((unsigned int)time(0));
// populate data array.
struct test * data = malloc(10 * sizeof(*data));
for (int i=0; i<10; ++i)
{
data[i].z = rand() % 20;
printf("%d ", data[i].z);
}
printf("\n");
// allocate a pointer array to use as a sort-bed, copying each
// structure address into this pointer array afterward
struct test **ptrs = malloc(10 * sizeof(*ptrs));
for (int i=0; i<10; ++i)
ptrs[i] = data+i;
// sort the pointer bed using our comparator
qsort(ptrs, 10, sizeof(*ptrs), cmp_test_ptrs);
// ptrs now has sorted pointers. Note the dereference for access
// to the actual data, which remains where it originally was.
for (int i=0; i<10; ++i)
printf("%d ", ptrs[i]->z);
printf("\n");
free(ptrs);
free(data);
return 0;
}
Sample Output
11 7 6 17 8 8 11 4 7 5
4 5 6 7 7 8 8 11 11 17
Important to note is the original structure array remained untouched. We sorted a sequence of pointers, not the actual structures themselves, and used what those pointers pointed-to as the criteria for the sort.
Anyway, I hope it helps. Study the comparator and the setup for qsort() carefully. They're important. You can read more about qsort() here.
Or you can just sort them in the same array and probably save some memory (4 bytes for a pointer of any type) :P
Doesn't really matter though, pointers'll work fine I guess.
But I think that in some cases pointers will be more useful as if you would sort it in the array you'll need a temporary struct defined to contain structs when you're sorting and if you're struct is big than you'll actually take a lot more memory than 4 bytes.
+Also remember that when defining a pointer to a struct - access it with -> operator.
Usage: STRUCT_POINTER_NAME->VARIABLE
Related
I want to use a struct to contain some data and passing them between different functions in my program,this struct has to contain a dynamic 2D array (i need a matrix) the dimensions change depending on program arguments.
So this is my struct :
struct mystruct {
int **my2darray;
}
I have a function that read numbers from a file and has to assign each of them to a cell of the struct array.
I tried doing this :
FILE *fp = fopen(filename, "r");
int rows;
int columns;
struct mystruct *result = malloc(sizeof(struct mystruct));
result->my2darray = malloc(sizeof(int)*rows);
int tmp[rows][columns];
for(int i = 0;i<rows;i++) {
for(int j = 0;j<columns;j++) {
fscanf(fp, "%d", &tmp[i][j]);
}
result->my2darray[i]=malloc(sizeof(int)*columns);
memcpy(result->my2darray[i],tmp[i],sizeof(tmp[i]));
}
But this is giving me a strange result : all the rows are correctly stored except for the first.
(I'm sure that the problem is not in the scanning of file).
While if i change the fourth line of code in this :
result->my2darray = malloc(sizeof(int)*(rows+1));
it works fine.
Now my question is why this happens?
Here's an answer using some "new" features of the language: flexible array members and pointers to VLA.
First of all, please check Correctly allocating multi-dimensional arrays. You'll want a 2D array, not some look-up table.
To allocate such a true 2D array, you can utilize flexible array members:
typedef struct
{
size_t x;
size_t y;
int flex[];
} array2d_t;
It will be allocated as a true array, although "mangled" into a single dimension:
size_t x = 2;
size_t y = 3;
array2d_t* arr2d = malloc( sizeof *arr2d + sizeof(int[x][y]) );
Because the problem with flexible array members is that they can neither be VLA nor 2-dimensional. And although casting it to another integer array type is safe (in regards of aliasing and alignment), the syntax is quite evil:
int(*ptr)[y] = (int(*)[y]) arr2d->flex; // bleh!
It would be possible hide all this evil syntax behind a macro:
#define get_array(arr2d) \
_Generic( (arr2d), \
array2d_t*: (int(*)[(arr2d)->y])(arr2d)->flex )
Read as: if arr2d is a of type array2d_t* then access that pointer to get the flex member, then cast it to an array pointer of appropriate type.
Full example:
#include <stdlib.h>
#include <stdio.h>
typedef struct
{
size_t x;
size_t y;
int flex[];
} array2d_t;
#define get_array(arr2d) \
_Generic( (arr2d), \
array2d_t*: (int(*)[(arr2d)->y])(arr2d)->flex )
int main (void)
{
size_t x = 2;
size_t y = 3;
array2d_t* arr = malloc( sizeof *arr + sizeof(int[x][y]) );
arr->x = x;
arr->y = y;
for(size_t i=0; i<arr->x; i++)
{
for(size_t j=0; j<arr->y; j++)
{
get_array(arr)[i][j] = i+j;
printf("%d ", get_array(arr)[i][j]);
}
printf("\n");
}
free(arr);
return 0;
}
Advantages over pointer-to-pointer:
An actual 2D array that can be allocated/freed with a single function call, and can be passed to functions like memcpy.
For example if you have two array2d_t* pointing at allocated memory, you can copy all the contents with a single memcpy call, without needing to access individual members.
No extra clutter in the struct, just the array.
No cache misses upon array access due to the memory being segmented all over the heap.
The code above never sets rows and columns, so the code has undefined behavior from reading those values.
Assuming you set those values properly, this isn't allocating the proper amount of memory:
result->my2darray = malloc(sizeof(int)*rows);
You're actually allocating space for an array of int instead of an array of int *. If the latter is larger (and it most likely is) then you haven't allocated enough space for the array and you again invoke undefined behavior by writing past the end of allocated memory.
You can allocate the proper amount of space like this:
result->my2darray = malloc(sizeof(int *)*rows);
Or even better, as this doesn't depend on the actual type:
result->my2darray = malloc(sizeof(*result->my2darray)*rows);
Also, there's no need to create a temporary array to read values into. Just read them directly into my2darray:
for(int i = 0;i<rows;i++) {
result->my2darray[i]=malloc(sizeof(int)*columns);
for(int j = 0;j<columns;j++) {
fscanf(fp, "%d", &result->my2darray[i][j]);
}
}
In your provided code example, the variables rows and columns have not been initialized before use, so they can contain anything, but are likely to be equal to 0. Either way, as written, the results will always be unpredictable.
When a 2D array is needed in C, it is useful to encapsulate the memory allocation, and freeing of memory into functions to simplify the task, and improve readability. For example, in your code the following line will create an array of 5 pointers, each pointing to 20 int storage locations: (creating 100 index addressable int locations.)
int main(void)
{
struct mystruct result = {0};
result.my2darray = Create2D(5, 20);
if(result.my2darray)
{
// use result.my2darray
result.my2darray[0][3] = 20;// for simple example, but more likely in a read loop
// then free result.my2darray
free2D(result.my2darray, 5);
}
return 0;
}
Using the following two functions:
int ** Create2D(int c, int r)
{
int **arr;
int y;
arr = calloc(c, sizeof(int *)); //create c pointers (columns)
for(y=0;y<c;y++)
{
arr[y] = calloc(r, sizeof(int)); //create r int locations for each pointer (rows)
}
return arr;
}
void free2D(int **arr, int c)
{
int i;
if(!arr) return;
for(i=0;i<c;i++)
{
if(arr[i])
{
free(arr[i]);
arr[i] = NULL;
}
}
free(arr);
arr = NULL;
}
Keep in mind that what you have created using this technique is actually 5 different pointer locations each pointing to a set of 20 int locations. This is what facilitates the use of array like indexing, i.e. we can say result.my2darray[1][3] represents the second column, forth row element of a 5X20 array, when it is not really an array at all.
int some_array[5][20] = {0};//init all elements to zero
Is what is commonly referred to in C an int array, also allowing access to each element via indexing. In actuality (Even though commonly referred to as an array.) it is not an array. The location of elements in this variable are stored in one contiguous location in memory.
|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0... (~ 82 more)
But C maintains the locations such that they are all indexable as an 2D array.
This question already has answers here:
Dynamic array in C — Is my understanding of malloc and realloc correct?
(3 answers)
Closed 5 years ago.
So for my school project, a large CSV file will be entered through stdin and we will have to sort it based on column and print it out as a sorted csv file.
The step I am on right now is figuring out how to keep reallocing a struct of arrays so that it will grow if there is not big enough to hold the data coming in from stdin. We don't know the exact amount of rows that will be inputted in the CSV file. Right now we just used a static amount to test and see if the values are assigned to the structs.
I am still a beginner at C so I do not clearly know how I would iterate through a pointer like I would iterate through an array. Since we are using a static amount of structs in the array, we can just iterate using array[i] like in Java but how would you iterate through something like *array?
I do not know where to start for creating this dynamic array. I tried
struct array* testArray = (array*)malloc(sizeof(testArray));
but I have no idea how to iterate through it like I did with the static array by using array[i].
Any help would be greatly appreciated, sorry for the wall of text...
You can navigate through a malloced space the same way as with an array (using indicies), but it seems that your main issue lies in your use of malloc. Malloc's argument is the size in number of bytes that you want to allocate. So if you want to have an array of structs, you would first need to find out how many bytes one struct contains using sizeof(struct array), and then determine how large of an array you want, let's say N. So that line of code should look more like struct array* testArray = malloc(N * sizeof(struct array));. The return value of malloc will be a void pointer containing the memory address of the first byte of allocated space. Upon assigning this value to testArray, it will be type-casted to the assigned variable type (struct array *). Now you can use pointer arithmetic to access a specific index i with *(testArray + i), or simply testArray[i]. If you find that N was not a sufficient size, you can use realloc to increase the array size to 2N, or whatever size deemed necessary.
struct array* testArray = (array*)malloc(sizeof(testArray));
is a little wrong as you only allocate 1 element of testArray.
It is more like:
struct A
{
int a;
int b;
....
};
struct A* arr = malloc( N * sizeof(struct A) );
^^^
N element of struct A
int j;
for (j=0; j<N; ++j) // Iterate it like any other array
{
arr[j].a = 5;
arr[j].b = 42;
....
}
Use realloc when you need the array to grow.
When reading from a file/stdin it could look like (based on comment from David C. Rankin):
int n=0; // Count of the number of structs read from the file
struct A* arr = malloc( N * sizeof(struct A) );
while (read line from file)
{
arr[n].a = val1;
arr[n].b = val2;
++n; // Increment count
if (n == N) // Check if current array is full, i.e. realloc needed
{
// realloc array to 2 * N; N = N * 2
}
}
I need to write a function that sums monoms with the same power,
the monoms are defined by the following struct:
typedef struct monom {
int coefficient;
int power;
}MONOM;
And the function I wrote from the job is:
int sumMonomsWithSamePower(MONOM** polynomial, int size)
{
int i, powerIndex = 0;
for (i = 0; i < size; i++)
{
if ((polynomial[powerIndex])->power == (polynomial[i])->power)
{
if (powerIndex != i)
(polynomial[powerIndex])->coefficient += (polynomial[i])->coefficient;
}
else
powerIndex++;
}
powerIndex++;
*polynomial = (MONOM*)realloc(polynomial, powerIndex);
return powerIndex;
}
Which is being called with the following call:
*polySize = sumMonomsWithSamePower(&polynomial, logSize);
polynomial array is being sent to the function as a sorted array of MONOMs (sorted ascending by powers).
My problem is that on the 7th line of sumMonomsWithSamePower() the function crashes since it can't see the elements in the array by the following way. When I put the elements of the array in Watch list in my debugger I also can't see them using polynomial[i], but if I use (polynomial[0]+i) I can see them clearly.
What is going on here?
I assume outside sumMonomsWithSamePower() you have allocated polynomial with something like polynomial = malloc( size * sizeof(MONOM) ); (everything else wouldn't be consistant to your realloc()). So you have an array of MONOMs and the memory location of polynomial[1] is polynomial[0] + sizeof(MONOM) bytes.
But now look at polynomial in sumMonomsWithSamePower() In the following paragraph I will rename it with ppoly (pointer to polynomial) to avoid confusing it with the original array: here it is a MONOM **, so ppoly[1] addresses the sizeof(MONOM *) bytes at the memory location ppoly[0] + sizeof(MONOM *) and interpretes them as pointer to a MONOM structure. But you have an array of structs, not an array of pointers. Replace your expressions by (*ppoly)[i].power (and all the others accordingly of course) and that part will work. By the way that's excactly the difference of the two debugger statements you have mentioned.
Besides, look at my comments concerning the use of powerIndex
I have an array of structs. Actually, it is a 2d-array but an unusual 2d array.
I am allocating memory on stack:
#define MAX_VERTICES 5068
struct ARRAY_FIX {
int ele[MAX_VERTICES];
int size;
int first;
};
ARRAY_FIX C[MAX_VERTICES];
int main() {
//...
}
So, I need to replace one row with another one (actually, i need this operation to be performed for sorting rows by some criteria).
How is it possible to perform? As I understand, if I use this code:
С[i] = C[j];
In this code, the operator "=" will copy all array, won't it? I needn't it, I want to change the rows by changing the pointer
How can I do it?
You can use an array of pointers to struct ARRAY_FIX and just switch the pointers into the array.
I am allocating memory on stack.
An object declared at file scope is usually NOT on the stack.
In your case, each row is represented by struct ARRAY_FIX object. If you want to be able to work with these rows by using references (changing the order of rows by swapping pointers etc.), your 2D array must be stored in a way that allows you to do that.
Possible solution is to change your 2D array to an array of pointers to struct ARRAY_FIX so that when you call С[i] = C[j]; only the reference (address of your object) is copied, not an object itself.
Also note, that you should worry about the performance and try to make your program faster only when it's really needed. It's much easier to make a correct program fast than it's to make a fast program correct.
as said before
Possible solution is to change your 2D array to an array of pointers
to struct ARRAY_FIX
here after how to do it:
#define MAX_VERTICES 5068
struct ARRAY_FIX {
int ele[MAX_VERTICES];
int size;
int first;
};
ARRAY_FIX *C[MAX_VERTICES];
int main() {
int i;
ARRAY_FIX *p;
//...
for (i=0;i<MAX_VERTICES;++i)
{
C[i] = malloc (sizeof(ARRAY_FIX ));
//...
}
//...
p = C[1];
C[1] = C[2];
C[2] = p;
//...
}
For an assignment at school, we have to use structs to make matrices that can store a infinite amount of points for an infinite amount of matrices. (theoretical infinite)
For the assignment I decided to use calloc and realloc. How the sizes for the matrix go is: It doubles in size every time its limit is hit for its points (so it starts at 1, then goes to 2, then 4 and so on). It also doubles in size every time a matrix is added as well.
This is where my issue lies. After the initial matrix is added, and it goes to add the second matrix name and points, it gives me the following:
B???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
B is the portion of it that I want (as I use strcmp later on), but the ? marks are not supposed to be there. (obviously)
I am not sure why it is exactly doing this. Since the code is modular it isn't very easy to get portions of it to show exactly how it is going about this.
Note: I can access the points of the matrix via its method of: MyMatrix[1].points[0].x_cord; (this is just an example)
Sample code that produces problem:
STRUCTS:
struct matrice {
char M_name[256];
int num_points[128];
int set_points[128];
int hasValues[1];
struct matrice_points * points;
} * MyMatrix;
struct matrice_points {
int set[1];
double cord_x;
double cord_y;
};
Setup Matrix Function:
void setupMatrix(){
MyMatrix = calloc(1, sizeof(*MyMatrix));
numMatrix = 1;
}
Grow Matrix Function:
void growMatrix(){
MyMatrix = realloc(MyMatrix, numMatrix * 2 * sizeof(*MyMatrix));
numMatrix = numMatrix * 2;
}
Add Matrix Function which outputs this problem after growing the matrix once.
void addMatrix(char Name, int Location){
int exists = 0;
int existsLocation = 0;
for (int i = 0; i < numMatrix; i++){
if (strcmp(MyMatrix[i].M_name, &Name) == 0){
exists = 1;
existsLocation = i;
}
}
*MyMatrix[Location].M_name = Name;
printf("Stored Name: %s\n", MyMatrix[Location].M_name);
*MyMatrix[Location].num_points = 1;
*MyMatrix[Location].set_points = 0;
*MyMatrix[Location].hasValues = 1;
MyMatrix[Location].points = calloc(1, sizeof(*MyMatrix[Location].points));
}
void addMatrix(char Name, int Location)
char Name represents a single char, i.e. a integer-type quantity. char is just a number, it's not a string at all.
When you do this:
strcmp(..., &Name)
you're assuming that the location where that one character is stored represents a valid C string. This is wrong, there is no reason why this should be the case. If you want to pass a C string to this function, you will need to declare it like this:
void addMatrix(char *Name, int Location)
Then you need to copy that C string into the appropriate place in your matrix structure. It should look like:
strncpy(... .M_name, Name, max_number_of_chars_you_can_store_in_M_Name);
Also these field definitions are strange in your struct:
int num_points[128];
int set_points[128];
int hasValues[1];
This means that your struct will contain an array of 128 ints called num_points, another array of 128 ints calls set_points, and an array of one int (strange) called hasValues. If you only need to store the count of total points and set points, and a flag indicating whether values are stored, the definition should be:
int num_points;
int set_points;
int hasValues;
and correct the assignments in your addMatrix function.
If you do need those arrays, then your assignments as they are are wrong also.
Please turn on all warnings in your compiler.
Try adding '\0' to the end of your data.
*MyMatrix[Location].M_name = Name;
You're copying a single character here, not a string. If you want a string, Name should be defined as char *, and you should be using strcpy.