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.
Related
This question already has answers here:
Why are we not allowed to have assignment statements in the file scope in C?
(2 answers)
Why can't I assign values to global variables outside a function in C?
(1 answer)
Declaring and assigning variables with file scope in C
(1 answer)
Closed 3 years ago.
My goal is to make a static variable 'val' available to a different .c file (just for experimentation) .
so i made a global pointer which holds the address of the static variable, and by this global pointer, i am trying to access the static variable's value in another file .
But,
static int val=33;
int *ptr;
ptr = &val;
gives me error.
while if i do it like this, it works .
static int val=33;
int *ptr = &val;
Why?
doing
static int val=33;
int *ptr;
ptr = &val; /* define a global variable with an init value */
at global scope you define global variables and for the compiler the implicit type of ptr is int in the line ptr = &val; so this is not compatible with an int*. You cannot have an assignment at global scope, this is why ptr = &val; is not the assignment of ptr previously defined but the definition of a global variable with an initial value.
Putting the code in a local scope there is no problem, for instance with
int main()
{
static int val=33;
int *ptr;
ptr = &val; /* an assignment of ptr */
}
compile without problem
You are using an old or poor quality compiler which assumes int for a missing type.
At file scope, an expression statement such as ptr = &val; is not part of standard C. The compiler is attempting to treat this as a declaration statement. To do that, it assumes a type of int, as if the statement were:
int ptr = &val;
Since ptr was previously declared int *, this new declaration with type int conflicts, and the compiler reports there are conflicting types.
C 2018 6.7.2 2 specifies a constraint for declarations:
At least one type specifier shall be given in the declaration specifiers in each declaration, and in the specifier-qualifier list in each struct declaration and type name.
When a constraint is violated, a compiler should issue a diagnostic message about it. So a good compiler will warn that the type specifier is missing (and not just that the resulting default type conflicts with a prior declaration).
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).
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]
I'm working on a block-cipher program in C, and it seems none of my functions are returning the proper type of pointer, so my code won't even compile.
An example is this:
char *evenString(char * inText) /*takes a string of text. If it has an odd number of chars, it adds ASCII char 19 as padding.*/
{
int inputLength = strlen(inText);
char* evenText; /*pointer to character array*/
if(inputLength%2) /*If even, pad*/
{
evenText = (char*) malloc(sizeof(char) * (inputLength+2));
strcpy(evenText,inText);
evenText[inputLength] = FILLER_CHARACTER;
}
else
{
evenText = (char*) malloc(sizeof(char) * (inputLength+1));
strcpy(evenText,inText);
}
return evenText; /*which should be a char*, right?*/
}
When I call it in main, the call looks like this:
char *plainText = evenString(inputText);
And the compiler brings up these exceptions:
block_cypher.c:28:22: warning: initialization makes pointer from integer without a cast [enabled by default]
char *plainText = evenString(inputText);
block_cypher.c: At top level:
block_cypher.c:38:7: error: conflicting types for ‘evenString’
char *evenString(char * inText)
block_cypher.c:28:22: note: previous implicit declaration of ‘evenString’ was here
char *plainText = evenString(inputText);
block_cypher.c: In function ‘evenString’:
block_cypher.c:45:26: warning: incompatible implicit declaration of built-in function ‘malloc’ [enabled by default]
evenText = (char*) malloc(sizeof(char) * (inputLength+2));
block_cypher.c:53:26: warning: incompatible implicit declaration of built-in function ‘malloc’ [enabled by default]
evenText = (char*) malloc(sizeof(char) * (inputLength+1));
It's like this for all my functions, which all return a pointer of type int* or char*. I haven't even been able to compile, so I don't even know where to begin to fix this. Any insight to this would be greatly appreciated. Thank you for your time!
UPDATE: Thank you all! I followed all your advice:
I inserted function prototypes, which resolved many of the errors right away.
I included ; I don't know what I was thinking, but I've been working in a Java IDE a lot recently and forgot a lot of important stuff about C.
As suggested, I stopped casting malloc(). My C professor told us that since malloc returns a VOID* pointer, it's always good practice to cast it.
Thank you all!
The below error:
block_cypher.c:28:22: note: previous implicit declaration of ‘evenString’ was here
// ^^^^^^^^
char *plainText = evenString(inputText);
indicates that at the location where the evenString function is invoked, the compiler doesn't know that function's signature, which usually means you are missing proper header file. As a result of that, the compiler deduces its own declaration (based on supplied arguments), with return type defaulted to int:
int evenString(char* inText);
And this is where the error originates from:
warning: initialization makes pointer from integer without a cast [enabled by default]
// ^^^^ ^^^^^^^
After reading all errors / warnings it looks that you define the evenString function after its first usage. You should either declare the function before its first usage, or move entire function definition to the top of your translation unit (at least before you use that function for the first time).
That is:
block_cypher.c: At top level:
block_cypher.c:38:7: error: conflicting types for ‘evenString’
// ^^ ^^^^^^^^^^^
char *evenString(char * inText)
block_cypher.c:28:22: note: previous implicit declaration of ‘evenString’ was here
// ^^
char *plainText = evenString(inputText);
means you have invoked evenString(inputText); at line 28, where the compiler deduced the return type to be int, and only then you declare/define the function at line 38, which is too late (at least without previous declaration).
To make it working, try adding declaration before that 28 th line:
char* evenString(char* inText);
You need to add a prototype for evenString() before calling the function.
Without a prototype the compiler assumes it returns a value of type int and the assignment
char *plainText = evenString(inputText);
is like
char *plainText = <int value here>;
so the compiler complains
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;