- Basic structure
- Basics
- Pointers
- Strings
- Variables types
- Functions
- Structures
- File I/O
- Libraries
- Dynamic memory allocation
int main(void) {
return 0;
}
- main's arguments are by convention called
argc
andargv
(argument count, argument vector) - they are used to handle the arguments passed in the command line
- argc is the number of arguments passed, including the program name
- argv is a pointer array which points to each argument
// print number or arguments, each one of them
int main(int argc, char *argv[] ) {
printf("%i\n", argc);
for(int i = 0; i < argc; i++) {
printf("%s\n", argv[i]);
}
}
- get a string from a stream (file or the standard input)
- stops when the limit of chars was passed or when it finds a newline or end-of-file
fgets(myVar, 10, stdin);
NOTE: Avoid buffer overflow while using scanf to get a string, always limit the numbers of chars accepted
scanf(format, &variable)
= reads data (of many formats, e.g. strings, numbers...) from the standard input- format = define width and accepted chars, e.g.
scanf("%10[A-z ]", myVar);
max number of chars read is 10 and only accepts letters and spacescanf("%d", &myInt)
will accept int numbers
- variableAddress = the address of the variable to save the data (variable name preceded by '&')
- strings don't need the ampersand, because in c, variables holding a string already are pointers pointing to the string's first char
- format = define width and accepted chars, e.g.
- multiple values input:
scanf("%c %c", &x, &y);
save first char in x and second in y
sizeof(char)
= returns the amount of memory that is allocated to the data type- you can use both with types
sizeof(int)
or expressionssizeof(3)
- the parentheses are only required when used with types; e.g.
sizeof "my string"
is valid
- you can use both with types
- by convention, constants have capitalized names
- two ways to declare a constant: define and const
#define VAL1 3
int const VAL2 = 5;
- pointer is a special type of variable whose value is the address of another variable
&
= the address of- e.g.
printf("%p\n", &x);
=>0x7ffce383cd78
- e.g.
*
= it'll be located in one of two places:- if in a variable declaration, make this variable a pointer to the variable in the right
- e.g.
int *y = &x;
NOTE: (int
specify that y will be a pointer to a variable of type int)
- e.g.
- if in front of a already declared variable storing a pointer, go to the address that this pointer is pointing and get its value (dereferencing)
- e.g.
printf("%i\n", *y);
- e.g.
- if in a variable declaration, make this variable a pointer to the variable in the right
- a string is a one-dimensional array of characters terminated by a null character
\0
- this null character is added automatically at the end of every sequence of characters enclosed with double quotes
- the variable that holds a string is a pointer to the address of the string's first char
char *s = "abc";
- allocates 4 consecutive bytes for a string literal ("abc\0")
- and 8 extra bytes for the pointer (pointing to the address of the first char) that will be stored in the variable
- can be entirely reassigned (
x = "def"
), that is, re-pointed to something else - but can't have its individual chars changed (
x[0] = 'e'
) - no fixed length - you can reassigne to a smaller or bigger string
char s[] = "abc";
length implicit,char s[] = "abc";
length explicit- creates a char array of 4 bytes ('a', 'b', 'c', '\0')
- it'll have the same functionalities as a normal array
- you can access a individual char (
x[0]
) - can't be entirely reassigned (
x = "def"
), - but can have its individual chars changed (
x[0] = 'e'
) - fixed length - you must say ahead of time how many characters the array may hold, this can't be change later
- you can create a custom string type to make variable creation easier
- 1 -
typedef char *string;
= create type - 2 -
string x = "test";
= declare variable with the new type
- strings must be declared with double quotes and chars with single quotes
- NULL is limited to identifying a null pointer
#include <stdio.h>
int main(void) {
int *p = NULL;
if (!p) {
printf("pointer is NULL");
}
}
- implicit casting happens when
- operands have mismatched types,
- or when you call a function using an argument whose type does not match the function’s corresponding parameter.
- Programs also perform implicit type conversion as necessary when initializing variables or otherwise assigning values to them.
(type) expression
= explicit casting
// you must declare which type the elements will be
// declare one by one
int x[5];
x[0] = 3;
// declare all at once
int x[5] = {3, 5, 8, 12, 15};
// pass arrays to functions (function declaration)
void test(int x[]);
#include <stdio.h>
void myf(int arr[]);
int main(void) {
int arr[] = { 3, 5, 8, 12, 15 };
// array size is 20 and each element is 4 (because is an array of ints)
printf("%lu, %lu\n", sizeof(arr), sizeof(arr[0]));
myf(arr);
}
void myf(int arr[]) {
// size is 8, because an array decays into a pointer to its first element
// when it's passed to a function
printf("%lu\n", sizeof(arr));
}
- C programming does not allow to return an entire array
- return a pointer instead
- static is needed, otherwise the array values (after first one) will be lost
#include <stdio.h>
int *rarr();
int main(void) {
int *p = rarr();
// 'i < 3' because array is length 3
for(int i = 0; i < 3; i++) {
printf("%d\n", *(p + i)); // pointer arithmetic
}
}
int *rarr() {
// static is to preserve the variable even when out of their scope
static int a[] = { 3, 5, 8 };
return a;
}
- A pointer is a numeric value
- do arithmetic operations with: ++, --, +, or -
- each x increment/decrement, will skip x as many bytes as the type need (e.g. 4 for int)
- array notation vs pointer arithmetic:
ptr[x]
is equivalent to*(ptr + x)
- it's convention to let the 'main' function be on top
- and declare only the other functions' prototypes (first line) before it
#include <stdio.h>
void test (void);
int main (void) {
test();
}
void test (void) {
printf("from test");
}
- user defined data type used to group values of different types
- it's convention to capitalize a struct name
struct myStruct x[5]
= arrays w/ struct, the type of the array's elements will be the specified structvoid printBook( struct Books book );
= pass struct to functions
#include <stdio.h>
#include <string.h>
// declaration
struct Person {
char name[10];
int age;
};
int main(void) {
// initialization
struct Person person1;
person1.age = 30;
strcpy(person1.name, "John");
// access
printf("%s, %i\n", person1.name, person1.age);
}
- typedef keyword creates an alias name for data types
- used with structures to simplify the syntax of declaring variables
typedef struct Person {
int age;
} person;
int main(void) {
// declaration
person me;
}
fopen(filename, mode)
= create a new file or open an existing file- mode = r (read), w (write), a (append), r+ (read and write), w+ ( read and write, trucantes file if already exists) and a+ (read and write, if file already exists write only can be appended).)
fclose(filePointer)
= close a file and flushes any data still pending in the buffer;
char fileContent[50];
FILE *filePointer = fopen("log.txt", "a+");
// read one line at time with fgets
// fgets will store the content in the variable fileContent
fgets(fileContent, 50, filePointer);
printf("%s", fileContent);
// after reading, fgets will move its internal pointer to the next line
fgets(fileContent, 50, filePointer);
printf("%s", fileContent);
// write
fprintf(filePointer, "log two\n");
fclose(filePointer);
strcpy(dest, src)
;- make it easier to reassign a string declared with the bracket notation (
char x[10]
);
memcpy(dest, src, bytesToCopy)
// copy a string
char *x = "test";
char y[strlen(x)];
memcpy(y, x, strlen(x));
printf("%s\n", y);
// copy a array
int a[] = {3, 5, 8};
int len = sizeof(a) / sizeof(a[0]);
int b[len];
memcpy(b, a, sizeof(a));
for (int i = 0; i < len; i++) {
printf("%i ", b[i]);
}
printf("\n");
#include <stdlib.h>
= to use any of the functions below, you need this include
- two options to access elements in a space allocated with malloc, calloc, realloc:
- by pointer arithmetic:
*(ptr + x)
= skips x times as many bytes as the type of element it points to- e.g.
*(ptr + 0)
points to the first element,*(ptr + 1)
points to the second element and so on... - example: if a pointer contains the address of an integer, then adding one to that pointer means skipping 4 bytes to point to the next integer
- e.g.
- by pointer arithmetic:
- by array notation:
ptr[x]
= equivalent to*(ptr + x)
; this is RECOMMENDED
ptr = (cast-type*) malloc(byte-size)
- memory allocation, dynamically allocate a single large block of memory with a specified size- returns a pointer, or
NULL
if the request fails - initializes each block with default garbage value
- returns a pointer, or
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int *ptr;
int n = 3;
ptr = (int*) malloc(sizeof(int) * n);
if (ptr == NULL) {
printf("Memory not allocated.\n");
exit(0);
}
// assign a value to each space
for (int i = 0; i < n; i++) {
ptr[i] = i * 2; // equivalent to *(ptr + i) = i * 2;
}
printf("%d in address %lu\n", ptr[0], ptr);
printf("%d in address %lu\n", ptr[1], ptr + 1);
printf("%d in address %lu\n", ptr[2], ptr + 2);
free(ptr);
}
free(ptr)
= frees the space allocated in the memory- its argument is the pointer returned by malloc, realloc, calloc...
ptr = (castType*) calloc(n, size);
= contiguous allocation, almost the same as malloc, however there're 2 essential differences:- malloc leaves the memory uninitialized; calloc initialize all bits to zero
- calloc takes two arguments: number of blocks and size of each block
ptr = realloc(ptr, newSize);
= re-allocation; dynamically change the memory allocation of a previously memory allocated with malloc or calloc- maintains the already present value and new blocks will be initialized with default garbage value
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int *ptr;
int n = 3;
ptr = (int*) malloc(sizeof(int) * n);
// assign a value to the first block
// it'll be kept after realloc
ptr[0] = 3;
int newN = 5;
ptr = (int*) realloc(ptr, newN);
// assign a value to the new last block
ptr[newN - 1] = 10;
printf("%d in address %lu\n", ptr[0], ptr);
printf("%d in address %lu\n", ptr[newN - 1], ptr + (newN - 1));
free(ptr);
}