Passing a dynamically allocated array from main to another function - c

First off, I have already looked at the example presented here:
Passing dynamically allocated array as a parameter in C
I am trying to pass a dynamically allocated array as a parameter to another function.
void InputIterations(int *iterations);
void CalcE(long double *E, int iterations);
int main()
{
int iterations;
long double *E;
InputIterations(&iterations);
E = (long double *) malloc(iterations * sizeof(long double));
CalcE(&E, iterations);
}
void InputIterations(int *iterations)
{
printf("Enter a number of iterations: ");
scanf("%d", iterations);
}
void CalcE(long double *E, int iterations)
{
long double sum = 0;
int i;
for(i=0; i<iterations; i++)
{
sum = sum + /*formula for calculating irrational constant e*/
*E = sum;
E++;
}
}
However, when I compile my code I get the following error:
error: cannot convert ‘long double**’ to ‘long double*’ for argument
‘1’ to ‘void CalcE(long double*, int)’ CalcE( &E, iterations );
Does anyone knows why I am getting this error ?
If you could please explain my mistake or point me to a source that explains it I would greatly appreciate the help.

Here:
CalcE(&E, iterations);
you take address of E (of type long double *) and pass it as an argument to CalcE. CalcE accepts as first parameter a pointer to long double. But when you take an address of E you are given actually a pointer to pointer to long double (long double**), and that is not a pointer to long double. And this is what your error tells you:
error: cannot convert ‘long double**’ to ‘long double*’ for argument
‘1’ to ‘void CalcE(long double*, int)’ CalcE( &E, iterations );
So you should have:
CalcE(E, iterations);

CalcE(&E, iterations);
should be
CalcE(E, iterations);
Hope I helped

Related

Segmentation Fault using a function pointer with array argument

today i started learning C, and i got stucked in function pointers. This is my code:
main.c:
#include <stdio.h>
int sumOfElements(int *arr, int arr_elements);
int main()
{
int (*ptr)(int,int) = NULL;
ptr = sumOfElements;
int a[] = {128, 64, 32, 16, 8, 4, 2, 1};
printf("Total of price is: %d", ptr(a, 8));
}
int sumOfElements(int *arr, int arr_elements)
{
int k =0;
int total;
for(;k < arr_elements;k++)
{
total += arr[k];
}
return total;
}
What i'm trying to do is access the elements of the array in the sumOfElements functions. When i call it normally, then everything goes smooth. When i try to use the function pointer, then the compiler throw me some warning before, and then the Segmentation Fault error:
main.c:17:9: warning: assignment from incompatible pointer type [-Wincompatible-pointer-types]
main.c:19:41: warning: passing argument 1 of ‘ptr’ makes integer from pointer without a cast [-Wint-conversion]
main.c:19:41: note: expected ‘int’ but argument is of type ‘int *’
Segmentation fault (core dumped)
Since i'm still learning it, i'm unsure about touching the code, because, like i said before, it works without the function pointer. Now, the error main.c:17:9: warning: assignment from incompatible pointer type [-Wincompatible-pointer-types], i didn't really understand it, because they're both int. So, if you could explain that too, it would save me a lot of time. So, why does it only throw Segmentation Fault (core dumped) when i use the funptr? All i know is that the Segmentation error is when we try to access memory that is only read-only or it's out-of-bound
For starters the pointer is declared like
int (*ptr)(int,int) = NULL;
that is it is a pointer to a function with two parameters of the type int.
But the function sumOfElements have different types of parameters
int sumOfElements(int *arr, int arr_elements);
That is the first parameter has the type int * instead of int.
Also as the array is not changed within the function then it is better to declare the function like
long long int sumOfElements( const int *arr, size_t arr_elements);
The function return type is long long int instead of int because it decreases the risk of overflow of the sum of elements of the array.
Correspondingly the pointer shall be declared like
long long int (*ptr)( const int *, size_t ) = NULL;
and the function should be called like
printf("Total of price is: %lld", ptr(a, sizeof( a ) / sizeof( *a ) ) );
Within the function you forgot to initialize the variable total.
int total;
The function can be defined the following way
long long int sumOfElements( const int *arr, size_t arr_elements )
{
long long int total = 0;
while( arr_elements-- )
{
total += arr[arr_elements];
}
return total;
}
Your function pointer declaration is of incorrect type.
Your pointer has signature of int (*ptr)(int,int) = NULL; while your function is int ()(int *, int).
Try declaring your pointer as int (*ptr)(int *,int) = NULL;
The function pointer needs to have the same parameter and return types as the function itself. In your ptr declaration you declare the first argument as int, but it should be int*.
int (*ptr)(int*,int) = NULL;
You have to use int (*ptr)(int*,int) = NULL; instead of int (*ptr)(int,int) = NULL;, since your array is an int pointer.
As your regular function sumOfElements already receives an int pointer it works correctly.

