Passing a variable into a function and changing its global value - c

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.

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.

How can I fix pointer problem in my C code?

#include <stdio.h>
void test(void **arg) {
int foo = 3;
**(int **)arg = foo; // I want to just fix this line!!
}
int main(void) {
int temp = 0;
printf("%d\n", temp);
test((void **)&temp);
printf("%d\n", temp);
return 0;
}
In my code, It has problem 'Segmentation fault', but I don't know how can I fix my code..
I wanna fix just **(int **)arg = foo; line.
Can Anybody Help Me?
In your code, function test(void **temp), variable temp is a pointer to a pointer, aka double pointer. That is to say, it value is an address. But when you call test from main, that value is 0, which means that address is address 0.
You can not assign a value to address 0.
It looks like you are writing to address 0.
Because:
&temp is a pointer to int.
*((int**)&temp) is an int.
**((int**)&temp) use your value from temp as address.
Your function
void test(void **arg);
expects a "pointer to pointer to void", that is the address of a location that contains another address pointing to a generic data.
When you call the function you are not passing what it expects
int temp = 0;
test((void **)&temp);
In fact &temp is an address pointing to an integer. But it expects the address of an address! So, when inside the function you deference it twice (every deferencing with the * operators means "resolving" one address) the second time you are trying to access the address 0.
In order to fix it, just pass to test a pointer to pointer:
int temp = 0;
int *tempAddr = &temp; //tempAddr points to temp
test((void **)&tempAddr); //The type of the address of tempAddr is 'int **'
You were actually asking a different thing: you explicitly asked for a fix of the **((int **) arg) = foo; line.
That's not so easy, as you are currently receiving an invalid pointer to pointer, and there's no way to make it valid just changing that line. In order to sort it out you would need to change test() interface, like shown below:
#include <stdio.h>
void test(void *arg) { // changed 'void **' to 'void *'
int foo = 3;
*(int *)arg = foo; // only one level of dereferencing
}
int main(void) {
int temp = 0;
printf("%d\n", temp);
test((void *)&temp); // cast to 'void *' instead of 'void **'
printf("%d\n", temp);
return 0;
}

cannot understand this error

