The following gives a quick overview of C concepts most relevant to Project 2.
We recommend you familiarize yourself with other detailed C resources. Some great examples are:
Function declarations include a function's name, the type of the data it returns, and its arguments.
void hello() // This function's return type is "void", meaning it returns nothing.
int add(int a, int b) // This function returns an integer, and takes in two integers a and b.
char *gets(char *s) // This function returns a char pointer, and takes in one as an arg.
Function definitions provides the actual body of a declared function.
void hello(){
printf("Goodbye!");
return; // Returns nothing; it's void!
}
int add(int a, int b) {
int c = a + b;
return c; // Returns a single integer.
}
Function calls are how one function invokes another. To be successful, the caller function must properly set up the callee's arguments. For example, suppose our callee's definintion is void callee(int a)
:
void caller(){
int x = 4440;
void callee(); // Will fail; missing the callee's argument!
void callee(x); // Works! We've passed-in the expected arg.
}
The following statically allocates a stack buffer with name name
to hold nmemb
objects of type type
: type name[nmemb]
. Note that buffer's total size is based on the number and size of the data type~
char foo[100] // Allocates a 100-byte character buffer foo.
// Characters are just one byte in size!
int bar[100] // Allocates a 400-byte integer buffer bar.
// Recall that integers are 4 bytes large!
In Project 2, you'll see function void *alloca(size_t size)
used to allocate stack buffers of variable sizes (i.e., dependent on argument size
).
int *buf = alloca(sizeof(int) * x); // Allocates a buffer that can fit x integers.
// Ints are 4 bytes large, so we get a (4*x) byte buf!
Pointers are variables whose values are the memory address of another variable of the same data type. Pointers are created with the *
operator (e.g., int *ptr = &var
):
int course = 4440; // Variable 'course' is an integer with the value 4440.
int *ptr = &course; // Pointer ptr now points to course's memory address!
Pointer dereferencing allows you to update or reference the value pointed to by a pointer variable. We'll again use the *
dereference operator accordingly:
int x = 4440; // Variable x is an integer with value 4440.
int *ptr = &x; // Pointer ptr points to x's memory address!
*ptr = 5963 // The memory slot that ptr points to now contains 5963!
gets()
: Get StringDefinition: char *gets(char *s)
.
Reads a line from stdin
(standard in) into the buffer pointed to by s
until either a terminating newline or EOF (end of file), which it replaces with a null terminator ('\0'
). No bounds check is performed.
char dest[4]; // Set up our dest buffer...
gets(dest); // Save user's input from stdin into dest!
strcpy()
: String CopyDefinition: char *strcpy(char *dest, char *src)
.
Copies the string pointed to by buffer src
to the buffer pointed to by dest
. No bounds check is performed.
char src[100]; // Set up our source buffer...
char dst[50]; // Set up our destination buffer...
strcpy(dst, src); // Copy source into dest; oh no, overflow!
strncpy()
: Restricted String CopyDefinition: char *strncpy(char *dest, char *src, size_t n)
.
Copies up to n
bytes from the string pointed to by buffer src
to the buffer pointed to by dest
.
char src[100]; // Set up our source buffer...
char dst[50]; // Set up our destination buffer...
strncpy(dst, src, 50); // Copy 50 bytes from source to dest!
fread()
: Reading from FilesDefinition: size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream)
.
Reads nmemb
items of size size
bytes long from the stream pointed to by stream
into the location pointed to by ptr
.
FILE *f = fopen("grades.txt", "r"); // Set up our file stream...
char buf[4440]; // Set up our char buffer...
fread(&buf, sizeof(char), 4440, f); // Read 4440 char-sized elements from f into buf!
system()
: Command ExecutionDefinition: int system(const char *command)
.
Executes a command specified in command
by calling /bin/sh -c command
, and returns after the command has been completed.
system("foo"); // This opens process foo!
system("/bin/sh"); // This opens the shell process!
setuid()
: Set PermissionsDefinition: int setuid(uid_t uid)
Calling setuid(0)
sets the process privilege level to root:
setuid(0); // Set privilege to root...
system("/bin/sh"); // Now this shell is a ROOT shell!