Error message "expected ‘float ***’ but argument is of type ‘float *’ [duplicate]

This question already has answers here:
Why can't we use double pointer to represent two dimensional arrays?
(6 answers)
Closed 4 years ago.
I have this code:
void checkmat(float *** mat,float * max,float * min){
float sum=0,sum2=0,flag=1;
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
sum+=(*mat[i][j]);
sum2+=(*mat[j][i]);
}
if(sum>(*max))
(*max)=sum;
if(flag){
(*min)=sum;
flag=0;
}
if(sum2<(*min))
(*min)=sum2;
sum=0;
sum2=0;
}
}
int main(){
float mat[n][n]={{1,2,3,4},{5,6,7.6,8},{9,1,2.9,3},{4,5,6,7}};
float min=0,max=0;
checkmat(&mat,&max,&min);
printf("%f is max %f is min \n",max,min);
}
For now, it doesn't really matter what the code does. I'm interested in why I get this error message from the compiler:
test3.c: In function ‘main’:
test3.c:38:10: warning: passing argument 1 of ‘checkmat’ from incompatible pointer type [-Wincompatible-pointer-types]
checkmat(&mat,&max,&min);
^
test3.c:8:6: note: expected ‘float ***’ but argument is of type ‘float (*)[4][4]’
void checkmat(float *** mat,float * max,float * min){
Any ideas?
As #Quentin mentioned, there is a difference between the two variables.
float ***
This is a pointer to a pointer to a pointer. I assume you used this because you thought that is how arrays are stored. It's a common misconception that a 2D array is always a array of pointers pointing to arrays.
Rather a pointer to a 2D array should be written as:
float (*)[][height]
In these cases 2D arrays are stored in memory column-after-column (when in column-major mode). Therefore if you have declare a 2D array:
float arr[5][10];
then the following will be true
arr == arr[0]
as the variable 'arr' points to the first element of the 2D array, which is also the start of the first column.
Also, you don't need to create a pointer to the array, as the array variable itself is already a pointer. So, to answer your question, your function parameters are declared incorrectly. It should be:
void checkmat(float mat[M][N],float * max,float * min){...}
NB: You need to specify the array size in the function.
You do not need any *** pointers
#include <stdio.h>
#include <float.h>
void checkmat(float * mat,float * max,float * min, size_t cols, size_t rows){
float sum=0,sum2=0,flag=1;
for(int i=0;i<rows;i++){
for(int j=0;j<cols;j++){
sum+=(mat[i * cols + j]);
sum2+=(mat[i * cols + j]);
}
if(sum > *max)
*max=sum;
if(flag){
*min=sum;
flag=0;
}
if(sum2< *min)
*min =sum2;
}
}
int main()
{
float mat[4][4]={{1,2,3,4},{5,6,7.6,8},{9,1,2.9,3},{4,5,6,7}};
float min=FLT_MAX,max=-FLT_MAX;
checkmat((&mat[0][0],&max,&min, sizeof(mat) / sizeof(mat[0]), sizeof(mat[0]) / sizeof(mat[0][0]));
printf("Size of the matrix is %zu x %zu. %f is max %f is min \n", max, min, sizeof(mat) / sizeof(mat[0]), sizeof(mat[0]) / sizeof(mat[0][0]));
}
It is often incorrectly said that pointers and arrays are equivalent in C. Your code is an example of this not being the case.
In most contexts, an array will decay to a pointer to its first member. This only applies to the outermost array dimension however. For example, a float [4], which is an array of float, will decay to float *. But a float [4][4], which is an array of float [4] decays into a float (*)[4] (i.e. a pointer to an array of float [4]), not a float **.
In your case you pass &mat, which has type float (*)[4][4], to a function expecting a float ***. These types are not compatible, hence the error.
The proper way to define the function is:
void checkmat(int rows, int cols, float mat[rows][cols] ,float * max,float * min)
This way you pass the 2D array to your function, along with the size of each dimension.
You then use mat in the function like this:
sum += mat[i][j];
sum2 += mat[j][i];
And you call the function like this, passing in mat directly instead of its address as well as its dimensions:
checkmat(n,n,mat,&max,&min);

Problems with local pointer array

I've done some digging around but cant find the solution. In my code below, when I declare my float **array as a global variable my program runs as intended. However I'm attempting to declare it in main() and pass it into functions and I'm getting some nasty errors when I try to compile.
Sample run below:
lab2.c: In function ‘NumOfVal’:
lab2.c:116: error: incompatible types when assigning to type ‘float’ from type ‘void *’
lab2.c:118: error: invalid type argument of ‘unary *’ (have ‘float’)
lab2.c:119: error: invalid type argument of ‘unary *’ (have ‘float’)
lab2.c:124: error: invalid type argument of ‘unary *’ (have ‘float’)
lab2.c:125: error: invalid type argument of ‘unary *’ (have ‘float’)
lab2.c: In function ‘calculateSum’:
lab2.c:137: error: invalid type argument of ‘unary *’ (have ‘float’)
lab2.c:140: error: invalid type argument of ‘unary *’ (have ‘float’)
lab2.c: In function ‘calculateAvg’:
lab2.c:150: error: invalid type argument of ‘unary *’ (have ‘float’)
lab2.c:153: error: invalid type argument of ‘unary *’ (have ‘float’)
lab2.c: In function ‘findMax’:
lab2.c:162: error: invalid type argument of ‘unary *’ (have ‘float’)
lab2.c:165: error: invalid type argument of ‘unary *’ (have ‘float’)
lab2.c:166: error: invalid type argument of ‘unary *’ (have ‘float’)
lab2.c: In function ‘findMin’:
lab2.c:174: error: invalid type argument of ‘unary *’ (have ‘float’)
lab2.c:175: error: invalid type argument of ‘unary *’ (have ‘float’)
lab2.c:177: error: invalid type argument of ‘unary *’ (have ‘float’)
lab2.c:178: error: invalid type argument of ‘unary *’ (have ‘float’)
lab2.c: In function ‘printVal’:
lab2.c:187: error: invalid type argument of ‘unary *’ (have ‘float’)
lab2.c:189: error: invalid type argument of ‘unary *’ (have ‘float’)
Now I realize the errors for my "calculation functions" are all the same and but the first error at line 116 is different. I don't understand why once I declare my pointer locally in main(), it gives me all these problems. I have to use pointer arithmetic and don't see how the statement:
*(array + index) = malloc();
Is no longer valid..
Why does a local declaration change the syntax for allocating memory and reading from/writing to an array??
Code below: Im not including some of the "calculation functions" to save space
/* Function declarations */
void printVal();
void userChoice();
void calculateSum();
void calculateAvg();
void findMin();
void findMax();
int main(){
float **array;
int numData;
/*get number of datasets */
numData = NumOfSet();
printf("%d\n",numData);
array = malloc(numData * sizeof(float*));
if(array != 0){
/* get # of values per dataset and enter those values */
NumOfVal(numData, array);
}
else{
printf("Memory Allocation Failed");
}
userChoice();
}
void userChoice(){
int flag = 1;
int setChoice;
int opChoice;
while(1){
if(flag == 0)
break;
printf("Which dataset would you like to perform operations on?: \n");
scanf("%d", &setChoice);
printf("Which operation would you like to perform on dataset #%d\n", setChoice);
printf("1. Calculate sum of all the values\n");
printf("2. Calculate average of all the values\n");
printf("3. Find the minimum value\n");
printf("4. Find the maximum value\n");
printf("5. Print values in dataset\n");
printf("6. Exit the program\n");
scanf("%d", &opChoice);
/* Switch statement which makes function calls to perform whatever the user specified */
switch(opChoice){
case 1:
calculateSum(setChoice);
break;
case 2:
calculateAvg(setChoice);
break;
case 3:
findMin(setChoice);
break;
case 4:
findMax(setChoice);
break;
case 5:
printVal(setChoice);
break;
case 6:
flag = 0;
printf("Thanks for playing!\n");
printf("%d", flag);
}
}
}
int NumOfSet(){
int numData;
printf("Enter number of Datasets: \n");
scanf("%d",&numData);
return(numData);
}
/* for each data set declare size and input float values */
int NumOfVal(int data, float *array){
int index; /* counters */
int array_index;
int copy;
float size;
float val;
for(index = 0; index < data;index++){
printf("Enter size of dataset %d:\n", index);
scanf("%f", &size);
copy = size;
printf("Size = %d\n", (int) size);
printf("Array #: %d\n", index);
/* malloc() for row */
/* array[index] */
*(array + index) = malloc(size * sizeof(float));
/*array[index][0] */
*(*(array + index) + 0) = size;
printf("%f\n", *(*(array + index) + 0));
/* loop for entering float values */
for(array_index = 1; array_index <= copy; array_index++){
printf("Enter each float value:");
scanf("%f", &val);
*(*(array + index) + array_index) = val;
printf("%f\n", *(*(array + index) + array_index));
printf("count: %d\n",array_index);
printf("size: %d\n",copy);
}
}
}
void calculateSum(int setChoice, float *array){
int i;
int first = *(*(array + setChoice-1) + 0);
float sum = 0;
for(i = 1; i <= first; i++){
sum += *(*(array + setChoice-1) + i);
}
printf("The sum of the values is: %.3f\n", sum);
}
P.S. Of course, when I had declared array as global, I didn't have array as a parameter of those functions.
So it seems if I declare pointer to a pointer to a float float **array in main(). I need to have float **array as the parameter of my functions and not just float *array.
**array --> a pointer which points to a list of float arrays(?)
*array --> simply a pointer to a single float array(?)
correct me if I'm wrong please
First you've got to understand what pointers are. A pointer is variable which
stores an address. It may be the address of another variable, it may be the
start of a memory block that you've got from dynamically allocating memory with
malloc & friends or the start of an array.
*array --> simply a pointer to a single float array(?)
Not necessarily.
Let's take a look at this:
float *array;
First, forget about the name of the variable, what really is important is the
type of the variable. In this case you've got a pointer to a float.
It may point to a single float variable
float n = 8.7;
float *array = &n;
but it also may point to the start of a sequence of floats (aka array):
float na[] = { 1.1, 2.2, 3.3, 4.4 };
float *array = na;
//or
float *array = na + 2;
Which one (pointing to a single object vs pointing to an array of objects) is the
correct one depends on the context. The documentation of the function will tell
you what it is expecting. That is what counts, not the variable name. If the
variable name is array, it may not be pointing to an array, the name is then
misleading, actually is a poorly chosen name.
**array --> a pointer which points to a list of float arrays(?)
Again, not necessarily.
Let's take one step further. A double pointer
float **array;
is a pointer that points to another pointer of float. Once again, forget about
the name of the variable, the context is important. It may point to a pointer
that points to a single variable:
float n = 8.7;
float *array = &n;
float **p2arr = &array;
or it may point to a pointer that points to the start of an array
float na[] = { 1.1, 2.2, 3.3, 4.4 };
float *array = na;
float **p2arr = &array;
// *p2arr[0] == 1.1
// *(p2arr[0] + 1) == 2.2
// *(p2arr[0] + 2) == 3.3
or it may point to an array of pointers that point to a single float values:
float a=1.1, b=2.2, c=3.3;
float **ppa = calloc(3, sizeof *ppa);
ppa[0] = &a;
ppa[1] = &b;
ppa[2] = &b;
or it may point to an array if pointers that point to an array of float
values:
float **ppa = calloc(3, sizeof *ppa);
ppa[0] = calloc(5, sizeof *ppa[0]);
ppa[1] = calloc(2, sizeof *ppa[1]);
ppa[2] = calloc(9, sizeof *ppa[2]);
ppa[0][0] = 1.1;
ppa[0][1] = 2.2;
ppa[1][0] = 10.8;
ppa[2][5] = 11.1;
The context matter, and if you are using a function that has a float **arg
argument, the function documentation will tell you what it will expect from such
a pointer. If you are creating a function, you decide the context,
because you know what you are working with.
So about your code. As far as I can see, float **array; in main allocates
memory for an array of pointers. I'd suggest in this case to use calloc
instead, because calloc sets the allocated memory to 0. It makes initializing
and freeing the whole block easier even if subsequent calls of
malloc/realloc/calloc fail.
array = calloc(numData, sizeof *array);
Also note that I'm not using the sizeof(float*) here but instead sizeof *array.
The former is not incorrect, but it is easier to make mistakes like forgetting
the * or adding more than needed, specially when
dealing with double pointer. The later however will always return the correct
size, regardless of the type.
Now you're array is allocated and initialized. All pointers array[0],
array[1], etc are initialized and point to NULL. The you call
NumOfVal(numData, array); and that's where all goes wrong.
Let's see the signature of the function:
int NumOfVal(int data, float *array);
It expects a single pointer, so that's not good, you cannot pass the double
pointer. Since you are writing the function yourself, you can change the
function and set your own context.
Before fixing the function, let's look at this line in NumOfVal:
*(array + index) = malloc(size * sizeof(float));
Again here, I'd suggest using calloc. Second let's take look at this:
*(array + index). It is the same as doing array[index] and will return you
a float. You are essentially doing:
float f = malloc(sizeof * sizeof(float));
This is just wrong, because malloc returns a void pointer, you cannot set a
float to a void pointer.
But also you train of thoughts are wrong here, because the array variable in
main is a double pointer, the memory layout is different. Why?
I will changed the name of the variable, to make it clear which variable I'm referring to.
*(array_main + index) is the same as doing array_main[index]. It returns you
a pointer to float. When you pass array_main to NumOfVal, you are now
treating it like a single pointer. The size of a float and the size of a
pointer to float might not be the same, so array_main + index and array + index will return you a
different memory location. In fact it will return you a memory location where a
pointer is saved, not where the float is saved, so you are destroying the
pointers saved in array_main.
pfs = sizeof(float*)
fs = sizeof(float)
real address base base+pfs base+2*pfs base+3*pfs
pointer arithmetic base base+1 base+2 base+3
+---------+----------+----------+
| float* | float * | float * |
+---------+----------+----------+
real address base base+fs base+2*fs base+3*fs
pointer arithmetic base base+1 base+2 base+3
+-----------+-----------+-----------+
| float | float | float |
+-----------+-----------+-----------+
The graph above shows you the same memory address base seen as a
float** (top) and seen as a float* (bottom). Basically the top representation
is what you have for array in main. The bottom one is what you have for
array in NumOfVal. You see the pointer arithmetic doesn't line up and so you
are accessing the incorrect memory locations and that yields undefined
behaviour.
How to fix it? Fix the NumOfVal like this:
int NumOfVal(int data, float **array) {
int index; /* counters */
int array_index;
int copy;
float size;
float val;
for(index = 0; index < data;index++){
printf("Enter size of dataset %d:\n", index);
scanf("%f", &size);
copy = size;
printf("Size = %d\n", (int) size);
printf("Array #: %d\n", index);
/* malloc() for row */
/* array[index] */
array[index] = calloc(size + 1, sizeof *index);
array[index][0] = size;
I see you want to use the first element to save the size. It's a nice strategy,
but you need to allocate one more element because of this, otherwise you for
loop will overflow.
You have the same problem in calculateSum, same fix:
void calculateSum(int setChoice, float **array){
int i;
int first = array[setChoice-1][0];
float sum = 0;
for(i = 1; i <= first; i++){
sum += array[setChoice - 1][i];
}
printf("The sum of the values is: %.3f\n", sum);
}
Never forget to check the return value of malloc & friends, if they return
NULL, you cannot access that memory. Also write a function that frees the
memory. Here calloc comes in handy because free(NULL); is legal.
void free_array(int size, float **array)
{
if(array == NULL)
return;
for(int i = 0; i < size; ++i)
free(array[i]);
free(array);
}
In your NumOfVal function you should do:
array[index] = calloc(size + 1, sizeof *index);
if(array[index] == NULL)
{
// error handling
return 0;
}
Also you are forgetting to return a value in NumOfVal. If your function is not
going to return a value anyway, declare it as void.
Now in main after doing your calculations:
free_array(numData, array);
to free the memory.

Passing a variable into a function and changing its global value

I am trying to pass a variable into a function and change its global value but it does not work. Here is the simplified code:
int main()
{
int *Num = malloc (sizeof (int));
ChangeValue(&Num);
printf("Number is %i\n", Num);
}
int ChangeValue(int *temp)
{
*temp = 10
}
The error message is:
expected 'int *' but argument is of type 'int **'
I tried to change the function to int ChangeValue(int **temp) but received the following error:
warning: assignment makes pointer from integer without a cast.
Any suggestions?
int *Num means Num is of type int *. Since it's already an integer pointer, there's no need to take its address again when you pass it to your function that takes an int *.
ChangeValue(Num);
OTOH, since it is a pointer you will have to dereference it to use it as an integer, like with printf.
printf("Number is %i\n", *Num);
Variable Num is a pointer to an int (int*). Function ChangeValue(int*) takes a pointer to an int (int*). However, you are passing a pointer to Num (int**) to it.
Short answer: remove & before Num in ChangeValue(&Num);
Long answer: You seem to have a problem understanding how pointers work, you might want to read more about this before going further.

Incompatible pointer type in function call - C

I have a program below
#include<stdio.h>
void int_frac(double a, long *int_part,double *frac_part)
{
*int_part = (long int)(a);
*frac_part = a - *int_part;
}
int main()
{
long int* ip;
double* fp;
double i = 1.2345467;
int_frac(i,&ip,&fp);
printf("%f = %ld + %f",i,*ip,*fp);
return 0;
}
I am using gcc compiler.
This program gives an error:
expected long int* but argument is of type long int**
and
expected double* but argument is of type double**
Why this error comes up.
The compiler is telling you that you are passing long int** where it expects long int*. And that you are passing double** where it expects double*.
The compiler is correct. You declared ip to be long int* and then passed &ip which has type long int**. You made the same mistake with fp.
What you have done is declare two pointers, ip and fp, but not initialized the pointers. The fundamental problem is that you have declared pointers but have not allocated any variables. If int_frac is to be able to write to variables, those variables must be allocated somewhere. So, what you need to do is declare two values, and pass the addresses of those values to int_frac.
Your code should be:
int main()
{
long int ip; // allocates a variable
double fp; // as does this
double i = 1.2345467;
int_frac(i,&ip,&fp); // pass the addresses of ip and fp
printf("%f = %ld + %f",i,ip,fp);
return 0;
}
The error which you received is the answer in my opinion as you are passing long int** when it expects long int* You need to declare ip as long int ip like this:
int main()
{
long int ip;
double fp;
double i = 1.2345467;
int_frac(i,&ip,&fp);
printf("%f = %ld + %f",i,ip,fp);
return 0;
}

Resources