This is part of an assignment.
I currently at the part where I have to read in a .csv file into following struct:
typedef struct {
int n;
double **data;
} Matrix;
The .csv file has for example 3 rows and 3 columns:
1 2 3
4 5 6
7 8 9
and would look like this as a 2d array:
double array[3][3] = {{1,2,3},{4,5,6},{7,8,9}};
and now I would want to assign my array to the Matrix like this:
function someFunction(Matrix *m) {
//...
double array[3][3] = {{1,2,3},{4,5,6},{7,8,9}};
A->data = array;
A->n = 3;
//...
}
But assigning array to data is not working as I get following error using gcc -Wall -pedantic-errors:
error: assignment to ‘double **’ from incompatible pointer type ‘double *’ [-Wincompatible-pointer-types]
79 | A->data = array;
How can I resolve this problem? Part of the assignment is that gcc is not allowed to show any warnings when using -Wall and -pedantic-erros, so hacks aren't allowed.
double** is a pointer to pointer. Of course the (outer) pointer could point to some array, too:
double* array[7];
double** pointer = array;
Now each of the pointers in array could point to other arrays as well:
double valueArray[12]
double* pointerArray[10];
double** pointer = array;
pointer[0] = valueArray; // equivalent to pointerArray[0] = ...
Two-dimenstional arrays are not arrays of pointers, though, they are arrays of arrays, and pointer to first element is of different type:
double array[10][12];
double(*pointer)[12] = array;
Trying to cover arbitrary size matrices produces some trouble, though:
struct Matrix
{
size_t n;
double(*data)[n]; // you cannot exchange arbitrary pointers,
// you need a compile time constant!
};
Be aware that this is different from VLA in function parameters, where you could have
void f(size_t n, int(*data)[n]);
Then it looks as if you get pretty much into trouble already with the bit of code you presented:
void someFunction(Matrix* m)
{
double array[3][3] = {{1,2,3},{4,5,6},{7,8,9}};
m->data = array; // assuming you meant m instead of A
// and you did adjust the pointer type already appropriately
}
array has local storage duration and will be destroyed as soon as you return from the function, resulting in a dangling pointer in m and in undefined behaviour, if you dereference that pointer after returning from function.
So you would need to malloc an array of sufficient size instead. But don't forget to free it again then, too!
So if you don't want to go the pointer to pointer way:
double** array = malloc(sizeof(*array) * n);
array[0] = malloc(sizeof(**array) * n);
// ...
which would allow for the m->data[x][y] syntax, then you should stick to the one-dimensional array approach:
struct Matrix
{
size_t n;
double* data;
};
and maybe some accessor function:
double get(struct Matrix* m, size_t row, size_t column)
{
return m->data[row * m->n + column];
}
which would get around the double pointer indirection and thus be faster (actually, that's exactly the same calculation that occurs under the hoods with a true two-dimensional array, i. e. not the pointer to pointer variant).
Related
I was searching for an efficient way to create a 2D array and return it, usually I make an array of pointers, allocate memory for each pointer, and return an **ptr. I was looking for another way to do it because then I have to free every single memory allocated.
I was reading online that you can allocate a 2D array like this: int (*arr)[n] = malloc( sizeof *arr * i );
In which ptr is a pointer, pointing to the adress of arr[n].
When I try to return arr, from the following function: int *array_of_smallest(int count);
I get: warning: assignment to ‘int *’ from incompatible pointer type ‘int (*)[n]’ [-Wincompatible-pointer-types]
If I initialise another pointer and point it to array, then return it, I'm only returning a 1D array.
I think I'm mixing diferent concepts and confusing myself.
With an array of pointers I don't have a problem, but I wanted a simplier way to create a 2D array, also easier to free, but I'm not being able to return that array from my function.
You declared a pointer of the variable modified type type int ( * )[n].
int (*arr)[n] = malloc( sizeof *arr * i );
That is the variable n used in the declarator is not an integer constant expression.
In this case the function should have the return type void *. And it can be declared like
void * array_of_smallest(int count)
{
int (*arr)[n] = malloc( sizeof *arr * i );
//...
return arr;
}
In the caller you will write
int ( *arr )[n] = array_of_smallest( count );
In this declaration the value of n must be the same as used within the function.
If to use an integer constant expression like
int (*arr)[2] = malloc( sizeof *arr * 2 );
then the function declaration will look like
int ( * array_of_smallest(int count) )[2];
Or you can introduce a typedef name before the function declaration like
typedef int Array[2];
and in this case the function will look like
Array * array_of_smallest(int count);
The fundamental issue with returning the array is that it is not possible to declare Variably-Modified Type (like a pointer to VLA) at the file scope.
The reason of the problem can be explained as follow. Your code behaves more or less like this:
typedef int T[n];
T* array_of_smallest(int count) { ... }
Unfortunately, defining such a type T is not possible because of two reasons:
n is usually not visible at file scope, except if it was a global variable
evaluation of n would require code execution at file scope which is forbidden
Workarounds
Return void* as described in other answer.
Pass a pointer to VLA in "by-reference" style, as a pointer to a pointer to array
void array_of_smallest(int count, int (**p)[n]) {
...
*p = arr;
}
Return a pointer to incomplete array type of int[]. It is not possible to return incomplete types but it is possible to return a pointer to incomplete types:
int (*array_of_smallest(int count))[] { ... }
or a bit cleaner with a help of typeof extension (feature in C23)
typeof(int[]) *array_of_smallest(int count) { ... }
And use it as in the void* case:
int ( *arr )[n] = array_of_smallest( count );
This solution provides type-checking for the element type, however it works only for 2-dimensional arrays.
Wrap the pointer into a structure and use a macro to reconstruct its type on the client side:
typedef struct {
int rows, cols;
void* data;
} Arr2D;
Arr2D array_of_smallest(int count) {
...
return (Arr2D) { .rows = i, .cols = n, .data = arr };
}
#define ARR2D_DEF_VIEW(view, arr2d) int (*view)[(arr2d).cols] = (arr2d).data
... usage ...
Arr2D arr = array_of_smallest(42);
ARR2D_DEF_VIEW(view, arr);
... do stuff with view[i][j]
}
Alternatively, use typeof extension to infer the type of VLA view rather than declare it in a macro:
#define ARR2D_VIEW_TYPE(arr2d) typeof(int(*)[(arr2d).cols])
...
Arr2D arr = array_of_smallest(42);
ARR2D_VIEW_TYPE(arr) view = arr.data;
I'd like to store the pointers of two arrays (*a and *b) created by malloc in a function.
For example, if &a[0]=0x0061FEC8 and &b[0]=0x007700FE, then the array to store these two pointers should be c[0]=0x0061FEC8 and c[1]=0x007700FE
void storePointers(int **ptrArray){
// create two arrays a and b by malloc
int *a = (int *)malloc(5*sizeof(int)); // a stores 5 integers
int *b = (int *)malloc(10*sizeof(int)); // b stores 10 integers
// create an array to store the pointers of a and b
*ptrArray = (int **)malloc(2*sizeof(int*));
(*ptrArray)[0] = a;
(*ptrArray)[1] = b;
}
int main(){
int *mArray = NULL;
storePointers(&mArray);
// these two lines should print 0x0061FEC8 and 0x007700FE
printf("mArray[0]: %p\n", mArray[0]);
printf("mArray[1]: %p\n", mArray[1]);
return 0;
}
This program actually worked. But the compiler displayed a warning message:
warning: assignment to 'int' from 'int *' makes integer from pointer without a cast [-Wint-conversion]
(*ptrArray)[0] = a;
warning: assignment to 'int' from 'int *' makes integer from pointer without a cast [-Wint-conversion]
(*ptrArray)[1] = b;
I assume int is common so the compiler fixed the problem by itself so that my program ran properly? I have another similar program, but it uses struct. So instead of a warning, I get an error of
Error: incompatible types when assigning to type 'myStruct' from type 'myStruct *'
I would like to know the root cause and solution to get rid of the warning and ultimately the error in my struct program.
If an array is int * then an array of arrays is int ** and if you want to return an array of arrays as an out parameter, then you need a pointer to that -- int ***. So you need to change the type of mArray as well as the ptrArray parameter:
void storePointers(int ***ptrArray){
// create two arrays a and b by malloc
int *a = (int *)malloc(5*sizeof(int)); // a stores 5 integers
int *b = (int *)malloc(10*sizeof(int)); // b stores 10 integers
// create an array to store the pointers of a and b
*ptrArray = (int **)malloc(2*sizeof(int*));
(*ptrArray)[0] = a;
(*ptrArray)[1] = b;
}
int main(){
int **mArray = NULL;
storePointers(&mArray);
// these two lines should print 0x0061FEC8 and 0x007700FE
printf("mArray[0]: %p\n", mArray[0]);
printf("mArray[1]: %p\n", mArray[1]);
return 0;
}
That should then work if you change the type from int to something else.
I am passing a chunk of memory to my subroutine. In the subroutine I am assigning parts of this memory to internal 1D array pointers.
typedef struct
{
// float a[SIZE_A];
// float b[SIZE_B];
// float c[SIZE_C1][SIZE_C2];
float* a;
float* b;
float c[][SIZE_C2]; // float** c;
} OBJ;
void init( OBJ* obj, void* mem )
{
float* mem_p = (float*)mem;
obj->a = mem_p;
mem_p += SIZE_A;
obj->b = mem_p;
mem_p += SIZE_B;
obj->c = ?
}
How would I assign a 3rd member which is 2D array?
The declaration float c[][SIZE_C2] in your struct declares an array of arrays which are stored inline in your struct. This declaration does not hold any pointers which refer to an array stored elsewhere.
If you wish to keep the struct signature you have currently, you can populate c as follows:
memcpy(&obj->c, mem_p, SIZE_C1 * SIZE_C2 * sizeof(float));
This copies the data from mem_p into the contiguous array of arrays c, after which c does not reference any data from mem as is the case with a and b, but instead holds its own copy of the data.
Keep in mind that your struct currently holds c as a "flexible array member", as the outermost array has an unspecified size, which means your struct must be dynamically allocated via malloc or similar and must be given extra space to hold the array elements at the end:
OBJ *obj = malloc(sizeof *obj + SIZE_C1 * sizeof(float));
If you intend in only storing pointers in obj which refer to memory referenced by mem without storing separate copies, so that c is consistent with a and b, you will have to change the declaration of c in your struct to the following:
float (*c)[SIZE_C2];
This declares c to be a "pointer to an array of SIZE_C2 elements" (which may be in a contiguous sequence of arrays), rather than an "array of arrays of SIZE_C2 elements".
This can be populated within your init function as follows:
obj->c = (float (*)[SIZE_C2]) mem_p;
If you are looking to assign further float fields after c, you can continue incrementing mem_p like so, without casting it to a pointer-to-array, as the size of an array in C is just the size of its element type multiplied by the number of its elements:
/* sizeof(float [SIZE_C2]) == SIZE_C2 * sizeof(float) */
/* mem_p is a `float *` so goes up in steps of `sizeof(float)` */
mem_p += SIZE_C1 * SIZE_C2;
With this approach, elements can still be accessed as obj->c[1][2], just as with your current struct's signature.
Since pointers to arrays are slightly tricky to use in C, and require your inner array to have a fixed size, you may find it more appropriate to store c as just a regular single float * pointer and address elements manually yourself as though it were a 2-dimensional array, e.g.:
float f = obj->c[row * SIZE_C2 + col];
I've been thinking about this and maybe you overcomplicated the whole thing. As I understand this you started off with statically allocated struct:
typedef struct
{
float a[SIZE_A];
float b[SIZE_B];
float c[SIZE_C1][SIZE_C2];
} OBJ;
and now you want the memory to be dynamically allocated. So you allocate a chunk of memory of size sizeof(float) * (SIZE_A + SIZE_B + SIZE_C1 * SIZE_C2) and set pointers in the struct to that memory.
But notice how that chunk of memory is exactly the size of your original struct? So why not allocate the original struct dynamically instead of changing the members of the struct to pointers?
void init( OBJ** obj, void* mem )
{
*obj = (Obj*)mem;
memset(mem, 0, sizeof(OBJ));
}
void cleanup( OBJ** obj) {
free(*obj);
*obj = NULL;
}
You might even move the malloc() into the init() function to make it even easier to use just like I have the free() call in cleanup().
You can't. In your struct float c[][SIZE_C2]; is a 2 dimensional array of floats that lives inside the struct. It is not a pointer to something that you can point to the memory you allocated. You have to make c a pointer to a 2 dimensional array like this:
#define SIZE_A 4
#define SIZE_B 8
#define SIZE_C1 12
#define SIZE_C2 16
struct Float2D {
float c[0][SIZE_C2];
};
typedef struct
{
// float a[SIZE_A];
// float b[SIZE_B];
// float c[SIZE_C1][SIZE_C2];
float* a;
float* b;
struct Float2D *c;
} OBJ;
void init( OBJ* obj, void* mem )
{
float* mem_p = (float*)mem;
obj->a = mem_p;
mem_p += SIZE_A;
obj->b = mem_p;
mem_p += SIZE_B;
obj->c = (struct Float2D*)mem_p;
}
Note: The 0 in float c[0][SIZE_C2]; is required as you can not have a struct with only a flex array. This could be a gcc compiler extension. Maybe you can figure out how to specify the type without extra struct.
I have the following struct:
struct Map {
void* arr; // includes pointers to certain blocks of memory
int mem_block_count;
};
The void* arr holds pointers to certain cells in memory. Each cell holds a void* pointer to another cell in memory (as a linked list) a char* string, and a value with its own size.
You should be able to grab one of these pointers to a cell by doing arr[i] where i is an index - is this right?
I am trying to access the first pointer to one of these cells, which is probably arr[0]. Then, I want to get access to the pointer to the next cell in memory. But this is of void type. So how do I get at it? For getting access to the char*, I just move forward in memory, and then do a strcpy. But how do I get access/deref the next pointer?
EDIT: I also thought about casting the void pointer to an int, but I'm not sure I would get the right value. Something like:
int *next_pointer;
next_pointer = (int*) map->arr[i]
But is this not preserving the pointer to the pointer when I cast this as an int? How do I dereference a pointer to a pointer correctly?
EDIT - Couldn't I also, because it is a linked list of pointers, also do something like - map->arr + index * sizeof(void*) to get to the start of a given cell?
You can't use array indexing on a void pointer, as it really just a generic pointer without a type. And if there's no type then there's no way of adding the correct offset to the base memory address. You have to cast it to the correct structure before using array indexing:
((struct some_structure *) arr)[1]
You need pointers to pointers.
#include <stdio.h>
#include <stdlib.h>
struct Map {
void **arr; // includes pointers to certain blocks of memory
int mem_block_count;
};
int main(void) {
struct Map map = {0};
int a1[100];
double a2[100];
char a3[100];
map.arr = malloc(3 * sizeof *map.arr);
if (!map.arr) /* error */;
a1[42] = 42;
a2[42] = 42;
a3[42] = 42;
map.mem_block_count = 3;
map.arr[0] = a1;
map.arr[1] = a2;
map.arr[2] = a3;
printf("%d %f %c\n", ((int *)(map.arr[0]))[42],
((double *)(map.arr[1]))[42],
((char *)(map.arr[2]))[42]);
free(map.arr);
return 0;
}
I am trying to compute the power of the matrix A using multiplications.
I am having problems with the ArrayPower function. It does not function as i think it should.The MultiArray function however seems to work fine. Can anyone help me ?
#include <stdio.h>
int** MultiArray(int a[2][2],int b[2][2]);
int** ArrayPower(int a[2][2],int e);
int main(void)
{
int fa[2][2];
fa[0][0]=0;
fa[0][1]=1;
fa[1][0]=1;
fa[1][1]=1;
int **multifa=malloc(sizeof(int)*2);
for (int i=0;i<2;i++) {
multifa[i]=malloc(sizeof(int)*2);
}
multifa=ArrayPower(fa,2);
printf("%d %d\n",multifa[0][0],multifa[0][1]);
printf("%d %d\n",multifa[1][0],multifa[1][1]);
return 0;
}
int** MultiArray(int a[2][2], int b[2][2]) {
//multi a *b
//memory allocation
int i,rows=2,cols=2;
int **c=malloc(rows*sizeof(int));
for (i=0;i<rows;i++) {
c[i]=malloc(cols*sizeof(int));
}
c[0][0]=a[0][0]*b[0][0]+a[0][1]*b[1][0];
c[0][1]=a[0][0]*b[0][1]+a[0][1]*b[1][1];
c[1][0]=a[1][0]*b[0][0]+a[1][1]*b[1][0];
c[1][1]=a[1][0]*b[0][1]+a[1][1]*b[1][1];
return c;
}
int** ArrayPower(int a[2][2],int e) {
//memory allocation
int i,rows=2,cols=2;
int **c=malloc(rows*sizeof(int));
for (i=0;i<rows;i++) {
c[i]=malloc(cols*sizeof(int));
}
c[0][0]=a[0][0];
c[0][1]=a[0][1];
c[1][0]=a[1][0];
c[1][1]=a[1][1];
for (i=1;i<e;i++) {
c=MultiArray(a,c);
}
return c;
}
MultiArray is declared as taking a second parameter of type int [2][2], but it is called with an argument of c, which as type int **. These are not compatible types.
In a parameter, the type int [2][2] is automatically converted to a pointer to an array of two int, the type int (*)[2]. This is a pointer to a place where there are two int objects (and, because we know it is the first element of an array of two arrays of two int objects, we know there are two more int objects beyond the first two).
The definition of c with int **c means that c is a pointer to a pointer to an int. A pointer to a pointer and a pointer to an array are different and are not compatible.
One way to fix this is to define c with int (*c)[2] = malloc(2 * sizeof *c);. It is then unnecessary to have the loop after the definition that allocates more space; the single allocation allocates the entire array.
The return type of MultiArray should be changed similarly, as well as the code within it and elsewhere in the program. Alternatively, the second parameter of MultiArray can be changed from int b[2][2] to int **b. (This latter is an easier edit but produces an inferior program, since it uses more pointers and allocations than necessary.)
You should always compile your code with warnings enabled. That would have alerted you to the incorrect call.