目录 ← 首页
CS61C

Introduction

Learning Outcomes

  • Learn characteristics of the C memory layout.
  • Differentiate between storage allocation and variable declaration.
  • Know where variables are stored in C, i.e., which memory segment data is allocated in.

C Memory Layout

So far, we have discussed how different data types occupy space (i.e., how the compile-time operator sizeof works with different declarations of local variables), but we haven’t discussed where all C things live in memory.

Memory allocation

In C, there are three ways of allocating storage space in memory:

  • Local variable declaration: To specify a local variable, declare it inside a function, e.g., in main(). Local variables are accessible within the function they are declared in; put another way, a C compiler will only recognize a local variable in its function’s namespace. These are declared and allocated at compile-time (hence why sizeof works).
  • Global variable declaration: Global variables are declared outside a function. Variables declared in the global namespace are accessible to every subroutine (main(), foo(), etc.). Warning: Do not overuse global variables! While they are useful for accessing shared constants or big tables, you can write the worst code in the world by having a billion globals: you risk accidental edits by different routines, and you remove the power of abstraction between functions. Global variables are declared and allocated at compile-time.
  • Dynamic memory allocation: Sometimes we won’t know how much space we need until we start running a program. For example, we may need to load in data from a file, and we only know how much data is in the file until we read all of its bytes. In this case, we can dynamically allocate memory storage at runtime by calling allocation functions: malloc is a common one. Read more when we discuss heaps.

Based on the descriptions above, note that storage allocation involves setting aside a block of memory to put data in. Variable declaration means allocating a block of memory and also specifying a name with which to refer to that memory. Because variables are typed, variable declaration implicitly specifies the size of the memory block to allocate. While variable declaration always implies storage allocation, the converse is not true (e.g., with dynamic memory allocation).

Four regions of memory layout

A C program’s address space contains 4 regions as shown in #fig-c-mem-layout.

Diagram of the C address space with text at the lowest addresses, data above text, heap above data growing upward, and stack at high addresses growing downward. The shaded gap between heap and stack indicates free space available for runtime growth.

C program memory layout.

Memory SegmentContentsMemory Management
Stacklocal variables, parameters, and return addresses 1automatic; grows downward2
Heapdynamically allocated storageresizes on demand (e.g. malloc to allocate storage, free to free storage); grows upward3
Data (aka Static)4global variablessize is fixed (“static”) throughout the whole program
Text (aka Code)5program codesize is fixed throughout the whole program; data is loaded when program starts

The names of these four memory segments are hard to memorize at first, so we apologize on behalf of all computer scientists. While the stack operates very much like the stack data structure you learned in a Data Structures course, the heap is NOT a heap data structure; it is just a “heap of memory.” Also, everything is technically data, but the data segment specifically refers to global data4. Finally, the text can be remembered because program code should be read-only data, just like how many texts you read in real life are read-only.

Programming in C requires knowing where data is in memory6. Otherwise things don’t work as expected. In particular, memory in each of the four regions is managed differently. The biggest source of bugs in C is from incorrect assumptions about memory. This lecture is all about learning how memory is managed in order to avoid common bugs. We focus our discussion on the stack and the heap.

Footnotes

  1. Because parameters and return addresses are critical to function call and return, they are often stored directly in the CPU where possible—on special hardware called registers (which we talk about later). Because there are only a limited number of such registers, additional parameters and return addresses are stored in memory on the stack until they are needed.

  2. Read more about the stack.

  3. Read more about the heap.

  4. We refer only to the .data segment in this course and ignore read-only data. Read more on Wikipedia. 2

  5. Read more on Wikipedia.

  6. By contrast, Java and Python both hide locations of objects.