Week 9
This week covers CLI arguments (as well as some basic shell operations),
calloc() and realloc(), and a bunch of other random topics that seems to
just be here to say it was covered..
Command Line Arguments in C
In C, we can grab arguments from the command line when running a program. To do this, we can supply two arguments to our main function:
int main(int argc, char *argv[])
// or equivanlently
int main(int argc, char **argv)
int argc stands for the argument count and always includes the name
of the program as the first counted element (a program always has at
least one argument as a result).
char *argv[] stands for the argument vector. It is an array of strings
representing the different arguments passed (including the name of the program).
Example
#include <stdio.h>
int main(int argc, char *argv[])
{
for (int i = 0; i < argc; ++i)
{
printf("Argument %d: %-10s\n", i + 1, argv[i]);
}
}
Shell Operators
These operators belong to the command-line environment, not to C. They behave the same idea-wise in Unix shells (bash, zsh, etc.) and in Windows CMD/PowerShell, even if some details differ. These operators let you combine, chain, and repurpose command-line programs by directing where their input comes from and where their output goes, making them far more flexible to use at the terminal.
Output redirection operator >
Sends a program’s stdout to a file, replacing the file if it already exists.
Output redirection operator > Example
Output append operator >>
Appends stdout to the end of a file instead of overwriting it.
Output append redirection operator >> Example
Input redirection operator <
Uses a file as stdin for the program.
Input redirection operator < Example
Pipe operator |
Connects the stdout of one program to the stdin of another.
Pipe operator | Example
calloc
calloc is like malloc, but it initializes all allocated memory to zero. It
takes two arguments:
- The number of elements
- The size of each element in bytes
int num_elements = 4;
void *myZeroedArray = calloc(num_elements, sizeof(int));
/* The memory layout of myZeroedArray would look like this (assuming returned
block starts at address 0x1000):
Address | Value (in binary)
--------------------------------------------------------------
0x1000 | 00000000 00000000 00000000 00000000 (first int)
0x1004 | 00000000 00000000 00000000 00000000 (second int)
0x1008 | 00000000 00000000 00000000 00000000 (third int)
0x100C | 00000000 00000000 00000000 00000000 (fourth int)
*/
Note
Bob mentioned that, in practice, you don't need to remember the order of the arguments as they are multiplied together and all bits set to 0 so passing them in the wrong order will still produce a correct result.
realloc
realloc is used to resize a previously allocated block of memory. It takes two
arguments:
- A pointer to a block of memory previously allocated with
malloc,calloc, orrealloc - The new desired size in bytes
int num_elements = 4;
int *arr = malloc(num_elements * sizeof(int));
/* Later, we decide we need space for 8 ints instead of 4: */
num_elements = 8;
int *newArr = realloc(arr, num_elements * sizeof(int));
/* After this call:
- If the block can be extended in place, `newArr` will have the same address
as `arr`.
- If it cannot be extended, a new block is allocated elsewhere, the old data
is copied to it, and `arr` is freed automatically.
- The newly allocated portion (the extra bytes) is *not initialized*.
*/
If realloc fails, it returns NULL and the original pointer remains valid.
To avoid losing access to the old memory, it's common to assign the result to a
temporary pointer first.
Warning
realloc may move the memory block to a different address. After calling
realloc, always use the returned pointer and never the original one.
Other Topics
These topics were covered in class but received very minimal emphasis.
Variable Argument Lists
Functions can accept an unspecified number of arguments using <stdarg.h>.
This is similar to Python's *args, but C provides no runtime type
information, so each argument's type must be known in advance.
To define such a function, end the parameter list with ... and use
<stdarg.h> macros to iterate through the unnamed arguments.
#include <stdarg.h>
int sum(int count, ...) {
va_list args;
va_start(args, count);
int total = 0;
for (int i = 0; i < count; i++) {
total += va_arg(args, int);
}
va_end(args);
return total;
}
Key concepts:
va_list: object used to traverse unnamed argumentsva_start(list, last_named_param): initialize traversalva_arg(list, type): read the next argument as the given typeva_end(list): finalize traversal
Notes
- Arguments must be read in the order they were passed.
- Using the wrong type with
va_argis undefined behavior. - Commonly used in wrappers around
printf-style functionality.
Constant Literal Suffixes
Suffixes help control literal type, avoid implicit promotions, and prevent overflow.
Integer and floating-point literals can be suffixed to control their type. This matters when matching function prototypes, avoiding overflow, or controlling storage size.
long a = 5; // '5' is an int literal; it is implicitly promoted to long
long b = 5L; // '5L' is a long literal; no promotion occurs
Common suffixes:
| Literal | Meaning |
|---|---|
| integers | |
U |
Unsigned int |
L |
Long int |
UL or LU |
Unsigned long |
LL |
Long long |
ULL |
Unsigned long long |
| floats | |
F |
Float (otherwise double) |
L |
Long double |
Typical Uses
- Prevent overflow in intermediate expressions.
- Match functions that expect
float. - Ensure constants match fixed-width integer types.
exit and atexit
exit terminates the program and runs any cleanup functions previously
registered with atexit.
exit(EXIT_SUCCESS);
exit(EXIT_FAILURE);
Before termination, exit will:
- flush open output streams
- close files
- call all functions registered with
atexit
atexit registers a function to run automatically when exit is invoked:
void cleanup(void) {
printf("Cleaning up...\n");
}
int main(void) {
atexit(cleanup);
exit(EXIT_SUCCESS);
}
Notes
- Functions run in reverse order of registration (LIFO).
- Useful in library code or for guaranteed cleanup.
goto
goto transfers control to a labeled statement within the same function.
if (error_condition)
goto cleanup;
cleanup:
free(ptr);
Key Points
- Labels must be inside the same function.
- No automatic cleanup occurs; stack variables are not unwound.
- Useful for unified cleanup blocks and breaking out of nested structures.
Warning
Avoid using goto for normal control flow; reserve it for cleanup/error paths.