Store function address into global variable - c

Consider the following code:
void f() {};
void* v = f;
int i = f;
int main() { }
Why storing function address into int variable gives me an error:
error: initializer element is not a compile-time constant
But for void* variable doesn't?

Variables declared at file scope ("global") have static storage duration.
All variables with static storage duration must be initialized to a compile-time constant. In C, other variables (not even const ones) don't count as compile-time constants, hence the error.
In addition, you cannot assign a pointer to an integer, doing so is not valid C. You have to manually convert the pointer to an integer type first, by casting it.
In addition, the cast void* v = f; is not well-defined. The C standard only specifies casts between void* and pointer to object type.
Now what you should do to get a function's address is this:
#include <stdint.h>
uintptr_t i = (uintptr_t) f;
int main (void)
{ ...
uintptr_t is guaranteed to be large enough to contain the address of any pointer.

When I compile this, I get:
$ gcc foo.c
foo.c:3:5: warning: incompatible pointer to integer conversion initializing 'int' with an expression of type 'void ()' [-Wint-conversion]
int i = f;
^ ~
foo.c:3:9: error: initializer element is not a compile-time constant
int i = f;
^
1 warning and 1 error generated.
Really, I think that warning ought to be an error. You're trying to put an address into an integer, and that's generally bad form.
That said, if the compiler goes ahead and makes the conversion, the result is not a compile-time constant (because it's a converted value). Thus the error.
Although you're not asking about C++, I was curious as to how the two languages differ, so I checked how my compiler behaved. In that language, both assignments from f are illegal, for good reason. void* is not a pointer to a parameter-less function, and int isn't either.
$ g++ foo.c
clang: warning: treating 'c' input as 'c++' when in C++ mode, this behavior is deprecated
foo.c:2:7: error: cannot initialize a variable of type 'void *' with an lvalue of type 'void ()'
void* v = f;
^ ~
foo.c:3:5: error: cannot initialize a variable of type 'int' with an lvalue of type 'void ()'
int i = f;
^ ~
2 errors generated.

The initializations of both v and i are illegal.
However, some compilers allow conversion from a function pointer to a void* as a compiler extension.
Try compiling with your warnings cranked up (as you should by habit), and you may get warnings on the void* v = f; line as well.
A correct way to store a pointer to the function f would be
void (*p)() = f;

If you really wanted to put the underlying bits of a pointer into an int you could try this:
typedef union _UPTRINT
{
void *ptr;
int i;
} UPTRINT;
Then assign the function pointer to UPTRINT::ptr and access it as i (assuming that pointers and ints are the same size on your platform; adjust the type of i as necessary).

Related

Why does having an `int (*)(float)` point to an `int foo()` trigger a warning, but having an `int (*)(double)` point to it doesn't?

I have this piece of code:
int foo() { return 0; }
int main()
{
int (*float_function)(float) = foo;
}
When compiled using x86-64 GCC 12.2, with -Wall, it produces the warning (Link):
warning: initialization of 'int (*)(float)' from incompatible pointer type 'int (*)()' [-Wincompatible-pointer-types]
But, when I change from float to double (Link):
int foo(){ return 0;}
int main()
{
int (*double_function)(double) = foo;
}
The warning is now gone.
But I think that both of these should get a warning.
Am I wrong somewhere? Why does GCC not complain about the second example?
int foo() is declared without specifying its parameters. This is an obsolescent feature that lets you call it with any arguments. When calling the function, integer arguments are promoted to int (if needed), and float arguments are promoted to double.
Due to this, it's impossible for this function to receive a float parameter, which makes it incompatible with int (*)(float), but not with int (*)(double).
If you want a function that takes no parameters, declare it as int foo(void) which will make it incompatible with both.
Note that even with double, the code is not valid C because int foo() {...} is a function definition, so the compiler knows that it has no parameters (see chux's comment below for the standard reference). Most compilers still allow it.
If you replace it with a declaration int foo(); and place the definition elsewhere then the above is correct. In that case, the relevant standard quote (C17 6.7.6.3/15) is:
For two function types to be compatible, [...] If one type has
a parameter type list and the other type is specified by a function declarator that is not part of a
function definition and that contains an empty identifier list, [...] the type of each parameter shall be compatible with the type that results from the application of the default argument promotions.

Why use a void pointer for dereferencing variables of datatypes?

Dereferencing a float variable using a void pointer:
#include <stdio.h>
int main() {
float a = 7.5;
void *vp = &a;
printf("%f", *(float*)vp); /* Typecasting a void pointer to float for dereference */
printf("\n");
}
Output: 7.500000
Dereferencing a variable using an integer pointer:
#include <stdio.h>
int main() {
float a = 7.5;
int *ip = &a;
printf("%f", *(float*)ip); /* Typecasting an int pointer to float for dereference */
printf("\n");
}
Output: 7.500000
In both, the outputs are same. What is the reason to go for dereferencing of different datatype variable, when we are able to do by typecasting a normal pointer?
Converting any data pointer to void* pointer and back is guaranteed to give back original pointer.
From C11 standard draft N1570:
6.3.2.3 Pointers
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.
Converting data pointer to other data pointer than void* (int* in your example) may work. It depends on the compiler you are using and the system you are on. Not all systems might use same internal representation for different pointer types.
A pointer to an object type may be converted to a pointer to a different object type. If the
resulting pointer is not correctly aligned 68) for the referenced type, the behavior is
undefined. Otherwise, when converted back again, the result shall compare equal to the
original pointer. When a pointer to an object is converted to a pointer to a character type,
the result points to the lowest addressed byte of the object. Successive increments of the
result, up to the size of the object, yield pointers to the remaining bytes of the object.
This is different from strict aliasing rules.
float a = 7.5;
int *ip=&a;
int i = *ip; // Dereferenced using invalid type
Code above breaks strict aliasing rule as dereferenced type is not the same as the original type. This results in undefined behaviour and is always invalid code.
A void pointer is a generic pointer which can hold the address of any type and can be typecast to any type.
In the first case, the program successfully compiled and ran without any warning or error, because using a void pointer to convert from one pointer type to another and then storing or casting it to the final type is safe without losing data.
But in the second case the GCC compiler generated a warning
prog.c: In function 'main':
prog.c:5:9: warning: initialization from incompatible pointer type [-Wincompatible-pointer-types]
int *ip=&a;
^
clang compiler:
warning: incompatible pointer types initializing 'int *' with an expression of type 'float *' [-Wincompatible-pointer-types]
int *ip=&a;
^ ~~
The C11 Standard, 6.3.2.3, paragraph 7:
A pointer to an object or incomplete type may be converted to a
pointer to a different object or incomplete type. If the resulting
pointer is not correctly aligned for the referenced type, the behavior
is undefined.
A void pointer is (sort of) untyped. It can point to anything without the compiler complaining. e.g. If you have an int variable, you can safely create a void pointer that points to this and pass it around. e.g.
int x = 10;
void *p = &x
is fine but
int x = 10;
float *p = &x;
will upset the compiler
This is especially useful for functions that operate on multiple pointer types or if you will decide what something is at runtime.
However, void pointers cannot be dereferenced (*) directly because the compiler doesn't know their type. So,
printf("%d\n", *p);
will break if p is void pointer. We have to know the size of what it points to to dereference it and this is done using a manual type cast (like what you've done).
In your specific case, you have a pointer that points to a float which you type cast back into float before printing it. So, you will get the same output. The void * pointer is not really playing a big role here.
An example of where you need a void * is the malloc function, If you look at the prototype, it returns a void *. i.e. a block of raw memory. You need to typecast this as a concrete type before you can do pointer arithmetic and dereferencing.

Pointers in C : assigning a literal to int *

What is really happening here when we provide a definition like below?
int * a = 2;
What is really happening behind the scenes?
SOLVED
Here a will point to memory address 2.
The result of conversion from integer to a pointer is implementation-defined in C, quoting C99 Section 6.3.2.3:
5 An integer may be converted to any pointer type. Except as previously specified, the result is implementation-defined, might not be correctly aligned, might not point to an entity of the referenced type, and might be a trap representation.
So you shouldn't rely on such conversion except when the literal is 0 which will give you a null pointer.
In practice you are likely to find that the pointer will hold the memory address specified by the integer constant:
#include <stdio.h>
int main(void) {
int * a = 2;
printf("%p", a); /* prints 0x2 in gcc */
return 0;
}
Most compilers will also warn about this unsafe cast :
$ gcc test.c test.c: In function ‘main’: test.c:4:13: warning:
initialization makes pointer from integer without a cast [enabled by
default] int * a = 2;
^
In here the memory for "a" will not be allocated. So, it gives the segmentation fault error.
It will try to take the 2 as a address and pointer a will try to point the address 2. But it may be our system does not have that address or it may be allocated for some other process.
So, in the compiling time it will give the one warning message.
Warning: initialization makes pointer from integer without a cast [enabled by default]

Redefinition of a pointer in global scope

In this question I am thoroughly confused about this seemingly basic aspect of C. Consider these two lines of code:
int *ptr;
*ptr = 2;
gcc will emit the following warnings:
main.cpp:4:1: warning: data definition has no type or storage class [enabled by default]
*ptr = 2;
^
main.cpp:4:2: warning: type defaults to 'int' in declaration of 'ptr' [enabled by default]
*ptr = 2;
^
main.cpp:4:8: warning: initialization makes pointer from integer without a cast [enabled by default]
*ptr = 2;
^
What type is ptr being defaulted to, int or int* (as in, is ptr a pointer, or an int)? If so, does this mean that ptr is pointing to address 2, or is that unchanged? I would assume that it's changed because it crashes unless you give ptr a valid address.
int i = 5;
int *ptr;
*ptr = &i;
int main(){
printf("%d", *ptr); // 5
}
I am aware of the possibility of undefined behavior and that you shouldn't ignore warnings, but I'm trying to see what actually happens here.
For context, see the comment chain under this answer.
Here is what's going on: since the two lines that you are showing are in the file scope (as opposed to a local scope) both lines are treated as declarations, not as a declaration and an assignment statement. This is because there could be no statements at the file scope - only declarations and definitions are allowed.
Old C rules allowed declarations of type int to omit type altogether. Therefore, the second line is
A declaration / definition of ptr
...which is a pointer, because it has an asterisk
...and it is also a pointer to int, because the type is missing.
That last rule is very archaic, and it has been deprecated in the ANSI version of the language standard. That is why you get a warning. If you change your code to
int *ptr;
int *ptr = &i;
your code is going to compile and run (demo).
Now one question remains: how come the compiler does not complain about duplicate declarations? It turns out that the compiler will treat multiple identical declarations as one and the same, as long as they are entirely identical to each other.

Warnings in a very basic Pointer Program in C

I've just started with pointers in c. This program gives the desired output but I am getting the below warnings when I compile it, What am I doing wrong?
pointer.c:3: warning: initialization makes pointer from integer without a cast
pointer.c:5: warning: incompatible implicit declaration of built-in function ‘printf’
pointer.c:8: warning: assignment from incompatible pointer type
pointer.c:12: warning: assignment makes pointer from integer without a cast
And my code is:
int main (int argc, char *argv[])
{
int *x=2, *ampx, *starampx, starx;
printf("x=");
printf("%d\n",x);
ampx=&x;
printf("&x=");
printf("%d\n",&x);
starampx=*ampx;
printf("*&x=");
printf("%d\n",starampx);
return 0;
}
Here is why:
initialization makes pointer from integer without a cast - int *x = 2, ... should be int x = 2, ...
incompatible implicit declaration of built-in function ‘printf’ - You need to add an #include <stdio.h> line at the top
assignment from incompatible pointer type - This will be fixed by the #1 change
assignment makes pointer from integer without a cast - *starampx should be starampx in the declaration.
In addition, printing pointers should be done with %p specifier, not %d:
printf("%p\n",&x);
Here is your fixed program on ideone: link.
int *x=2
is actually causing your program to have undefined behavior. It tells the compiler to point at an address 2, which may or may not be valid and derferencing the pointer will causes an undefined behavior.
Whenever you are using pointers, You need to make the pointer point to a valid memory big enough to store a int before you can store anything. So you either need:
int i = 2;
int *x = &i;
or you simply use:
int x = 2;
Considering, how you use x later in the program the latter is what you need.
You need to include stdio.h which tells the compiler that printf is an standard c library function.
In your code, in this statement, you have a problem.
int *x=2, *ampx, *starampx, starx;
^ ^
| |
*x is a pointer. To store the value of 2, you would need to allocate space for the same. Instead of the current implementation, please try with
int x=2, *ampx, starampx, starx;

Resources