As I am a beginner in C
When I run the following C code :
#include<stdio.h>
void f(int *p)
{
*p=3;
}
int main()
{
int *p;
f(&p);
return 0;
}
I get these messages after compilation:
1) warning:passing arg 1 of 'f' from incompatible pointer type f(&p)
2) note: expected 'int *' but argument is of type 'int **' void
f(int *p)
Please note:
&p is the memory address of a variable
*p is the actual value of the variable
So what you are doing is:
In main: You are generating a variable pointer p. And you are passing this pointers address to a function f.
f thinks it gets a pointer as parameter but it gets a memory address and obviusly can't handle it.
More information is here
Hope I could help!
You're passing a pointer to a pointer as an argument to a function expecting a pointer to an int. They're two different types and the compiler will complain.
There is another issue where you have the 'pointer' to the int but not the space allocated to store it. There are a couple solutions to this situation.
1)
{
int q; // add a new int, to which p points to. this allocates memory for an int
int *p = &q;
f(p); // don't pass a pointer to a pointer, just the pointer
return 0;
}
2)
{
int p; // have p be the int in the first place, not a pointer
f(&p); // your original call is unchanged and works now
return 0;
}
It's hard to think about this, because there are multiple levels of pointers.
Here's a simpler example. This is what we might call "pass by reference":
#include <stdio.h>
void f(int *ip)
{
*ip = 5;
}
int main(){
{
int i = 7;
printf("%d\n", i);
f(&i);
printf("%d\n", i);
}
Function f accepts a pointer to an int. It modifies that int. In main, we have an int variable i. We take the address of that variable using &, and pass the resulting pointer to function f. In this way, function f is able to modify i in the caller. Note that function f takes a pointer to something, and that what it takes a pointer to is the type of i in the caller -- in other words, pointer to int.
Now let's look at another example. This time, instead of manipulating an int by reference, we're going to manipulate a pointer variable, a char *. (We're going to end up dealing with pointers to pointers.)
void g(char **sp)
{
*sp = malloc(10);
strcpy(*sp, "oranges");
}
int main(){
{
char *s = "apples";
printf("%s\n", s);
g(&s);
printf("%s\n", s);
}
Again, function g accepts an argument by reference. Again, function g is able to modify a variable in its caller. Again, the pointer type accepted by g is a pointer to the type being manipulated. Since the type being manipulated is "pointer to char", function g accepts a pointer to pointer to char, or char **.
But the code you posted is sort of a mixture of the two. Your function f accepts an int pointer, just like mine, and it tries to use that int pointer to modify an int variable in the caller. But then, in your main function, the variable you're trying to modify isn't an int, it's a pointer to int, or int *.
When you call
f(&p);
you're starting with a pointer-to-int, p, and you're taking its address with &, resulting in a pointer-to-pointer-to-int. But then you;re calling function f, which expects a pointer-to-int. That's what the compiler is trying to tell you with the messages
warning: passing arg 1 of 'f' from incompatible pointer type
note: expected 'int *' but argument is of type 'int **'

Void** as parameter requires cast

I have a function in C that needs to receive a pointer to an array (with an unspecified type).
To do so, I use void**, as I would use void* to receive an array of unspecified elements.
There's a problem unfortunately: the compiler gives a warning (passing argument 1 of 'f' from incompatible pointer type). If I ignore the warning, and try to execute the program, everything works as expected.
The only way to get rid of the warning is to cast whatever I try to pass to the function to void**.
Why does C behaves like that? And is there a better way to solve the warning?
PS: I need to compile using GCC with the flags -std=gnu89 -pedantic -Wall
Example
int f(void** param){ return 1; }
int main(){
int *arr = malloc(sizeof(int) * 20);
int i;
for(i=0; i < 20; i++) arr[i] = i;
f(&arr);
}
The pointer to anything type is void*, and the compiler will not complain about conversions to that type. But void** is not a pointer to anything, it's a pointer to an array of pointers to anything, which is quite different from a pointer to an array of pointers to integers, so the compiler complains.
So, to solve the warning, yes you would need to cast explicitly.
Although void * is the "generic pointer" in C, void ** isn't a "generic pointer to pointer". Instead, it's nothing more than the "specific pointer to void *, the generic pointer".
In your case, a int ** is converted implicitly to a void **, which is not generic. Since a void ** is not guaranteed to be able to hold all pointer variables (thus incompatible to a int **), the compiler raises a warning.
Here is the warning generated by clang:
main.c:7:7: warning: incompatible pointer types passing 'int **' to parameter of
type 'void **' [-Wincompatible-pointer-types]
f(&arr);
^~~~
main.c:1:14: note: passing argument to parameter 'param' here
int f(void** param){ return 1; }
To eliminate this warning, you can have int f(void* param);, and cast param to int ** inside the function. There will be no warning because a void * can be used to store any pointer (Quoted from N1570):
6.3.2.3 Pointers
1 A pointer to void may be converted to or from a pointer to any
object type. A pointer to any object type may be converted to a
pointer to void and back again; the result shall compare equal to the
original pointer.
It seems that you want to modify the address of the data (not the value) inside the function, you can't do that directly with a void * because you can't use arithmetic with void *, but you can pass the size of the first element and a chunk of bytes (char *), suppose you want to change the address of arr to arr + 1 (the second element of the array) inside the function:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void f(void *ptr, size_t size)
{
// char *p = *ptr; Wrong, you can't dereference a void * without a cast
char *p = *(char **)ptr; /* pointer to address of ptr */
memmove(p, p + size, size); /* assign the address of ptr + 1 to ptr */
}
int main(void)
{
int *arr = malloc(sizeof(int) * 20);
int i;
for (i = 0; i < 20; i++) arr[i] = i;
f(&arr, sizeof arr[0]);
printf("%d\n", arr[0]);
return 0;
}
Output:
1

C passing a pointer to an array on the stack

I am getting confused as to whether it is valid (in C) to pass a pointer to an array that has been initiated as follows (e.g. at compile time on the stack):
int a[] = {1, 2, 3};
my_func(&a);
void my_func(int **a)
{
int *tmp = *a; /* this statement fixes the problem */
printf("%d %d %d", (*a)[0], (*a)[1], (*a)[2]); /*doesn't work */
printf("%d %d %d", tmp[0], tmp[1], tmp[2]); /*does work */
}
when I step through this with gdb I can't 'see' any of the values (*a)[0], etc. from 'inside' my_func. e.g.
(gdb) p (*a)[0]
Cannot access memory at address 0x0
I'm thinking that possibly I have a fundamental misunderstanding with regard to what you can and can't do with arrays that are on the stack rather than the heap?
I hope thats not the case as it is very convenient for my unit tests to declare arrays on the stack as in the example, but I need to test functions that are expecting pointers to pointers to int.
Note I do get a compiler warning as follows:
test_utility.c:499:5: warning: passing argument 1 of ‘int_array_unique’ from incompatible pointer type [enabled by default]
../src/glamdring2.h:152:5: note: expected ‘int **’ but argument is of type ‘int (*)[12]’
but I thought it would be ok to mix int *a[] with **a? Perhaps not? Are they not equivalent?
a[] is an array, not a pointer ("not an lvalue"); in your function call
func( &a);
&a decays to a pointer to int; &a is not a pointer to pointer to int. Why? there is no pointer to point to.
The function prototype
void func( int **p);
expects a pointer to pointer to int, that does not fit the function being called with a pointer to int as an argument, like you did.
UPDATE: I don't know what the OP's intentions were, so this is just a guess...
void my_func(int *a);
int a[] = {1, 2, 3};
my_func(a); /* note: this is equivalent to my_func( &a ); */
void my_func(int *a)
{
printf("%d %d %d\n", a[0], a[1], a[2] );
}
printf("%p vs %p vs %p\n",&a,&a[0],a);
&a &a[0] and a - are all the same - the address of the first int in the array
void my_func(int **a);
int main(int ac, char *av[]) {
int a[] = {1, 2, 3};
int *p = a;
printf("%p vs %p vs %p\n",&a,&a[0],a); //are all the same - the address of the first int in the array
my_func(&p);
return 0;
}
void my_func(int **a) {
printf("%d %d %d", (*a)[0], (*a)[1], (*a)[2]);
}
http://www.ibiblio.org/pub/languages/fortran/append-c.html
http://publications.gbdirect.co.uk/c_book/chapter5/arrays_and_address_of.html
It's perfectly fine to declare an array on the stack and pass its address to a function, so long as the function doesn't try to store a reference to the array for later use.
Not sure why you're adding the extra indirection to the the argument, though. Why not just declare my_func to take an int* as a parameter, and simply pass a as the argument? This is less likely to confuse the debugger.
You are assuming that sizeof(int) == sizeof(void *). This might not be true. I am not sure if this is the cause of your problem, but at least I would start by either adding a runtime test asserting the assumption, or probably better change from int to pointer.

Resources