Reputation: 37
int *p=123;
char *s="hi";
i tried searching this question on google but didn't get the exact answer.So, the question is why does the compiler throw error when we assign numbers in the place of the address to an integer pointer, But compiles perfectly when a char pointer is assigned string or bunch of characters and stores them in the memory with each character by different memory address.
like the code i have put in the beginning, when i declare the int pointer and assign it the integers it throws the warning "warning: initialization of 'int *' from 'int' makes pointer from integer without a cast"
but when i declare the char pointer and not assign it the address but the characters, it compiles without a problem. As the char pointer stores each character with its own address in the memory, shouldn't the int pointer store each number in the memory as an array with each element's different address?
Upvotes: 2
Views: 1058
Reputation: 310910
These two declarations
int *p=123;
char *s="hi";
have a semantic difference.
To make them semantically identical you need to write either
int *p=123;
char *s= 'h';
In this case the pointers are initialized by scalar objects. In the first declaration the pointer p
is initialized by the integer constant 123
and in the second declaration the pointer s
is initialized by the integer character constant 'h'
.
So the compiler should issue a warning for the both declarations.
Or you could write for example
int a[] = { 123 };
int *p = a;
char *s = "hi";
In this case the both pointers are initialized by addresses of first characters of arrays.
Pay attention to that the string literal "hi"
has the array type char[3]
and is stored in memory like { 'h', 'i', '\0' }
. Used as initializers the array a
and the string literal "hi"
are implicitly converted to pointers to their first elements correspondingly of the types int *
and char *
. So pointers of the types int *
and char *
are initialized by expressions of the same types int *
and char *
.
Upvotes: 3
Reputation: 47915
The difference is due to two separate facts, both of which are "quirks" of C which can be confusing at first.
The first quirk is that when you have a pointer variable initialization, it's different from an assignment. As you know, when you say
int *p = 123;
this means, "p
is a pointer to an int
, with an initial value of 123". But what if we defined the variable p
on one line, and gave it a value on a second line using an ordinary assignment statement? It's easy to imagine that would look like this:
int *p;
*p = 123; /* WRONG */
But in fact it would look like this:
int *p;
p = 123;
In a pointer declaration/definition with initialization, the *
means you're declaring a pointer variable, but it doesn't mean you're initializing the contents of what the pointer points to. In a regular assignment expression, on the other hand, the *
is an operator that means, "I am talking about the memory location pointed to by this pointer". So when you say
*p = 123;
that means, "Pointer p
points to some memory location, and I want to store the value 123 into that memory location." On the other hand, when you say
p = 123;
that means, "Make pointer p
point to some memory location 123." In that case, it might be more clear to say
p = 0x0000007b;
But, of course, unless we're doing some kind of specialized embedded programming, we rarely pick exact numeric values for the memory locations we want to access.
But let's get back to the question you asked. To avoid confusion, let's take your two declarations-with-initializers
int *p = 123;
char *s = "hi";
and change them into separate declarations:
int *p;
char *s;
and assignments:
p = 123;
s = "hi";
As we just saw, the integer pointer assignment p = 123
doesn't do what we want, and it probably doesn't even do anything useful at all. So why does the character pointer assignment
s = "hi";
work?
That has to do with the second quirk of C, which is in two parts. The first part is that, as you probably know, strings in C are represented as arrays of characters. (That's fine, it's hardly even a "quirk".) But then the second part is, whenever you mention an array in an expression and try to take its value, the value you get is a pointer to the array's first element. But what does that mean?
First of all, when you mentioned that string constant "hi"
in your program, the compiler automatically created a little array for you. The effect was just as if you had said
char __string_constant = { 'h', 'i', '\0' };
(perhaps that's a third quirk).
And then, when you said
s = "hi";
it was just exactly as if you had written
s = &__string_constant[0];
This has been kind of a long answer, but if you're still with me, the bottom line is that when you're working with pointers, you have to be clear in your mind about the distinction between the pointer and what it points to. When you said
char *s = "hi";
you were taking care of both things: You were making the pointer p
point somewhere, and you were arranging that, at the location that it pointed to, there was a string "hi"
. But when you said
int *p = 123;
you were making the pointer p
point to location 123, but you weren't doing anything to affect the value at memory location 123 — and you were certainly not making p
point to the integer value 123.
With all of that said, you might still be wondering, is there a one-step way to set an integer pointer to point to an array of integers, like there is for character arrays with s =
"hi"`?
Traditionally, there wasn't. But C99 introduced something called an array initializer that's just what you'd want. It looks like this:
int *p = (int[]){ 123 };
That sets up the same sort of situation as with the string literal. The compiler creates an array and sets the pointer to point to it, just as if you had said
int __array initializer[] = { 123 };
int *p = &array initializer[0];
So, in summary, you can say
int *p = (int[]){ 123 };
char *s = "hi";
and, once you've done it this way, there's not so much difference between int pointers and char pointers, after all.
Addendum: It didn't really come up in your question or this answer, but there's one more closely-related C quirk that it's worth mentioning while we're at it. I said, "whenever you mention an array in an expression and try to take its value, the value you get is a pointer to the array's first element." And I said that when you wrote
s = "hi";
it was just as if you had written
char string_constant = { 'h', 'i', '\0' };
s = &string_constant[0];
But it would also work if you wrote
s = string_constant;
That looks wrong at first — how can you take an array like string_constant
, and assign it to a pointer like s
? Well, it's because, as I said, when you mention an array in an expression and try to take its value, what you get is a pointer to the array's first element. Or, in other words,
s = &string_constant[0];
Upvotes: 3
Reputation: 121
First of all, you are trying to store a primitive type to a pointer. Since pointers expect pointers either a variable address or an array starting point, they cannot really hold a variable directly.
When you declared the variable "hi" this way: char *s = "hi";
, you actually created an array of characters which is stored in stack segment if it is not global. However, when you tried to store 123, it is a primitive type as I mentioned, so you are trying to store a variable to the pointer which expects address.
If you try to do something like, char s1[3] = {'h', 'i', '\0'};
this will work similarly with the "hi" declaration. What you did with your code is like char* s = 'h'
which will give an error as well.
As a result, pointers need to store addresses, so that primitive types cannot be used at right side of the assigning.
Upvotes: 0
Reputation: 222272
C 2018 6.5.16.1 1 specifies constraints for assignments. The first constraint about assigning to a pointer is:
— the left operand has atomic, qualified, or unqualified pointer type, and (considering the type the left operand would have after lvalue conversion) both operands are pointers to qualified or unqualified versions of compatible types, and the type pointed to by the left has all the qualifiers of the type pointed to by the right;
In int *p=123;
, p
is initialized, not assigned. However, the rules for initialization say the rules for assignment apply, in 6.7.9 11. p
is a pointer, but 123
is an int
, not a pointer, so it does not satisfy the constraint, and the compiler complains.
In char *s="hi";
, "hi"
is an array of characters. It is used as an expression to provide the initial value of s
. C automatically converts an array used as an expression to a pointer to its first element.1 Thus, with "hi"
, we have a pointer to a character, which is a char *
, and the constraint above allows assigning a char *
to a char *
, so the compiler does not complain.
Note that p = 123;
would not assign the address of 123
to the pointer. It would attempt to assign the value of 123
to the pointer. That should never be done except in special situations where a special memory address is known, such as an address for a hardware interface or a special address set by the operating system. In those cases, we would use a cast to convert the integer address to a pointer, as with p = (void *) 0x1000;
. In contrast, the reason s = "hi";
assigns the address of "hi"
to s
instead of assigning its value is because of the automatic conversion of an array to a pointer.
For completeness, the second constraint about assigning to a pointer is:
— the left operand has atomic, qualified, or unqualified pointer type, and (considering the type the left operand would have after lvalue conversion) one operand is a pointer to an object type, and the other is a pointer to a qualified or unqualified version of
void
, and the type pointed to by the left has all the qualifiers of the type pointed to by the right;
This does not apply since there is no void *
involved.
The third constraint about assigning to a pointer is:
— the left operand is an atomic, qualified, or unqualified pointer, and the right is a null pointer constant; or
This rule would allow you to assign an integer to a pointer, because one kind of null pointer constant is an integer constant expression with value 0. Thus, int *p = 0;
or int *p = 4-4;
is allowed. int *p = x-x;
would not satisfy this constraint because it is not a constant expression.
1 This conversion is not performed when the array is the operand of sizeof
, is the operand of unary &
, or is a string literal used to initialize an array. The rule for this is in 6.3.2.1 3.
Upvotes: 1