1
int *x;        Declares pointer which holds an ADDRESS to a location in memory
*x = 51;       * is de-reference operator. I'm saving 51 at that address.
&x;            & is address of operator, so &x would probably print some memory location

What I don't understand:

  1. 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?

  2. What happens if you say &x ? would it just print the memory location that the pointer is sitting in?

  3. A function declaration is function(int *x) but calling the function the form is function(&x). Why?

  4. 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?

    I've also seen it written like this x = malloc(sizeof(int)); .....whats the difference?

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?

clearlight
  • 12,255
  • 11
  • 57
  • 75
msmith1114
  • 2,717
  • 3
  • 33
  • 84
  • `*x = 51; //* is [...] and putting in 42` I get that you are confused about pointers, but I'm pretty sure it puts 51 :) – Fureeish Feb 04 '17 at 23:34
  • Possible duplicate of [What are the barriers to understanding pointers and what can be done to overcome them?](http://stackoverflow.com/questions/5727/what-are-the-barriers-to-understanding-pointers-and-what-can-be-done-to-overcome) – alain Feb 04 '17 at 23:44

4 Answers4

4

Answers:

  1. Yes.

  2. print("%p", &x); will display the address of x.

  3. Function is declared as accepting a pointer (e.g. memory address). Therefore, when you call it you have to pass a pointer. The address & of x is a pointer. Or you could declare int z = 10, *y = &z; and then function(y) call would work.

  4. x is declared as the location of memory that can store a pointer. malloc() returns the address (pointer to) memory it allocated. When you say x = malloc(n) you're indicating it should store the address of the allocated memory at the location x, which is declared as memory that stores a pointer.

  5. A common use of ** is when you want a function to save the address of memory it allocated to a caller's pointer. Example:


// Example of a common use of pointer to pointer (**)

#include <stdio.h>
#include <stdlib.h>

void mymsg(char **pointer_to_pointer) {
   char *mem = malloc(NBYTES);
   if (mem == NULL) {
       fprintf(stderr, "out of memory\n")
       exit(-1);
   }
   strcpy(mem, "This is some data, also see strdup() man page");
   *pointer_to_pointer = mem;  // Store the pointer mem at the callers pointer 
                               // storage area, pointed to by 
                               // pointer_to_pointer
} 

int main() {
       char *memptr = NULL; // Pointer variable (stores  of chars)

       mymsg(&memptr);      // Pass address `*` of our pointer `*` (i.e. **)
                            // mymsg() will store pointer to allocated  
                            // memory in memptr (which is our " holder" 
                            // location).

       printf("text: %s\n", memptr);  // Displays text in memory pointed to
                                      // to by memptr. That memory was allocated
                                      // in mymsg(), but that's irrelevant
                                      // now. It doesn't matter where the memory
                                      // was allocated. We have the only
                                      // pointer to it now. mymsg()'s
                                      // variables were all stack local
                                      // and were destroyed when it returned.

       free(memptr);        // We can free the memory memptr points to now.
                            // No where else has or needs it at this point.

       memptr = NULL;       // Do this so we always know this pointer doesn't
                            // point to valid memory anymore. Because
                            // once the memory is freed contents must be
                            // considered garbage. If you try to free()
                            // memory at that address more than once
                            // it breaks the memory allocator, introducing
                            // nasty difficult to diagnose bug(s).
                            // Most modern C environments make freeing NULL
                            // a NOP (i.e. safe) so you can reduce risk of 
                            // of double-free bugs if you set pointers to null
                            // after freeing them, however, if you keep multiple
                            // pointers to the same memory there's
                            // still risk you might free the same memory 
                            // more than once via another pointer if you're
                            // not careful.
}
clearlight
  • 12,255
  • 11
  • 57
  • 75
  • Question: How come in functions (like fopen/fclose) they take a pointer as they're type (like int fclose(FILE *stream) ). But when we call the function we are simple passing just fp instead of &fp. (pretend we declared FILE *fp earlier). Wouldn't we need to pass the address of fp? – msmith1114 Feb 05 '17 at 06:14
  • @msmith1114 `FILE *fp;` declares a file pointer. `fopen()` returns a `FILE *`. `fclose()`, and `fprintf()` accept `FILE *` as their parameter, so there there is no inconsistency. The address of a `FILE` struct is getting passed around in all cases. Don't get `FILE *` types associated with C stdio I/O functions confused with the UNIX fd (file descriptor) type which is an int or long. – clearlight Feb 05 '17 at 06:30
  • So by default the FILE type is always an address when it's getting passed around then? – msmith1114 Feb 05 '17 at 08:56
  • @msmith1114 yes. Study stdio.h. All related API interfaces there pass it as `FILE *` which is an [opaque pointer](https://en.wikipedia.org/wiki/Opaque_pointer). If you find C code where the address of a `FILE *` (e.g. `FILE **`) is declared, or sent to a function, such as `FILE *foo; myfunc(&foo)`, it's likely doing something like what is shown in the code example above. But that's not how C Standard I/O API does it. `fopen()` and `popen()`, for example, give a `FILE *` to the caller as the function *return value*. Otherwise you *would* need to pass `FILE **` as a parameter to receive it. – clearlight Feb 05 '17 at 14:01
3
  1. A pointer is a variable whose value is the address of another variable. So, yes, if you do x = 51, you're saying the address is 51, most likely corrupting memory (unless you're sure the address 51 is actually correct)

  2. Yes. You could use printf("Address of variable: %x\n", &x); and see the address.

  3. In the declaration you're saying "I expect a pointer" and when you call it, well, you want to pass the pointer - remember, the pointer is just the address of another variable.

  4. The short answer is you don't need to cast at all b/c there are no benefits, only potential risks. See here for more details.

  5. Yes, it means pointer to a pointer. One example of where this comes in handy is an array of strings: each string is a collection of chars, the first of which is the "first" pointer. Then you have a secondary pointer to this collection of pointers.

Side note, see if you grab a copy of The C Programming Language, it's very well written and I think it might help.

Community
  • 1
  • 1
avip
  • 1,445
  • 13
  • 14
3

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?

It would do that, yes. If you tried to dereference it, you would get a segmentation fault.

What happens if you say &x ? would it just print the memory location that the pointer is sitting in?

Yes.

A function declaration is function(int *x) but calling the function the form is function(&x). Why?

With int *x you declare that you're taking a memory address as an argument. With &x you're getting the memory address of x, which you're passing to the function.

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.

I can see why you don't understand it, your current idea of it is a bit vague.

The full definition of malloc is void *malloc(size_t size). It works by telling the operating system to allocate size bytes of memory for you. Once it has done that, it will return the memory address of the beginning of the allocated block of memory in form of a pointer. This is the opposite of stack allocation, being heap allocation, but I won't go into detail on the difference.

To clarify, void* more or less means "pointer to anything", which means you can cast the pointer to the new block of memory (explicitly or implicitly) to become a pointer to any data type. This means you could treat 4 allocated bytes as an int, since an int is (usually) 4 bytes big, for example.

x = (int *) malloc (sizeof (int)); is putting a pointer in x......but x is a pointer already? So is it pointing to another pointer? I've also seen it written like this x = malloc(sizeof(int)); .....whats the difference?

I've explained part of this already in the previous paragraph.

In C++, you need to cast the void* to int* explicitly, meaning you need to put (int*) in front. However, C is glad to do this cast implicitly, meaning it will be put there for you, which means both variants are valid depending on the language.

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?

Yes, it's a pointer to a pointer (a double pointer). You can even have a triple pointer with ***. You can dereference "multi-pointers" like these fully with the same amount of asterisks (e.g. **a).

You would use this when you need to reassign a heap-allocated variable from within a function, for example. Here's some code:

object *a = malloc(sizeof(object));

void reallocate_object(object **b) {
    // Doing *b = x here is the same as doing a = x directly.
    *b = malloc(sizeof(object));
}

reallocate_object(&a);
Emily
  • 1,030
  • 1
  • 12
  • 20
  • Your example really confused the heck out of me for a second. but let me make sure I got this straight: *a is holding the memory location of the new block of memory. we are passing the address of a (a pointer) to the function, so it's basically a pointer to a pointer to the b variable. In the function we are dereferencing the pointer to the pointer (so a) and making it point to a new memory block? question is, when we say **b.....we haven't created a pointer to a pointer? So how does that work. And when you dereference a pointer to a pointer. Does it follow the whole path? – msmith1114 Feb 05 '17 at 05:09
  • You have to keep in mind that pointers are variables in memory on their own. We're not creating `**b`, but `*b` still points to `a`, which points to something else, which is what we're changing. Essentially, `*b` is the same as saying `a`, `**b` is the same as `*a`. – Emily Feb 05 '17 at 15:02
2

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.


  1. 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.
  2. 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.
  3. 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.
  4. Pointers to different types *may* have different sizes and representations, although I think x86-64 uses 8 bytes for all pointer types.

John Bode
  • 119,563
  • 19
  • 122
  • 198
  • "No. x is an object that stores a pointer value; malloc is a function that returns a pointer value." which is an memory address correct? – msmith1114 Feb 06 '17 at 15:11
  • 1
    @msmith1114: A pointer is an *abstraction* of a memory address - yes, it evaluates to the location of an object in storage, but it also carries some additional type information with it. – John Bode Feb 06 '17 at 15:29
  • I guess it makes sense, regarding the function and passing &x for a pointer data type. It's just difficult at first to wrap your mind around. It's the same as saying function(int x), well we don't pass the type but the value that would go into x. Just like function(int *x) we wouldn't pass the type but the value we want for x....and the value for a pointer is a memory address...which is &y or whatever we would pass. – msmith1114 Feb 06 '17 at 16:21
  • @msmith1114: Yup. Pointers in an of themselves are simple beasties (something that evaluates to the location of another object), but pointer syntax isn't the most straightforward, at least not at first. It takes time, but I promise it will make sense eventually. – John Bode Feb 06 '17 at 16:26
  • Also your walkthrough was very helpful thank you! I think if I just remember a pointer is just an address it makes more sense. But when functions are returning pointers it for some reason was confusing to me when I didn't make the association that they are just passing back an address – msmith1114 Feb 06 '17 at 20:00