title: C subtitle: Short summary license: CC-BY-NC-SA 4.0 author: Ekaitz Zarraga - ElenQ Technology links-as-notes: true ...
GCC does everything: it compiles to assembly and then assembles and then links.
Only the first thing is actually done by GCC, the rest are Coreutils' tools
called from GCC: as and ld.
# Do everything:
gcc file.c -o executable# Just compile to object file
gcc -c file.c -o file.o# Uses `ar` (ARchive) to create static library
ar cr libexample.a file1.o file2.o file3.o# Create a dynamically linked library
gcc -shared -fPIC -o libexample.so file1.o file2.o file3.oDeclare the "interfaces" to some codebase, the things that can be used. They shouldn't have actual code. They should be just a bunch of declarations.
They help with separate compilation.
When you ship a compiled library, you provide the lib and the headers. The headers let the users compile code that they write but uses things that are defined in yours.
gcc -c -o x.o user_wrote.c # this includes your header
gcc -o user_exe x.o -lyour_lib # this links your lib-ladds libraries to the mix (it chooses by itself if they are static or dynamic)-Ladds library search paths-Iadds header file search paths
- During compilation: Missing declarations -> You are missing some header file or a declaration in your code. (Things that a person wrote miss something)
- During linking: Missing symbol -> You are missing a library in link time. This may happen when linking or when running the program because that's when dynamic linking is actually done (Things that a compiler built are not added to the mix)
They can be signed or unsigned. All integers are signed by default,
chars can be signed or unsigned depending on the machine you are (I don't
make the rules)
// Integer values
char a;
short b;
int c;
long d;
long long e;
// Floating point
float f;
double g;
long double h;
// Nothing
void v; // <-- Funny thing, you cannot create void objects, this does not
// compileSizes are not guaranteed. They are ordered from smaller to larger in each of the groups, but they might be the same.
Use
stdint.hfor fixed sizes that you can know:int32_t...
- Chars use simple quotes:
'a' - Numbers can use suffixes for specifying the type:
100L,90UL - Strings use double quotes
"Hello". Putting one string literal after another concatenates them. - Escape character is
\ - There are very cool ways to initialize structs and arrays with
{}. - No undefined, null, true, false or anything like that.
Declare means mentioning you are going to use something. This tells the compiler that something will exist, but doesn't really create anything. You can declare the same thing several times.
Definitions are declarations that also create the values themselves. They require memory and can only happen once. Definitions are not forced to set the value of what they define.
// Declaration
int this_is_a_function (char argument, float other_argument);
// ^^^^^^^^ ^^^^^^^^^^^^^^
// This is a declaration so you don't need these, but they help.
// Definition
int this_is_a_function (char argument, float other_argument) {
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Declarations
// Do something
return 1;
}A pointer is a variable that stores an address to the memory and its type tells the compiler how that memory should be accessed:
char a; // This is a character
char *b; // This is a pointer to a character
// This is how we obtain the addresses of things
char *c = &a;
// ^^ Pointer to char
// ^^ Address of char
char **d = &c
// ^^^ Pointer to char pointer
// ^^ Address of char pointerFunctions are also in memory so you can make pointers to them and pass them around. The syntax gets funny:
int *function(int); // Receives an int and returns and int pointer
int (*function)(int); // Pointer to a function that gets an int and
// returns an int
int *(*function)(int*); // Pointer to a function that gets a pointer to an int
// and returns a pointer to an intVoid pointers point to memory but you don't know how to interpret that yet. You cannot access them unless you cast them to something else. They enable generics.
void *a = &whatever;Pointer syntax (*) is not part of the type name. It represents "this thing
can be dereferenced to something of type X".
// Make a integer
int a = 10;
// Make something that dereferences to an integer, and initialize it to `a`'s
// address
int *b = &a;
// Dereference `b`: Go to what `b` points to, read it as an integer and tell
// me the result.
// Is it 10?
*b == 10
// Yes
// Function pointers you don't need to dereference to call:
function(x);Arrays are a collection of values of certain type, put in memory one after another (raw).
// Declaration
float a[100]; // Put 100 floats in memory, one after another
long *b[100]; // Put 100 long pointers in memory.
// ^^^ Must be compile-time known
// Access
a[90] = 1.1; // Set
float c = a[90]; // Get
// c == 1.1
// Nobody prevents you from (well, you should prevent yourself):
a[10000] // <--- heheIf arrays are just values one after another you know the memory layout so you can just access with pointers:
float f[10];
*f = 0.0;
*(f+1) = 1.0;
*(f+2) = 2.89;
// Guess this
f[2]This works because C knows pointer arithmetic. It calculates the size of things, so when you add numbers to the addresses of pointers, it adjusts to memory slots and not just bytes (memories are normally accessible by byte).
In the case in the example, floats are normally 32 bits (4 bytes) so doing
f+1 will add 4 to the raw pointer automatically.
Yes, if we cast the pointer to char we can increment it one by one and read the slots byte by byte.
They are char arrays.
char *a = "hello";
char a[6] = "hello";
// ^ No explicit \0 there!
// ^^^ They are \0 terminated by default.Convert types
char a = 9;
int b = (int) a; // (this is not needed because it promotes automatically)
long c = 1000000000000000000000;
long d = (char) c; // Truncate! `d` is 0!typedef int f0(void); // Lets you rename types. Read about it.
unsigned int instructions[2];
instructions[0] = 0x03800513; // addi a0, zero, 56
instructions[1] = 0x00008067; // jalr zero, ra, 0
f0 *load_56 = (f0*) instructions; // Reinterpret the array address
// as a function pointer
int a = load_56(); // Call it!
// a == 56They are new types we make combining the base types:
// Declare the struct
struct thing {
int a;
char b;
// the fields are stored in order!
};
// Define one
struct thing x;
// Access the fields
x.a = 10;
// Anonymous
struct {
int x;
char y;
} a_thing; // This struct is only declared for defining `thing`.
a_thing.x = 10;
a_thing.y = 'b';Oftentimes mixed with typedef for simplicity:
typedef struct {
int whatever;
// ...
} my_type;
my_type x;struct thing x;
struct thing *y = &x;
// You can (and should) write:
(*y).a
// Like this:
y->asizeof(struct thing); // Returns the size of the structAlignment and padding and so on are important. Rule of thumb: order the size of the fields from high to low.
They are like structs but the values are exclusive. Their sizes is that of the largest of their fields:
union u {
int a;
float b;
};
union u x;
x.a = 11111;
x.b = 0.1;
// ^^^ this overwrites `a`enum {
A, // 0
B, // 1
C // 2
};A, B and C will be set to some integer value, according to the predefined
algorithm the compilers have. Their value is assigned in order, from low to
high, and is available in the scope directly. They can be compared to
integers directly. You can set some to a fixed value if you wish, but better
learn.
statement;
{
// you can use the compound in any place you can use a statement
compound;
statement;
}
// Some ideas
while (x < 10) {
if (x % 2)
break;
else
continue;
x++;
}
do
xxx;
while (something);
for (int i; i < 10; i++)
xxx;
switch (var) {
case X:
// ^^ compile time known
xxx;
break;
case Y: // fallthrough!!
case Z:
yyy;
break;
default:
break;
}
// considered harmful
back:
xxx;
goto back;inlineis a function specifier that means the function will be inlined (instead of compiling it as a call, copy-paste it in place).staticis a specifier:- In a global variable it means it can only be accessed in the current file.
- In a local variable it means the variable will be kept in memory from one execution of the function to the next, keeping its value.
They deal with text, and the macro expansion is run before the compiler is actually run.
gcc -E file.c # Macro expand the file// Every A in your code will be converted to a 10
#define A 10
A // Becomes 10
// Function style
#define second(a, b) b
second(10, 90) // becomes 90
// if guards are useful in header files
#ifndef THIS_FILE_H
#define THIS_FILE_H
// MY CODE HERE
#endif
#include "somefile.h" // just copy the file here, literally
#include <somefile.h> // same but search in system paths// main.c
#include<stdio.h> // Include standard input/output from LibC
int
main (int argc, char *argv[]) {
/*
* The main function is the entry point of your program.
* The libc prepares everything for that to be called with the input
* arguments the program was given from the shell.
*/
printf("Hello world\n");
}gcc -o xxx main.c # the compiler links the libc for you$ ./xxx
Hello world
$