4

new learner ; something puzzle about pointer;

As I learn from books, before using the pointer it must be initialized , so we usually use like this

int a = 12;

int * p = &a; 

so I understand why int* p = 12 is wrong ,because it has no address;

then I find something today while coding , That is from this :

char * months[12] = {"Jan", "Feb", "Mar", "April", "May" , "Jun", "Jul"    
,"Aug","Sep","Oct","Nov","Dec"};

Then another usually used situation came to my mind , That is :

char *p = "string"; (this is ok , why int * a = 12 can't be allowed ?)

I am puzzled. when is it initialized and how ? and why int * a = 12 can't be auto initialized ? maybe something about the arrange of memory.

Rakib
  • 7,435
  • 7
  • 29
  • 45
Fanl
  • 1,491
  • 2
  • 11
  • 15
  • char *p = "string"; is not ok! compilers will let you do it with a warning, but it's dangerous and will likely cause problems later. – thang May 24 '14 at 07:51
  • 4
    @thang Unfortunately, it is allowed but deprecated. `const char* p = "string";` is OK. – juanchopanza May 24 '14 at 07:53
  • `int *a=12` means set `a` (which is *typically* a memory address) to value 12 (not set the integer pointed by a to 12)... However string literals are already represented as a *constant* char array that can be casted to a *constant* char pointer... so you can assign them to a pointer, not as a copied string, but copying the reference to the existing string. – jsantander May 24 '14 at 07:53
  • 2
    "I understand why int* p = 12 is wrong ,because it has no address;" - No it is wrong because `12` **is not an address**. – WhozCraig May 24 '14 at 08:02
  • It's wrong because 12 is not a valid address on many systems. – Martin James May 24 '14 at 10:12
  • A pointer should be written with a valid address before it is dereferenced. – Martin James May 24 '14 at 10:16

9 Answers9

8

First off:

int a = 12;
int* p = &a;

This works because &a is a memory address.

int* p = 12;

This fails mostly because 12 is not a memory address. It's also true that 12, by itself, has no address, but this would be better reflected by a snippet like int* p = &12; (which wouldn't work, as you correctly noted).

An interesting property of pointers is that they are often used to designate the start of a list of values. For instance, take this array of integers:

int a[] = {1, 3, 7, 13};

It can trivially be turned into an integer pointer.

int* p = a; // magic!

The pointee is the first element of a, so *p == 1. Now, you can also do p[0] (which is 1, too), p[1] == 3, p[3] == 7, and p[4] == 13.

The reason char* foo = "bar" works is that "bar" is not a single value: it's a character array in disguise. Single characters are denoted by single quotes. As a matter of fact:

"bar"[0] == 'b'
"bar"[1] == 'a'
"bar"[2] == 'r'

The compiler has special support for string literals (quoted strings) that make it possible to assign them straight to pointers. For instance, char* foo = "bar" is valid.

A C99-compliant compiler also has support for array literals. For instance, int* p = (int [3]){1, 2, 3}; is valid. The character array and the int array will be given a global address, because the people who made C felt that it was a useful thing to do.

Pascal Cuoq
  • 79,187
  • 7
  • 161
  • 281
zneak
  • 134,922
  • 42
  • 253
  • 328
  • 3
    For completeness: `"bar"[3] == '\0'`. – Mankarse May 24 '14 at 08:14
  • 2
    “contrary to array literals”: The C language has had support for array literals for 15 years: http://stackoverflow.com/questions/5495983/c-compound-literals-pointer-to-arrays – Pascal Cuoq May 24 '14 at 08:16
  • Also, it's possible to take the address of an `int` literal in C++: `template int &as_value(T &&v) {return v;} ... &as_lvalue(12)`. – Mankarse May 24 '14 at 08:21
  • @PascalCuoq, your comment misses an awful lot of context. Array literals work and exist (which is unquestionable, and unquestioned by my answer) but cannot be assigned to pointers: they have to be assigned to arrays. – zneak May 24 '14 at 08:21
  • 2
    @zneak I believe you are missing the point of my comment. Please see https://ideone.com/08sVYR and tell me again that array literals cannot be assigned to pointers. – Pascal Cuoq May 24 '14 at 08:25
  • @PascalCuoq, if you disagree with the wording, be sure to let your knowledge shine through an edit. – zneak May 24 '14 at 08:31
  • 1
    *sigh*, the confusion of tagging a question as both C and C++, as if they are the same language... – Mankarse May 24 '14 at 08:33
  • 'This fails mostly because 12 is not a memory address.' Why not? It is actually valid on some of my embedded systems. – Martin James May 24 '14 at 10:14
  • @MartinJames, I wrote the answer with C++ in mind, where integers can't be implicitly casted to pointers. It produces a warning in C on Clang and GCC, so I feel that it's still a valid thing to say. – zneak May 24 '14 at 15:23
3

int* p = 12 is wrong because the assigned value may or may not belongs to memory address. You are forcing p to point at that location.
char *p = "string" is allowed because compiler already has set the space for the string and p is pointing to the first character of that string.

haccks
  • 104,019
  • 25
  • 176
  • 264
3

It comes down to types.

In both C and C++, the type of a plain integer literal like 12 is int. There is no implicit conversion from the type int to the type int*, which makes sense: a pointer and an integer are, conceptually, completely different things. So int *p = 12; is invalid.

In C, a plain string literal like "abc" is translated into a static array of chars (of size exactly sufficient to store abc plus a terminating null char). The type "array of chars" is implicitly convertible to the type char* (pointer to char) - arrays are said to decay into pointers. So the assignment char *p = "abc"; is valid.

But there's a catch: it's undefined behavior to modify that array (both in C and C++). That conversion is in fact deprecated (or even illegal) in C++, and you should use const char * instead.

