1

I'm a beginner to the C programming language. I sort of understand the general definition of stack memory, heap memory, malloc, pointers, and memory addresses. But I'm a little overwhelmed on understanding when to use each technique in practice and the difference between them.

I've written three small programs to serve as examples. They all do the same thing, and I'd like a little commentary and explanation about what the difference between them is. I do realize that's a naive programming question, but I'm hoping to connect some basic dots here.

Program 1:

void B (int* worthRef) {
  /* worthRef is a pointer to the
     netWorth variable allocated
     on the stack in A.
  */
  *worthRef = *worthRef + 1;
}

void A() {
  int netWorth = 20;
  B(&netWorth);

  printf("%d", netWorth);  // Prints 21
}

int main() {
  A();
}

Program 2:

int B (int worthRef) {
  /* worthRef is now a local variable. If
     I return it, will it get destroyed
     once B finishes execution?
  */
  worthRef = worthRef + 1;
  return (worthRef);
}

void A() {
  int netWorth = 20;
  int result = B(netWorth);

  printf("%d", result);  // Also prints 21
}

int main() {
  A();
}

Program 3:

void B (int* worthRef) {
  /* worthRef is a pointer to the
     netWorth variable allocated on
     the heap.
  */
  *worthRef = *worthRef + 1;
}

void A() {
  int *netWorth = (int *) malloc(sizeof(int));
  *netWorth = 20;

  B(netWorth);

  printf("%d", *netWorth);  // Also prints 21

  free(netWorth);
}

int main() {
  A();
}

Please check my understanding:

  • Program 1 allocates memory on the stack for the variable netWorth, and uses a pointer to this stack memory address to directly modify the variable netWorth. This is an example of pass by reference. No copy of the netWorth variable is made.
  • Program 2 calls B(), which creates a locally stored copy of the value netWorth on its stack memory, increments this local copy, then returns it back to A() as result. This is an example of pass by value.
    • Does the local copy of worthRef get destroyed when it's returned?
  • Program 3 allocates memory on the heap for the variable netWorth, variable, and uses a pointer to this heap memory address to directly modify the variable netWorth. This is an example of pass by reference. No copy of the netWorth variable is made.

My main point of confusion is between Program 1 and Program 3. Both are passing pointers around; it's just that one is passing a pointer to a stack variable versus one passing a pointer to a heap variable, right? But in this situation, why do I even need the heap? I just want to have a single function to change a single value, directly, which I can do just fine without malloc.

The heap allows the programmer to choose the lifetime of the variable, right? In what circumstances would the programmer want to just keep a variable around (e.g. netWorth in this case)? Why not just make it a global variable in that case?

Alureon
  • 179
  • 1
  • 3
  • 14
  • 1
    *pass by reference* does not exist in C. Also, replace `int *netWorth = (int *) malloc(sizeof(int));` with `int *netWorth = malloc(sizeof(*netWorth));` – unalignedmemoryaccess Mar 24 '19 at 20:03
  • There are no "allocates memory on stack or heap". There are only storage durations: static, allocated, thread-local and global. The compiler is free to do as it pleases - most importantly your `malloc`ated object might not be ever allocated on heap. – Antti Haapala -- Слава Україні Mar 24 '19 at 20:06
  • @tilz0R: The term 'pass by reference' is commonly used in C for this kind of thing where a pointer is passed to a function. Its totally different from 'pass by reference' in C++, but C (and the term) existed long before C++ did. – Chris Dodd Mar 24 '19 at 20:12
  • You might be interested in [the difference between scope and lifetime](https://stackoverflow.com/questions/7632120/scope-vs-life-of-variable-in-c) – Chris Dodd Mar 24 '19 at 20:20

2 Answers2

1

I sort of understand the general definition of stack memory, heap memory, malloc, pointers, and memory addresses... I'd like a little commentary and explanation about what the difference between them is...

<= OK...

Program 1 allocates memory on the stack for the variable netWorth, and uses a pointer to this stack memory address to directly modify the variable netWorth. This is an example of pass by reference.

<= Absolutely correct!

Q: Program 2 ... Does the local copy of worthRef get destroyed when it's returned?

A: int netWorth exists only within the scope of A(), whicn included the invocation of B().

Program 1 and Program 3 ... one is passing a pointer to a stack variable versus one passing a pointer to a heap variable. Q: But in this situation, why do I even need the heap?

A: You don't. It's perfectly OK (arguably preferable) to simply take addressof (&) int, as you did in Program 1.

Q: The heap allows the programmer to choose the lifetime of the variable, right?

A: Yes, that's one aspect of allocating memory dynamically. You are correct.

Q: Why not just make it a global variable in that case?

A: Yes, that's another alternative.

The answer to any question "Why choose one design alternative over another?" is usually "It depends".

For example, maybe you can't just declare everything "local variable" because you're environment happens to have a very small, limited stack. It happens :)

In general,

  1. If you can declare a local variable instead of allocating heap, you generally should.

  2. If you can avoid declaring a global variable, you generally should.

paulsm4
  • 114,292
  • 17
  • 138
  • 190
1

Checking your understanding:

  • Program 1 allocates memory on within the function stack frame of A() for the variable netWorth, and passes the address of netWorth as a pointer to this stack memory address to function B() allowing B() to directly modify the value for the variable netWorth stored at that memory address. This is an example of pass by reference passing the 'address of' a variable by value. (there is no pass by reference in C -- it's all pass by value) No copy of the netWorth variable is made.
  • Program 2 calls B(), which creates a locally stored copy of the value netWorth on its stack memory, increments this local copy, then returns the integer back to A() as result. (a function can always return its own type) This is an example of pass by value (because there is only pass by value in C).

(Does the local copy of worthRef get destroyed when it's returned? - answer yes, but since a function can always return its own type, it can return the int value to A() [which is handled by the calling convention for the platform])

  • Program 3 allocates memory on the heap for the variable netWorth, and uses a pointer to this heap memory address to directly modify the variable netWorth. While "stack/heap" are commonly used terms, C has no concept of stack or heap, the distiction is that variables are either declared with Automatic Storage Duration which is limited to the scope within which they are declared --or-- when you allocate with malloc/calloc/realloc, the block of memory has Allocated Storage Duration which is good for the life of the program or until the block of memory is freed. (there are also static and thread local storage durations not relevant here) See: C11 Standard - 6.2.4 Storage durations of objects This is an example of pass by reference. (It's all pass by Value!) No copy of the netWorth variable is made. A pointer to the address originally returned by malloc is used throughout to reference this memory location.
David C. Rankin
  • 81,885
  • 6
  • 58
  • 85