What would happen with x = 51 after declaring int *x; Would just a '51' be stored in the pointer, so it'd be pointing to some error "51" location?
Essentially, yes. x
is a variable like any other, and you can assign new values to it like any other. The expression 51
will be converted from type int
to type int *
, and the result will be assigned to x
.
51
is most likely not a valid pointer value, so attempting to dereference it will likely lead to a runtime error. Valid pointer values are obtained by either applying the unary &
operator to an lvalue1, by calling one of malloc
, calloc
, or realloc
, or by using an array expression in some circumstances2.
What happens if you say &x ? would it just print the memory location that the pointer is sitting in?
The expression &x
will evaluate to the location of the object x
, and the type of the resulting value will be int **
(pointer to pointer to int
).
A function declaration is function(int *x) but calling the function the form is function(&x). Why?
Because, for any object x
of type T
, the expression &x
yields a value of type T *
:
void foo( T *ptr ) // for any type T
{
*ptr = new_value(); // write a new value to the object ptr
} // points to
void bar( void )
{
T var;
foo( &var ); // foo updates the value of var
}
In the code above,
ptr == &var // T *
*ptr == var // T
This is true for any type, including pointer types. If we replace the type T
with a pointer type P *
, that code above becomes
void foo( P **ptr )
{
*ptr = new_value(); // write a new value to the thing ptr points to
}
void bar( void )
{
P *var;
foo( &var ); // write a new value to var
}
This code is exactly the same as the code above - the only thing that has changed is the types:
ptr == &var // P **
*ptr == var // P *
**ptr == *var == some_object // P
malloc() I dont understand "WHY" it works, I understand what it does however. Since in the C library definition it returns a "pointer" to the requested memory.
x = (int *) malloc (sizeof (int)); is putting a pointer in x......but x is a pointer already? So is it pointing to another pointer?
No. x
is an object that stores a pointer value; malloc
is a function that returns a pointer value.
I've also seen it written like this x = malloc(sizeof(int)); .....whats the difference?
The (int *)
is a cast expression - it tells the compiler to treat the result of malloc
as a pointer to int
. As of the 1989 version of the language, that cast is no longer necessary, and its use is discouraged. IMO, the proper way to write a malloc
call is
T *p = malloc( sizeof *p * number_of_elements );
This works because malloc
returns void *
, which is a "generic" pointer type - a void *
value can be converted to any other pointer type (and vice versa) without a cast3.
Prior to the 1989 standard, malloc
returned char *
, which did require an explicit cast to assign the result to a different pointer type.
5: The ** operator, I'd assume it means "pointer to a pointer"....but I guess maybe I don't understand when/why this would come into play?
Multiple indirection (pointers to pointers, pointers to pointers to pointers, etc.) is a common thing. I showed one example above, where a function needs to write a new value to a parameter of pointer type. It's rare to see more than two or three levels of indirection, though.
EDIT
Some actual code might help. Here's a small example that creates two variables, one an int
, and the other an int *
. We start out with the pointer initialized to NULL
(a well-defined "nowhere" value, guaranteed to compare unequal to any valid pointer value), then we set it to point to another integer variable, then we set it to point to memory allocated by malloc
. I've added calls to a utility I wrote to display the contents of each object:
#include <stdio.h>
#include <stdlib.h>
#include "dumper.h"
int main( void )
{
int x = 10;
/**
* Start with ptr pointing "nowhere"
*/
int *ptr = NULL;
char *names[] = {"x", "ptr", "unnamed"};
void *addrs[] = {&x, &ptr, NULL };
size_t sizes[] = { sizeof x, sizeof ptr, 0 };
printf( "ptr is currently NULL\n\n" );
dumper( names, addrs, sizes, 2, stdout );
/**
* Set ptr to point to x
*/
ptr = &x; // int * = int *; use the & operator to obtain the location of x
printf( "ptr currently points to x\n\n" );
dumper( names, addrs, sizes, 2, stdout );
ptr = malloc( sizeof *ptr * 10 ); // int * = void *; use malloc to set aside
if ( ptr ) // dynamic memory and obtain its location;
{ // in C, you can assign a void * to an int * without a cast
for ( size_t i = 0; i < 10; i++ )
ptr[i] = (int) i;
}
addrs[2] = ptr;
sizes[2] = sizeof *ptr * 10;
printf( "ptr currently points to dynamically allocated memory\n\n" );
dumper( names, addrs, sizes, 3, stdout );
free( ptr );
return 0;
}
When built and run on my system (SLES-10 on x86-64, gcc 4.1.2), I get the following output:
$ ./pointer_example
ptr is currently NULL
Item Address 00 01 02 03
---- ------- -- -- -- --
x 0x7fff89aeb304 0a 00 00 00 ....
ptr 0x7fff89aeb2f8 00 00 00 00 ....
0x7fff89aeb2fc 00 00 00 00 ....
ptr currently points to x
Item Address 00 01 02 03
---- ------- -- -- -- --
x 0x7fff89aeb304 0a 00 00 00 ....
ptr 0x7fff89aeb2f8 04 b3 ae 89 ....
0x7fff89aeb2fc ff 7f 00 00 ....
ptr currently points to dynamically allocated memory
Item Address 00 01 02 03
---- ------- -- -- -- --
x 0x7fff89aeb304 0a 00 00 00 ....
ptr 0x7fff89aeb2f8 10 20 50 00 ..P.
0x7fff89aeb2fc 00 00 00 00 ....
unnamed 0x502010 00 00 00 00 ....
0x502014 01 00 00 00 ....
0x502018 02 00 00 00 ....
0x50201c 03 00 00 00 ....
0x502020 04 00 00 00 ....
0x502024 05 00 00 00 ....
0x502028 06 00 00 00 ....
0x50202c 07 00 00 00 ....
0x502030 08 00 00 00 ....
0x502034 09 00 00 00 ....
Before going over that in detail, remember that x86-64 is little-endian, so multi-byte objects are stored starting with the least-significant bit. This also means that objects that span multiple 32-bit words are stored starting with the least-significant word.
In short, read the memory dumps for each object from right to left, bottom to top.
Starting with the first section:
Item Address 00 01 02 03
---- ------- -- -- -- --
x 0x7fff89aeb304 0a 00 00 00 ....
ptr 0x7fff89aeb2f8 00 00 00 00 ....
0x7fff89aeb2fc 00 00 00 00 ....
The object x
is stored starting at location 0x7fff89aeb304
; on my system, objects of type int
take up 4 bytes. The value stored at x
is 10
(0x0000000a
; again, read the memory dump from right to left).
The object ptr
is stored starting at location 0x7fff89aeb2f8
; on my system, objects of type int *
take up 8 bytes4. The value stored at ptr
is NULL
(0).
After setting ptr
to point to x
, we get the following:
Item Address 00 01 02 03
---- ------- -- -- -- --
x 0x7fff89aeb304 0a 00 00 00 ....
ptr 0x7fff89aeb2f8 04 b3 ae 89 ....
0x7fff89aeb2fc ff 7f 00 00 ....
The value stored at ptr
is now 0x7fff89aeb304
, which is the address of x
. Right now, the expressions x
and *ptr
would yield the same value (10
).
Finally, we allocate space for 10 integers using malloc
and set ptr
to point to the first element in that sequence:
Item Address 00 01 02 03
---- ------- -- -- -- --
x 0x7fff89aeb304 0a 00 00 00 ....
ptr 0x7fff89aeb2f8 10 20 50 00 ..P.
0x7fff89aeb2fc 00 00 00 00 ....
unnamed 0x502010 00 00 00 00 ....
0x502014 01 00 00 00 ....
0x502018 02 00 00 00 ....
0x50201c 03 00 00 00 ....
0x502020 04 00 00 00 ....
0x502024 05 00 00 00 ....
0x502028 06 00 00 00 ....
0x50202c 07 00 00 00 ....
0x502030 08 00 00 00 ....
0x502034 09 00 00 00 ....
Hopefully, the pattern should be obvious by now - ptr
stores the value 0x502010
, which is the address of the first element of the dynamically-allocated sequence.
- An lvalue is an expression that refers to a region of memory such that the memory may be read or written to. A variable is an lvalue, but other expressions may be lvalues as well.
- Except when it is the operand of the
sizeof
or the unary &
, or is a string literal used to initialize a character array in a declaraction, an expression of type "N
-element array of T
" will be converted ("decay") to an expression of type "pointer to T
", and the value of the expression will be the address of the first element of the array.
- This is not the case in C++, however; a call to
malloc
in C++ does require a cast, but you shouldn't use malloc
in C++ code anyway.
- Pointers to different types *may* have different sizes and representations, although I think x86-64 uses 8 bytes for all pointer types.