Mat
  • 202,337
  • 40
  • 393
  • 406
2

In reality the gcc compiler will warn you about:

char* p = "hello";

This is because "hello" is now treated as an equivalent to const char*.

so this would be better:

const char* p = "hello";

But yes as other people have described, "hello" has an address which points to the start of a fixed sequence of characters.

0

The language has made an exception and allows string literals to be used to initialize char const*. Some compilers are less strict and allow string literals to be used to initialize char* as well.

Update, in response to comment by Pascal Cuoq

In C and C++, the following are valid ways to initialize variables using string literals:

char carr[] = "abc";
char carr[10] = "abc";
char const* cp = "abc";

The following are valid ways to initialize variables using integer literals in an initializer list:

int iarr[] = {1, 2, 3};
int iarr[10] = {1, 2, 3};

However, the following is not a valid way to initialize a variable using integer literals in an initializer list:

int const* ip = {1, 2, 3};

That's what I meant when I said the language has made an exception and allows string literals to be used to initialize char const*.

R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • 1
    What language are you talking about? Why should using string literals to initialize a `char const *` be an exception? A string literal is an expression and it behaves just like any other expression when it is used as an initializer, there is no “exception” there in either C or C++. – Pascal Cuoq May 24 '14 at 08:14
  • @PascalCuoq, hope my update to the answer explains my thought, and more importantly, if you think I am mistaken, please send me some references that explain why. – R Sahu May 24 '14 at 16:16
  • You can initialize or even assign a pointer with an array literal. You just have to use the correct C99 syntax. Please see the other answer. – Pascal Cuoq May 24 '14 at 16:20
  • @PascalCuoq, thanks for the pointer. I didn't know that feature of C99. My point is still valid for C++ though. Don't you agree? – R Sahu May 24 '14 at 16:31
  • @RSahu `{1,2,3}` is NOT an integer literal. It's a brace-enclosed initializer. – M.M May 31 '14 at 04:36
0

int* p = 12 is wrong in the sense that it does something that is almost definitely not what you think it does. Assuming that your compiler doesn't complain that you are trying to implicitly cast an int into a int *, this is not illegal. What you did was, point p at memory location 12, which is almost definitely something you shouldn't be reading. The assignment is legal, but if you dereference that pointer you are in undefined behavior territory. If you are in user mode, then *(int*)12 is probably a segmentation fault.

KevinZ
  • 3,036
  • 1
  • 18
  • 26
0

Using C terminology, the difference between these two cases is that string literals exist, which are an array of char (and thus an lvalue, so you can take their address or point to them); but in C90 there are no other literals. 12 is an integer constant, not a literal. You can't do &(12), because the language says so. Brace-enclosed initializer lists are not values. Constants are rvalues; literals are lvalues.

In C++ the behaviour is the same, however C++ uses different terminology for the same thing. In C++, constants are all called "literals", however they are also all rvalues (except for string literals) so you cannot take their address.

C99 added array literals of other types.

M.M
  • 138,810
  • 21
  • 208
  • 365
0

Many programmers are confused by the syntax of specifying a pointer.

int*  p;
int  *p;
int * p;

All of the above declare the same thing: a pointer, p, which either be NULL or the address of an integer-size storage unit in memory.

Thus

int * p = 12;

declares a pointer, p, and assigns it the value 12.

In C and C++ pointers are just variables with special meaning to the compiler such that you are allowed to use special syntax to access the memory location whose value they hold.

Think about this a different way for a moment.

Think of the number "90210". That could be your bank balance. It could be the number of hours since you were born.

Those are all simple "integer" interpretations of the number. If I tell you that it is a Zip Code - suddenly it describes a place. 90210 isn't Beverly Hills in California, it is the [postal] address of Beverly Hills in California.

Likewise, when you write

int * p = 12;

you are saying that "12" is the address of an integer in memory, and you're going to remember that fact in the pointer-variable p.

You could write

int * p = &12;

This will force the compiler to generate an integer storage unit in the program executable containing the native integer representation of 12, and then it will generate code which loads the address of that integer into the variable p.

char* p = "hello";

is very different.

12;      // evaluates to an integer value of 12
"hello"; // evaluates to an integer value which specifies the location of the characters { 'h', 'e', 'l', 'l', 'o', 0 } in memory, with a pointer cast.

int i = 12; // legal
char h = 'h'; // legal
const char* p = "hello"; // legal
uintptr_t p = "hello"; // legal

The double-quotes in C and C++ have a special meaning, they evaluate to a pointer to the string contained in them, rather than evaluating to the string itself.

This is because

"The quick brown fox jumped over the lazy dog"

wouldn't fit into a CPU register (which are 32 or 64 bits depending on your machine). Instead, the text is written into the executable and the program instead loads the address of it (which will fit into a register). And this is what in C/C++ we call a pointer.

kfsone
  • 23,617
  • 2
  • 42
  • 74
0

I have found on stackoverflow one good hint: always initialize declared pointer with NULL. This will help understanding that newly created pointer cannot be used without further actions.

So the actions that should follow have to assign proper address to the pointer (initialize that pointer).

To do what was probably your intention with original code

int *p = 12;

you have to do for example:

int *p;
p = malloc(sizeof(p));  /* pointer p holds address of allocated memory */
*p = 12;

or another example:

int *p;
const int a = 12;
p = &a;            /* pointer p holds address of variable a */

Why char *p = "string"; is correct was answered by others above. This is just another way of pointer initialization.

Also, similarly, in one of answers here on stackoverflow I have found really nice option for your case:

int *p = &(int){12};
Sany
  • 103
  • 2
  • 8