Related
This question already has answers here:
In C, why can't an integer value be assigned to an int* the same way a string value can be assigned to a char*?
(5 answers)
Why it is possible to assign string to character pointer in C but not an integer value to an integer pointer
(3 answers)
Assigning strings to pointer in C Language
(4 answers)
Why must int pointer be tied to variable but not char pointer?
(8 answers)
Closed 4 years ago.
Still learning more C and am a little confused. In my references I find cautions about assigning a pointer that has not been initialized. They go on to give examples. Great answers yesterday by the way from folks helping me with pointers, here:
Precedence, Parentheses, Pointers with iterative array functions
On follow up I briefly asked about the last iteration of the loop and potentially pointing the pointer to a non-existent place (i.e. because of my references cautioning against it). So I went back and looked more and find this:
If you have a pointer
int *pt;
then use it without initializing it (i.e. I take this to mean without a statement like *pt= &myVariable):
*pt = 606;
you could end up with a real bad day depending on where in memory this pointer has been assigned to. The part I'm having trouble with is when working with a string of characters something like this would be ok:
char *str = "Sometimes I feel like I'm going crazy.";
Where the reference says, "Don't worry about where in the memory the string is allocated; it's handled automatically by the compiler". So no need to say initialize *str = &str[0]; or *str = str;. Meaning, the compiler is automatically char str[n]; in the background?
Why is it that this is handled differently? Or, am I completely misunderstanding?
In this case:
char *str = "Sometimes I feel like I'm going crazy.";
You're initializing str to contain the address of the given string literal. You're not actually dereferencing anything at this point.
This is also fine:
char *str;
str = "Sometimes I feel like I'm going crazy.";
Because you're assigning to str and not actually dereferencing it.
This is a problem:
int *pt;
*pt = 606;
Because pt is not initialized and then it is dereferenced.
You also can't do this for the same reason (plus the types don't match):
*pt= &myVariable;
But you can do this:
pt= &myVariable;
After which you can freely use *pt.
When you write sometype *p = something;, it's equivalent to sometype *p; p = something;, not sometype *p; *p = something;. That means when you use a string literal like that, the compiler figures out where to put it and then puts its address there.
The statement
char *str = "Sometimes I feel like I'm going crazy.";
is equivalent to
char *str;
str = "Sometimes I feel like I'm going crazy.";
Simplifying the string literal can be expressed as:
const char literal[] = "Sometimes I feel like I'm going crazy.";
so the expression
char *str = "Sometimes I feel like I'm going crazy.";
is logically equivalent to:
const char literal[] = "Sometimes I feel like I'm going crazy.";
const char *str = literal;
Of course literals do not have the names.
But you can't dereference the char pointer which does not have allocated memory for the actual object.
/* Wrong */
char *c;
*c = 'a';
/* Wrong - you assign the pointer with the integer value */
char *d = 'a';
/* Correct */
char *d = malloc(1);
*d = 'a';
/* Correct */
char x
char *e = &x;
*e = 'b';
The last example:
/* Wrong - you assign the pointer with the integer value */
int *p = 666;
/* Wrong you dereference the pointer which references to the not allocated space */
int *r;
*r = 666;
/* Correct */
int *s = malloc(sizeof(*s));
*s = 666;
/* Correct */
int t;
int *u = &t;
*u = 666;
And the last one - something similar to the string literals = the compound literals:
/* Correct */
int *z = (int[]){666,567,234};
z[2] = 0;
*z = 5;
/* Correct */
int *z = (const int[]){666,567,234};
Good job on coming up with that example. It does a good job of showing the difference between declaring a pointer (like char *text;) and assigning to a pointer (like text = "Hello, World!";).
When you write:
char *text = "Hello!";
it is essentially the same as saying:
char *text; /* Note the '*' before text */
text = "Hello!"; /* Note that there's no '*' on this line */
(Just so you know, the first line can also be written as char* text;.)
So why is there no * on the second line? Because text is of type char*, and "Hello!" is also of type char*. There is no disagreement here.
Also, the following three lines are identical, as far as the compiler is concerned:
char *text = "Hello!";
char* text = "Hello!";
char * text = "Hello!";
The placement of the space before or after the * makes no difference. The second line is arguably easier to read, as it drives the point home that text is a char*. (But be careful! This style can burn you if you declare more than one variable on a line!)
As for:
int *pt;
*pt = 606; /* Unsafe! */
you might say that *pt is an int, and so is 606, but it's more accurate to say that pt (without a *) is a pointer to memory that should contain an int. Whereas *pt (with a *) refers to the int inside the memory that pt (without the *) is pointing to.
And since pt was never initialized, using *pt (either to assign to or to de-reference) is unsafe.
Now, the interesting part about the lines:
int *pt;
*pt = 606; /* Unsafe! */
is that they'll compile (although possibly with a warning). That's because the compiler sees *pt as an int, and 606 as an int as well, so there's no disagreement. However, as written, the pointer pt doesn't point to any valid memory, so assigning to *pt will likely cause a crash, or corrupt data, or usher about the end of the world, etc.
It's important to realize that *pt is not a variable (even though it is often used like one). *pt just refers to the value in the memory whose address is contained in pt. Therefore, whether *pt is safe to use depends on whether pt contains a valid memory address. If pt isn't set to valid memory, then the use of *pt is unsafe.
So now you might be wondering: What's the point of declaring pt as an int* instead of just an int?
It depends on the case, but in many cases, there isn't any point.
When programming in C and C++, I use the advice: If you can get away with declaring a variable without making it a pointer, then you probably shouldn't declare it as a pointer.
Very often programmers use pointers when they don't need to. At the time, they aren't thinking of any other way. In my experience, when it's brought to their attention to not use a pointer, they will often say that it's impossible not to use a pointer. And when I prove them otherwise, they will usually backtrack and say that their code (which uses pointers) is more efficient than the code that doesn't use pointers.
(That's not true for all programmers, though. Some will recognize the appeal and simplicity of replacing a pointer with a non-pointer, and gladly change their code.)
I can't speak for all cases, of course, but C compilers these days are usually smart enough to compile both pointer code and non-pointer code to be practically identical in terms of efficiency. Not only that, but depending on the case, non-pointer code is often more efficient than code that uses pointers.
There are 4 concepts which you have mixed up in your example:
declaring a pointer. int *p; or char *str; are declarations of the pointers
initializing a pointer at declaration. char *str = "some string"; declares the pointer and initializes it.
assigning a value to the pointer. str = "other string"; assigns a value to the pointer. Similarly p = (int*)606; would assign the value of 606 to the pointer. Though, in the first case the value is legal and points to the location of the string in static memory. In the second case you assign an arbitrary address to p. It might or might not be a legal address. So, p = &myint; or p = malloc(sizeof(int)); are better choices.
assigning a value to what the pointer points to. *p = 606; assigns the value to the 'pointee'. Now it depends, if the value of the pointer 'p' is legal or not. If you did not initialize the pointer, it is illegal (unless you are lucky :-)).
Many good explanations over here. The OP has asked
Why is it that this is handled differently?
It is a fair question, he means why, not how.
Short answer
It is a design decision.
Long answer
When you use a literal in an asigment, the compiler has two options: either it places the literal in the generated assembly instruction (maybe allowing variable length assembly instructions to accomodate different literal byte lenghts) or it places the literal somewhere the cpu can reach it (memory, registers...). For ints, it seems a good choice to place them on the assembly instruction, but for strings... almost all strings used in programs (?) are too long to be placed on the assembly instruction. Given that arbitrarily long assembly instructions are bad for general purpose CPUs, C designers have decided to optimize this use case for strings and save the programmer one step by allocating memory for him. This way, the behaviour is consistent across machines.
Counterexample
Just to see that, for other languages, this has not to be necessarily the case, check this. There (it is Python), int constants are actually placed in memory and given an id, always. So, if you try to get the address of two different variables that were asigned the same literal, it will return the same id (since they are refereing to the same literal, already placed in memory by the Python loader). It is useful to stress that in Python, the id is equivalent to an address in the Python's abstract machine.
Each byte of memory is stored in its own numbered pigeon-hole. That number is the "address" of that byte.
When your program compiles, it builds up a data-table of constants. At run-time these are copied into memory somewhere. So upon execution, in memory is the string (here at the 100,000th byte):
#100000 Sometimes I feel like I'm going crazy.\0
The compiler has generated code, such that when the variable str is created, it is automatically initialised with the address of where that string came to be stored. So in this example's case, str -> 100000. This is where the name pointer comes from, str does not actually contain that string-data, it holds the address of it (i.e. a number), "pointing" to it, saying "that piece of data at this address".
So if str was treated like an integer, it would contain the value 100000.
When you dereference a pointer, like *str = '\0', it's saying: The memory str points at, put this '\0' there.
So when the code defines a pointer, but without any initialisation, it could be pointing anywhere, perhaps even to memory the executable doesn't own (or owns, but can't write to).
For example:
int *pt = blah; // What does 'pt' point at?
It does not have an address. So if the code tries to dereference it, it's just pointing off anywhere in memory, and this gives indeterminate results.
But the case of:
int number = 605;
int *pt = &number
*pt = 606;
Is perfectly valid, because the compiler has generated some space for the storage of number, and now pt contains the address of that space.
So when we use the address-of operator & on a variable, it gives us the number in memory where the variable's content is stored. So if the variable number happened to be stored at byte 100040:
int number = 605;
printf( "Number is stored at %p\n", &number );
We would get the output:
Number is stored at 100040
Similarly with string-arrays, these are really just pointers too. The address is the memory-number of the first element.
// words, words_ptr1, words_ptr2 all end up being the same address
char words[] = "Sometimes I feel like I'm going crazy."
char *words_ptr1 = &(words[0]);
char *words_ptr2 = words;
There are answers here with very good and detailed information.
I will post another answer, perhaps targeting more straightly to the OP.
Rephrasing it a bit:
Why is
int *pt;
*pt = 606;
not ok (non working case), and
char *str = "Sometimes I feel like I'm going crazy.";
is ok (working case)?
Consider that:
char *str = "Sometimes I feel like I'm going crazy.";
is equivalent to
char *str;
str = "Sometimes I feel like I'm going crazy.";
The closest "analogous", working case for int is (using a compound literal instead of a string literal)
int *pt = (int[]){ 686, 687 };
or
int *pt;
pt = (int[]){ 686, 687 };
So, the differences with your non-working case are three-fold:
Use pt = ... instead of *pt = ...
Use a compound literal, not a value (by the same token, str = 'a' wouldn't work).
Compound literals are not always guaranteed to work, since the lifetime of its storage depends on standard/implementation.
In fact, its use as above may give the compilation error taking address of temporary array.
A string variable can be declared either as an array of characters char txt[] or using a character pointer char* txt. The following illustrates the declaration and initialization of a string:
char* txt = "Hello";
In fact, as illustrated above, txt is a pointer to the first character of the string literal.
Whether we are able to modify (read/write) a string variable or not, depends on how we declared it.
6.4.5 String literals (ISO)
6. It is unspecified whether these arrays are distinct provided their elements have the appropriate values. If the program attempts to modify such an array, the behavior is undefined.
Actually, if we declare a string txt like we previously did, the compiler will declare the string literal in a read-only data section .rodata (platform dependent) even if txt is not declared as const char*. So we can not modify it. Actually, we should not even try to modify it. In this case gcc can fire warnings (-Wwrite-strings) or even fail due to -Werror. In this cas, it is better to declare string variable as const pointers:
const char* txt = "Hello";
On the other hand, we can declare a string variable as an array of characters:
char txt[] = "Hello";
In that case, the compiler will arrange for the array to get initialized from the string literal, so you can modify it.
Note: An array of characters can be used as if it was a pointer to its first character. That's why we can use txt[0] or *txt syntax to access the first character. And we can even explicitly convert an array of characters to a pointer:
char txt[] = "Hello";
char* ptxt = (char*) txt;
What is the correct way to use int* x?
Mention any related link if possible as I was unable to find one.
Because the literal "hello" evaluates to a pointer to constant memory initialised with the string "hello" (and a nul terminator), i.e. the value you get is of char* type.
If you want a pointer to number 12 then you'll need to store the value 12 somewhere, e.g. in another int, and then take a pointer to that:
int x_value = 12;
int* x = &x_value;
However in this case you're putting the 12 on the stack, and so that pointer will become invalid once you leave this function.
You can at a pinch abuse that mechanism to make yourself a pointer to 12; depending on endianness that would probably be
int* x = (int*)("\x0c\x00\x00");
Note that this is making assumptions about your host's endianness and size of int, and that you would not be able to modify that 12 either (but you can change x to point to something else), so this is a bad idea in general.
Because the compiler creates a static (constant) string "hello" and lets x point to that, where it doesn't create a static (constant) int.
A string literal creates an array object. This object has static storage duration (meaning it exists for the entire execution of the program), and is initialized with the characters in the string literal.
The value of a string literal is the value of the array. In most contexts, there is an implicit conversion from char[N] to char*, so you get a pointer to the initial (0th) element of the array. So this:
char *s = "hello";
initializes s to point to the initial 'h' in the implicitly created array object. A pointer can only point to an object; it does not point to a value. (Incidentally, that really should be const char *s, so you don't accidentally attempt to modify the string.)
String literals are a special case. An integer literal does not create an object; it merely yields a value. This:
int *ptr = 42; // INVALID
is invalid, because there is no implicit conversion of 42 from int* to int. This:
int *ptr = &42; // INVALID
is also invalid, because the & (address-of) operator can only be applied to an object (an "lvalue"), and there is no object for it to apply to.
There are several ways around this; which one you should use depends on what you're trying to do. You can allocate an object:
int *ptr = malloc(sizeof *ptr); // allocation an int object
if (ptr == NULL) { /* handle the error */ }
but a heap allocation can always fail, and you need to deallocate it when you're finished with it to avoid a memory leak. You can just declare an object:
int obj = 42;
int *ptr = &obj;
You just have to be careful with the object's lifetime. If obj is a local variable, you can end up with a dangling pointer. Or, in C99 and later, you can use a compound literal:
int *ptr = &(int){42};
(int){42} is a compound literal, which is similar in some ways to a string literal. In particular, it does create an object, and you can take that object's address.
But unlike with string literals, the lifetime of the (anonymous) object created by a compound literal depends on the context in which it appears. If it's inside a function definition, the lifetime is automatic, meaning that it ceases to exist when you leave the block containing it -- just like an ordinary local variable.
That answers the question in your title. The body of your question:
What is the correct way to use int* x?
is much more general, and it's not a question we can answer here. There are a multitude of ways to use pointers correctly -- and even more ways to use them incorrectly. Get a good book or tutorial on C and read the section that discusses pointers. Unfortunately there are also a lot of bad books and tutorials. Question 18.10 of the comp.lang.c FAQ is a good starting point. (Bad tutorials can often be identified by the casual use of void main(), and by the false assertion that arrays are really pointers.)
Q1. Why can't we assign int *x=12? You can provided that 12 is a valid memory address which holds an int. But with a modern OS specifying a hard memory address is completely wrong (perhaps except embedded code). The usage is typically like this
int y = 42; // simple var
int *x = &y; // address-of: x is pointer to y
*x = 12; // write a new value to y
This looks the same as what you asked, but it is not, because your original declaration assigns the value 12 to x the pointer itself, not to *x its target.
Q2. Why can't we assign int *x = "12"? Because you are trying to assign an incompatible type - a char pointer to int pointer. "12" is a string literal which is accessed via a pointer.
Q3. But we can assign char* x= "hello"
Putting Q1 and Q2 together, "hello" generates a pointer which is assigned to the correct type char*.
Here is how it is done properly:
#include <stdio.h>
#include <stdlib.h>
int main() {
int *x;
x = malloc(sizeof(int));
*x = 8;
printf("%d \n", *x);
}
Given pointers to char, one can do the following:
char *s = "data";
As far as I understand, a pointer variable is declared here, memory is allocated for both variable and data, the latter is filled with data\0 and the variable in question is set to point to the first byte of it (i. e. variable contains an address that can be dereferenced). That's short and compact.
Given pointers to int, for example, one can do this:
int *i;
*i = 42;
or that:
int i = 42;
foo(&i); // prefix every time to get a pointer
bar(&i);
baz(&i);
or that:
int i = 42;
int *p = &i;
That's somewhat tautological. It's small and tolerable with one usage of a single variable. It's not with multiple uses of several variables, though, producing code clutter.
Are there any ways to write the same thing dry and concisely? What are they?
Are there any broader-scope approaches to programming, that allow to avoid the issue entirely? May be I should not use pointers at all (joke) or something?
String literals are a corner case : they trigger the creation of the literal in static memory, and its access as a char array. Note that the following doesn't compile, despite 42 being an int literal, because it is not implicitly allocated :
int *p = &42;
In all other cases, you are responsible of allocating the pointed object, be it in automatic or dynamic memory.
int i = 42;
int *p = &i;
Here i is an automatic variable, and p points to it.
int * i;
*i = 42;
You just invoked Undefined Behaviour. i has not been initialized, and is therefore pointing somewhere at random in memory. Then you assigned 42 to this random location, with unpredictable consequences. Bad.
int *i = malloc(sizeof *i);
Here i is initialized to point to a dynamically-allocated block of memory. Don't forget to free(i) once you're done with it.
int i = 42, *p = &i;
And here is how you create an automatic variable and a pointer to it as a one-liner. i is the variable, p points to it.
Edit : seems like you really want that variable to be implicitly and anonymously allocated. Well, here's how you can do it :
int *p = &(int){42};
This thingy is a compound literal. They are anonymous instances with automatic storage duration (or static at file scope), and only exist in C90 and further (but not C++ !). As opposed to string literals, compound literals are mutable, i.e you can modify *p.
Edit 2 : Adding this solution inspired from another answer (which unfortunately provided a wrong explanation) for completeness :
int i[] = {42};
This will allocate a one-element mutable array with automatic storage duration. The name of the array, while not a pointer itself, will decay to a pointer as needed.
Note however that sizeof i will return the "wrong" result, that is the actual size of the array (1 * sizeof(int)) instead of the size of a pointer (sizeof(int*)). That should however rarely be an issue.
int i=42;
int *ptr = &i;
this is equivalent to writing
int i=42;
int *ptr;
ptr=&i;
Tough this is definitely confusing, but during function calls its quite useful as:
void function1()
{
int i=42;
function2(&i);
}
function2(int *ptr)
{
printf("%d",*ptr); //outputs 42
}
here, we can easily use this confusing notation to declare and initialize the pointer during function calls. We don't need to declare pointer globally, and the initialize it during function calls. We have a notation to do both at same time.
int *ptr; //declares the pointer but does not initialize it
//so, ptr points to some random memory location
*ptr=42; //you gave a value to this random memory location
Though this will compile, but it will invoke undefined behaviour as you actually never initialized the pointer.
Also,
char *ptr;
char str[6]="hello";
ptr=str;
EDIT: as pointed in the comments, these two cases are not equivalent.
But pointer points to "hello" in both cases. This example is written just to show that we can initialize pointers in both these ways (to point to hello), but definitely both are different in many aspects.
char *ptr;
ptr="hello";
As, name of string, str is actually a pointer to the 0th element of string, i.e. 'h'.
The same goes with any array arr[], where arr contains the address of 0th element.
you can also think it as array , int i[1]={42} where i is a pointer to int
int * i;
*i = 42;
will invoke undefined behavior. You are modifying an unknown memory location. You need to initialize pointer i first.
int i = 42;
int *p = &i;
is the correct way. Now p is pointing to i and you can modify the variable pointed to by p.
Are there any ways to write the same thing dry and concisely?
No. As there is no pass by reference in C you have to use pointers when you want to modify the passed variable in a function.
Are there any broader-scope approaches to programming, that allow to avoid the issue entirely? May be I should not use pointers at all (joke) or something?
If you are learning C then you can't avoid pointers and you should learn to use it properly.
I'm learning C programming in a self-taught fashion. I know that numeric pointer addresses must always be initialized, either statically or dynamically.
However, I haven't read about the compulsory need of initializing char pointer addresses yet.
For example, would this code be correct, or is a pointer address initialization needed?
char *p_message;
*p_message = "Pointer";
I'm not entirely sure what you mean by "numeric pointer" as opposed to "char pointer". In C, a char is an integer type, so it is an arithmetic type. In any case, initialization is not required for a pointer, regardless of whether or not it's a pointer to char.
Your code has the mistake of using *p_message instead of p_message to set the value of the pointer:
*p_message = "Pointer" // Error!
This wrong because given that p_message is a pointer to char, *p_message should be a char, not an entire string. But as far as the need for initializing a char pointer when first declared, it's not a requirement. So this would be fine:
char *p_message;
p_message = "Pointer";
I'm guessing part of your confusion comes from the fact that this would not be legal:
char *p_message;
*p_message = 'A';
But then, that has nothing to do with whether or not the pointer was initialized correctly. Even as an initialization, this would fail:
char *p_message = 'A';
It is wrong for the same reason that int *a = 5; is wrong. So why is that wrong? Why does this work:
char *p_message;
p_message = "Pointer";
but this fail?
char *p_message;
*p_message = 'A';
It's because there is no memory allocated for the 'A'. When you have p_message = "Pointer", you are assigning p_message the address of the first character 'P' of the string literal "Pointer". String literals live in a different memory segment, they are considered immutable, and the memory for them doesn't need to be specifically allocated on the stack or the heap.
But chars, like ints, need to be allocated either on the stack or the heap. Either you need to declare a char variable so that there is memory on the stack:
char myChar;
char *pChar;
pChar = &myChar;
*pChar = 'A';
Or you need to allocate memory dynamically on the heap:
char* pChar;
pChar = malloc (1); // or pChar = malloc (sizeof (char)), but sizeof(char) is always 1
*pChar = 'A';
So in one sense char pointers are different from int or double pointers, in that they can be used to point to string literals, for which you don't have to allocate memory on the stack (statically) or heap (dynamically). I think this might have been your actual question, having to do with memory allocation rather than initialization.
If you are really asking about initialization and not memory allocation: A pointer variable is no different from any other variable with regard to initialization. Just as an uninitialized int variable will have some garbage value before it is initialized, a pointer too will have some garbage value before it is initialized. As you know, you can declare a variable:
double someVal; // no initialization, will contain garbage value
and later in the code have an assignment that sets its value:
someVal = 3.14;
Similarly, with a pointer variable, you can have something like this:
int ary [] = { 1, 2, 3, 4, 5 };
int *ptr; // no initialization, will contain garbage value
ptr = ary;
Here, ptr is not initialized to anything, but is later assigned the address of the first element of the array.
Some might say that it's always good to initialize pointers, at least to NULL, because you could inadvertently try to dereference the pointer before it gets assigned any actual (non-garbage) value, and dereferencing a garbage address might cause your program to crash, or worse, might corrupt memory. But that's not all that different from the caution to always initialize, say, int variables to zero when you declare them. If your code is mistakenly using a variable before setting its value as intended, I'm not sure it matters all that much whether that value is zero, NULL, or garbage.
Edit. OP asks in a comment: You say that "String literals live in a different memory segment, they are considered immutable, and the memory for them doesn't need to be specifically allocated on the stack or the heap", so how does allocation occur?
That's just how the language works. In C, a string literal is an element of the language. The C11 standard specifies in §6.4.5 that when the compiler translates the source code into machine language, it should transform any sequence of characters in double quotes to a static array of char (or wchar_t if they are wide characters) and append a NUL character as the last element of the array. This array is then considered immutable. The standard says: If the program attempts to modify such an array, the behavior is undefined.
So basically, when you have a statement like:
char *p_message = "Pointer";
the standard requires that the double-quoted sequence of characters "Pointer" be implemented as a static, immutable, NUL-terminated array of char somewhere in memory. Typically implementations place such string literals in a read-only area of memory such as the text block (along with program instructions). But this is not required. The exact way in which a given implementation handles memory allocation for this array / NUL terminated sequence of char / string literal is up to the particular compiler. However, because this array exists somewhere in memory, you can have a pointer to it, so the above statement does work legally.
An analogy with function pointers might be useful. Just as the code for a function exists somewhere in memory as a sequence of instructions, and you can have a function pointer that points to that code, but you cannot change the function code itself, so also the string literal exists in memory as a sequence of char and you can have a char pointer that points to that string, but you cannot change the string literal itself.
The C standard specifies this behavior only for string literals, not for character constants like 'A' or integer constants like 5. Setting aside memory to hold such constants / non-string literals is the programmer's responsibility. So when the compiler comes across statements like:
char *charPtr = 'A'; // illegal!
int *intPtr = 5; // illegal!
the compiler does not know what to do with them. The programmer has not set aside such memory on the stack or the heap to hold those values. Unlike with string literals, the compiler is not going to set aside any memory for them either. So these statements are illegal.
Hopefully this is clearer. If not, please comment again and I'll try to clarify some more.
Initialisation is not needed, regardless of what type the pointer points to. The only requirement is that you must not attempt to use an uninitialised pointer (that has never been assigned to) for anything.
However, for aesthetic and maintenance reasons, one should always initialise where possible (even if that's just to NULL).
First of all, char is a numeric type, so the distinction in your question doesn't make sense. As written, your example code does not even compile:
char *p_message;
*p_message = "Pointer";
The second line is a constraint violation, since the left-hand side has arithmetic type and the right-hand side has pointer type (actually, originally array type, but it decays to pointer type in this context). If you had written:
char *p_message;
p_message = "Pointer";
then the code is perfectly valid: it makes p_message point to the string literal. However, this may or may not be what you want. If on the other hand you had written:
char *p_message;
*p_message = 'P';
or
char *p_message;
strcpy(p_message, "Pointer");
then the code would be invoking undefined behavior by either (first example) applying the * operator to an invalid pointer, or (second example) passing an invalid pointer to a standard library function which expects a valid pointer to an object able to store the correct number of characters.
not needed, but is still recommended for a clean coding style.
Also the code you posted is completely wrong and won't work, but you know that and only wrote that as a quick example, right?
1.char str[] = "hello"; //legal
2.char str1[];
str1 = "hello"; // illegal
I understand that "hello" returns the address of the string literal from the string literal pool which cannot be directly assigned to an array variable. And in the first case the characters from the "hello" literal are copied one by one into the array with a '\0' added at the end.
Is this because the assignment operator "=" is overloaded here to support this?
I would also like to know other interesting cases wherein initialization is different from assignment.
You cannot think of it as overloading (which doesn't exist in C anyway), because the initialization of char arrays with string literals is a special case. The type of a string literal is const char[N], so if it were similar to overloading, you'd be able to initialize a char array with any expression whose type is const char[N]. But you cannot!
const char arr[3];
const char arr1[] = arr; //compiler error. Cannot initialize array with another array.
The language standard simply says that character arrays can be initialized with string literals. Since they say nothing about assignment, the general rules apply, in particular, that an array cannot be assigned to.
As for other cases when initialization is different from assignment: in C++, where there are references and classes, there would be zillions of examples. In C, with no full-fledged classes or references, the only other thing I can think of off the top of my head is const variables:
const int a = 4; //OK;
const int b; //Error;
b = 4; //Error;
Another example: array initialization with braces
int a[3] = {1,2,3}; //OK
int b[3];
b = {1,2,3}; //error
Same with structs
If you want to think of it as the operator being overloaded (even though C doesn't use the term), you can of course do that.
Do you also consider this to be overloading:
unsigned char x;
double y;
x = 2;
y = 1.243;
Those are assigning totally different types of data, after all, but using the "same operator", right?
It's just different, to be initializing or to be assigning.
Another big difference is that you used to be able to initialize structures, but there was no corresponding "struct literal" syntax for later assignments. This is no longer true as of C99, where we now have compound literals.
char str[] = "hello";
Is array initialization, using syntactic sugar defined in C because string initialization is so common. The compiler allocates some fixed memory in your program an initializes it. The name of the array (str) evaluates to the address of this memory, and it cannot be changed because there is no variable which holds that address.
Grijesh Chauhan explains more details of this.
Other cases depend on what you mean. Extending the current case, you can easily see that other initialized arrays have the same properties, for example
int a[] = { 1, 2, 3, 4 };
Array has non modifiable address. You need a pointer as a modifiable lvalue.
By assigning(trying) to a contant string literal, you are taking the address of it. Different address causes that illegality.
"hello" allocates some space in memory and gives and address. Then you take its address to initialize the array.