Skip to content
This repository has been archived by the owner on Jan 12, 2022. It is now read-only.

L15 [Continuing With C]

Richard Luo edited this page Dec 14, 2021 · 8 revisions

L15 [Continuing With C]

C Compiler

  1. Preprocessor takes C source code and turns it into Preprocessed source code
  • Expands #include statements
  1. Compiler takes Preprocessed Source Code and turns it into Object Files
  2. Linkers links the Object Files and Library Files and turns it into Machine Language Executables

Memory Layout

  • Text
    • Program code (unmodifiable)
    • Constant data (maybe)
  • Data
    • Constant data (maybe)
    • Initialized variables
  • BSS
    • Uninitalized static data
    • From "Block Starting With Symbol"
    • .blkw from LC-3

Table

  1. Function - program text
  2. Global variables - data segment
  3. Static variables - data segment
  4. Local variables - stack
  5. Dynamically allocated - heap

Scopes

  • static means visible to no other files
  • extern means visible to other files - declares reference to an external definition elsewhere
  • <no keyword> means External definition (visible to other files)

From something like file1.c

int a[10];      //External definition
static struct r *p;  //This file only
extern float c[100]; //ref to c in file2.c

And in file2.c

extern int a[10];       // Ref to arr in file1.c
static struct r *p;      // NOT the p in file1
float c[100];           // Definition

Storage Class Specifiers

Storage class tells us where the data will be stored and who will be able to see it

Remember: it is NOT part of the type

The rules for storage classes are NOT regular; you will need to memorize them or have a steady reference

  • auto - automatic duration and no linkage
  • register - automatic duration and no linkage; address of this variable cannot be taken
  • static - static duration and internal linkage (unless at block scope)
  • extern - static duration and external linkage (unless already declared internal)

Remember: static has different meanings

  1. Inside a function - static changes the storage location to static memory, either data or BSS segment (the scope stays local)
  2. Outside a function - static changes the scope (visibility) to be only visible within the file (the storage location stays in static memory)
  3. static defined functions are not visible outside of its C file

Extern

  • Compiler does NOT allocate storage
  • For type checking of the identifier name only
  • Another C file must allocate storage by defining that var name (or function)
    • Note that you cannot initialize an extern variable in the same name, e.x. extern int i = 3;

Global Variables

  • By default, global variables are static storage duration and external linkage

Type Qualifiers

  • const - the value of this variable may not be changed after initialization
  • volatile - the compiler may not optimize references to this variable (e.g. it’s a device register that may change value asynchronously)
  • restrict - For the lifetime of a pointer, only the pointer itself or a value directly derived from it may be used to access the object to which it points. This allows better optimization.

Volatile

  • Tells the compiler not to optimize away the variable
  • Use this for device registers

Definitions

A declaration in C introduces an identifier and describes its type, be it a scalar, array, struct, or function. A declaration is what the compiler needs to accept references to that identifier (for type checking).

A definition in C actually instantiates/implements this identifier. For instance, a definition allocates storage for variables or defines the body of a function.

Example

static volatile long int i, *j, k[10];

i is a volatile long int, j is a pointer to a volatile long int, and k is an array[10] of volatile long int

Remember, each declarator is separate

int *x, y, z;

y is NOT a pointer, only x is

Reading and Forming Declarators

  1. Remember the precedence of declarators
  • () and [] declarators get processed first
    • gets processed last
  • Parentheses change the precedence order
  1. Read or form the declarations from the inside out

Example: `int *(**f)()

  • f is a pointer to a pointer of a function that returns a pointer to an int

Step by step

  1. *f means pointer to
  2. (**)f means pointer to pointer
  3. (**f)() means pointer to pointer to a function
  4. *(**f)() means pointer to pointer to a function that returns a pointer
  5. int *(**)f() means pointer to pointer to a function that returns a pointer

How Do We Do It Automatically?

Parsing: determining which grammar productions (rules) were used to generate a sentence

  • Try out dcl.c in Canvas > Files > Source Code

Typedef

Typedef is a shortcut that allows you to create a new alias for a type

  • Does NOT create a new type
  • Start the declaration with "typedef" and put hte alias name where you would put the variable name
  • Really just creates another name for an existing type
struct a b[5]; //Array of 5 struct a named b
typedef struct a sa5[5]; //Create type alias for an array of 5 struct a
sa5 c;

Function Calls

  • Functions must be declared before they are used
  • Function prototype: int nfact(int);
  • A function prototype is automatically given the extern storage class
  • Parameters are always passed as call-by-value
    • Copies of the parameters (even structs) are pushed onto the stack
    • Arrays turn into pointers to their first element, a copy of which is passed

Arrays

  • Manual bounds checking
  • sizeof(ary) / sizeof(ary[0])
  • If it's passed as a parameter, you need another parameter for the length
  • Arrays and all C variables are always allocated in a single contiguous memory block
    • short matrix[10][10][10] would assemble as matrix .blkw 1000

Any array element selection you can do using subscripts can be done with pointers and pointer arithmetic

  • *(p+n) is literally the same as p[n] (or more surprisingly n[p])

Initializing Arrays

int ib[5] = { 5, 4, 3, 2, 1 };
int ib[] = { 5, 4, 3, 2, 1 };
char cb[] = { ‘x’, ‘y’, ‘z’ };
char cb[] =hello”;

Notes about sizeof

char c1[] =hello”;
char *c2 =hello”;

sizeof(c1); //sizeof(c1) == 6
sizeof(c2); //sizeof(c2) == 8, the size of a pointer on your system
strlen(c1) == strlen(c2); //strlen(c1)==5==strlen(c2)

Note that "hello" from c2 would be stored in Constant Data but everything else (*c2, c1[], and "hello" from c1[]) will be stored as Initalized Data

Why is this?

char *c2 =hello”;

In this case, the pointer value can be changed, but "hello" cannot be changed.

However, for

char c1[] =hello”;

The pointer value cannot be changed, but "hello" can be changed.

Space Allocated by Arrays

Let's you you have int ia[6]; How much space is allocated?

6 * sizeof(int) == 24

So then, ia[4] is quite literally 4 * 4 + address of array

Stupid Things No One Ever Does

Let's say you wrote this code

int ia[6];
ia[3] = 42;

Now, ia[3] is the same as *(ia + 3) = 42;, and *(3 + ia) = 42;

Someone might think that this would imply 3[ia] = 42;, which is dumb but works.