-1

For example whats the different between these two?

Example 1:

int *x;
x = malloc(sizeof(int));
*x = 42;

example 2:

int x = 42;

This probably seems like a really dumb question that has a basic answer but I'm just confused about pointers and trying to just understand the basics of it.

HKhan
  • 9
  • 1
  • 2
    heap vs stack. This is kind of a big topic so best to do some searching on C and memory management. – Garr Godfrey Jul 23 '20 at 01:52
  • `int x;` defines a value that holds an integer. The value lives on the stack (or might even be optimized out of memory entirely by the compiler in some cases). The stack is a convenient place to store data. `int *x;` defines a value that can point to another memory location that holds an integer. The _pointer_ is stored on the stack. It is just a number. The `malloc` call requests a new (heap) memory location to be reserved that's large enough to hold an integer. You store that location in the pointer `x`. When you use `*x`, you are accessing the integer at this other memory location. – paddy Jul 23 '20 at 02:03
  • Example 1, `sizeof x` is `sizeof(a_pointer)`, Example 2, `sizeof(int)`. Ex 1. `x` has *allocated storage duration*, good tor the life of the program or until freed, Ex 2 has *automatic storage duration* - only valid within the scope declared. A few links that provide basic discussions of pointers may help. [Difference between char *pp and (char*) p?](https://stackoverflow.com/a/60519053/3422102) and [Pointer to pointer of structs indexing out of bounds(?)...](https://stackoverflow.com/a/60639540/3422102) (don't let titles scar you -- very basic into to pointers in both) – David C. Rankin Jul 23 '20 at 06:09

2 Answers2

1

Simple question. Long, complex answer.

The chief difference between the two snippets has to with the objects that get allocated and their storage durations.

In the first case, you have two objects - the unnamed int object created by the malloc call that stores 42 and the pointer object x that stores the address of the int object:

   int *     int
   +–––+     +––––+
x: | +–+–––> | 42 |
   +–––+     +––––+

The unnamed int object that stores 42 has allocated storage duration - it hangs around until you explicitly deallocate it with the free library function. As written, pointer object x has to have auto storage duration, meaning its storage is released as soon as you exit its enclosing block or function. This is important to understand - the memory for x is released, but the memory for the int object it points to is not. If you lose track of the address for the dynamically allocated object, you lose the ability to deallocate it with free. This is called a memory leak, and is generally bad juju.

In the second case, you have the single int object x that stores the value 42:

   int
   +––––+
x: | 42 |
   +––––+

If x is declared at file scope, it has static storage duration, meaning its storage is reserved over the lifetime of the program. If declared in the body of a function, it has auto storage duration and its lifetime is limited to the lifetime of that function.


So, why pointers?

C requires us to use pointers in two circumstances:

  • when a function needs to write to a parameter (think scanf);

  • when we want to track dynamically allocated memory (as in your first snippet).

Why would we ever want to use dynamically allocated memory?

  • when we don’t know how much memory we need until runtime;

  • when we need to allocate a very large object;

  • when we want objects to persist beyond the lifetime of any individual function, but not over the lifetime of the entire program (as opposed to items declared static)

Pointers and dynamic memory are useful for creating any number of data structures (sometimes called containers) - lists, trees, queues, stacks, etc.

In practice, you wouldn’t use a pointer and dynamic memory as you do in your first snippet, precisely because it’s pointless. Instead, you would either be allocating an array of int or some other type, or an element in a container like a list or tree.

John Bode
  • 119,563
  • 19
  • 122
  • 198
0

This ...

int x = 42;

... declares an object of type int and initial value 42, identified by the symbol x. If it appears anywhere inside a function body then the object's lifetime ends when execution of the innermost containing block finishes. Otherwise, the object's lifetime is the whole execution of the program.

This ...

int *x;
x = malloc(sizeof(int));
*x = 42;

... declares an object of type int * with an indeterminate initial value, identified by the symbol x. It then attempts to allocate sufficient memory to store an int and assign x to point to that space, and then stores an int with value 42 in the allocated space via x. If it appears anywhere inside a function body (as the two assignment statements, at least, must do) then the pointer's lifetime ends when execution of the innermost containing block finishes. Otherwise, the pointer's lifetime is the whole execution of the program. Either way, the allocated object is separate, and its lifetime ends when it is deallocated or when the program terminates, whichever happens first.


Both approaches yield an object that can store an int, and both make it store the int value 42. They differ with respect to the lifetime of that object, and in the fact that the second alternative (given first in the question) additionally creates and assigns a value to another object, of type int *. Also, it is possible for malloc() to fail, returning NULL instead of a valid pointer, and robustness demands that you check for and handle that. And of course, the syntax for accessing the int object is different.

Generally speaking, you should avoid the second, indirect form unless you need one of the properties specific to pointers, among them

  • they can be used to access dynamically allocated objects. These, too, you should avoid unless you need one or more of their unique properties, among them

    • allocated objects live and keep their last-stored values until they are manually deallocated. In particular, they can outlive the function in which they were allocated, whereas the local variables of a function do not do so. However, it is exceedingly important here to understand that it is the (anonymous) allocated object that has such a lifetime, not any particular object storing a pointer to it.

    • the size required for an allocated object can be computed at run time, based on the program's input or details of its runtime environment. That is important when you want to store multiple objects, but you cannot statically predict a useful bound on how many.

    • In many C implementations, the maximum size of a dynamically allocated object is much larger than the maximum size of a local variable. Thus, if you need to store a very large object, and it cannot or should not be declared as a file-scope variable, then you may need to use a dynamically-allocated one.

  • Pointer objects can be reassigned to point to multiple different objects, both dynamically allocated and not, over their lifetimes. Accessing objects indirectly, via pointers, allows you to write code that does not need to know at compile time which object it is working on.

  • Pointers are sometimes much smaller than the objects they point to, so copying and exchanging pointers can be more efficient than copying and exchanging the objects to which they point.

  • Pointers can be used to access live local or file-scope variables whose identifiers are not in scope. For example, a function f can allow a different function, g to modify one of its local variables by passing a pointer to that variable as one of g's arguments. Although the variable's identifier is not in scope in g, it can still be accessed via the pointer value g receives as a parameter.

Be aware also that the places where you need a pointer are not all places where you need dynamic allocation. I have tried in this answer to be careful to distinguish between these related but distinct concepts. Fairly often, what you want or even need is a pointer to an ordinary local or file-scope variable, which you can obtain by use of the address-of operator (&), as opposed to a pointer to a dynamically allocated object.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157