Professional Documents
Culture Documents
CMake allows us to configure the build process in a way independent from the compiler or
the operating system. In Linux, it generates makefiles, in Windows (specifically Visual
Studio workspaces) it generates solution files (.sln) and project files (.vcxproj). Regardless
of the compiler or operating system, the generated files can then be compiled and run
without any further configuration.
Example
For our example, we'll be generating a simple project that prints out "hello world". When
starting, CMake will look for a 'CMakeLists.txt' file. All other configurations are done based
on that file.
Folder structure:
cmake_example
| CMakeLists.txt
|
++++include
| print_hello.h
|
++++src
main.cpp
say_hello.cpp
Contents of 'CMakeLists.txt'
cmake_minimum_required(VERSION 3.0)
# create variable containing project name
set(PROJECT_NAME "CMake_Example")
# set project name
project(${PROJECT_NAME})
message("building ${PROJECT_NAME} ...")
# specify the folder(s) where the compiler should look for include files
include_directories(include) #in our case the 'include' folder present in the
root folder
# or we can use file(GLOB ...) for wildcard additon (add any file ending with
'.h')
file(GLOB HEADER_FILES "include/*.h")
The full contents of this project are available to be in the 'cmake_example.7z' archive.
Statements
Represent each individual instruction given to the program.
They always end with a semicolon (;).
Are executed sequentially .
During its process, a program may repeat segments of code, or take decisions and
bifurcate. For that purpose, C++ provides flow control statements that serve to specify
what has to be done by our program, when, and under which circumstances.
Flow Control
IF
The if keyword is used to execute a statement or a block of code if the expression inside
the brackets evaluates to true.If the expression evaluates to false the statement or block of
code is simply ignored.
void main()
{
// declare local variable
int a = 10;
// check the boolean condition
if(a == 10)
{
printf("a == %d",a);
}
}
// evaluates to true
int n = 5;
if(n)
{
printf("this works")
}
// evaluates to true
n = -5;
if(n)
{
printf("this also works")
}
// evaluates to false
n = 0;
if(n)
{
printf("this code will not be run")
}
Switch
A switch statement selects which block of code to execute based on the value of an
integral expression.
The switch statement compares the value of “selector” to each integral value. If no
match is found, the default case is executed.
Every case needs to end with a break keyword. This causes the program to jump to
the end of the switch body.
If no break keyword is encountered the program continues to the next case until the
of the switch body.
Looping
Infinite Loops
A loop is said to be an infinite loop if the control enters but never leaves the body of the
loop. This happens when the test condition of the loop never evaluates to false.
Jump Statements
}
while (true)
{
/* body of the loop */
}
for (;;)
{
/* body of the loop */
}
Pointers
A pointer is a type of variable which can store the address of another object or a function.
A pointer is declared much like any other variable, except an asterisk (*) is placed between
the type and the name of the variable to denote it is a pointer.
The address-of or reference operator denoted by an ampersand (&) gives the address of a
given variable which can be placed in a pointer of appropriate type.
int value = 1;
pointer = &value;
The above code copies the address of the variable value to the pointer p.
To use the value pointed at by the pointer, we have to dereference it, with the * operator.
Pointers are also re-assignable. This means that a pointer pointing to an object can later
be used to point to another object of the same type.
Arrays
An array is a series of elements of the same type placed in contiguous memory locations
that can be individually referenced by adding an index to a unique identifier.
When declaring an uninitialized array inside a local scope, the values contained by
the array are undetermined.
void main()
{
// declare array
int integerList[5];
// iterate through array
for(int i=0; i< 5; ++i)
{
// will print out junk values
printf("%d", integerList[i]);
}
}
When declaring a global or static array, the values contained by the array will be
automatically initialized to 0.
// declare array
int integerList[5];
void main()
{
// iterate through array
for(int i=0; i< 5; ++i)
{
// will print out '0'
printf("%d", integerList[i]);
}
}
C/C++ does not do boundary checking when using arrays. Be careful when iterating over
arrays to not go out of bounds or risk undefined behavior.
Multi-Dimensional Arrays
Multidimensional arrays can be described as "arrays of arrays". For example, a bi-
dimensional array can be imagined as a bi-dimensional table made of elements, all of
them of a same uniform data type.
int integerList[5][5];
for(int i =0;i<5;++i)
{
for(int j=0;j<5;++j)
{
integerList[i][j] = i+j;
}
}
This means that a C-string with a content of "abc" will have four characters 'a', 'b', 'c' and
'\0'.
We can create strings using several methods. For instance, we can declare a char * and
initialize it to point to the first character of a string:
When initializing with char * , the string is a pointer to the first element of the array, the
character 'h', the string is allocated in read-only memory, any attempts to modify it will
cause undefined behavior.
To create a modifiable string, you can declare a character array and initialize its contents
using a string literal, like so:
char modifiable_string[] = {'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l',
'd', '\0'};
Since the second version uses brace-enclosed initializer, the string is not automatically
null-terminated unless a '\0' character is included explicitly in the character array usually
as its last element.
String Tokenization
The function strtok breaks a string into a smaller strings, or tokens, using a set of
delimiters.
#include <stdio.h>
#include <string.h>
int main(void)
{
int toknum = 0;
char src[] = "Hello,, world!";
const char delimiters[] = ", !";
char *token = strtok(src, delimiters);
while (token != NULL)
{
printf("%d: [%s]\n", ++toknum, token);
token = strtok(NULL, delimiters);
}
/* source is now "Hello\0, world\0\0" */
}
String Length
strlen counts all the bytes from the beginning of the string up to, but not including, the
terminating NUL character, '\0'. As such, it can only be used when the string is guaranteed
to be NUL-terminated.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void main(int argc, char **argv)
{
/* Exit if no second argument is found. */
if (argc != 2)
{
puts("Argument missing.");
return;
}
size_t len = strlen(argv[1]);
printf("The length of the second argument is %d.\\n", len);
return;
}
Copying Strings
Since C Strings are represented as arrays of characters, simply using the '=' operator to
copy will not work, since that will just copy the address of the pointer.
#include <stdio.h>
void main() {
char c[] = "abc";
char* d;
c[1] = 'x'; // modifies the original string, d[1] would do the same thing
#include <stdio.h>
#include <string.h>
void main() {
char a[] = "abc";
char b[8];
strcpy(b, a);
printf("%s\n", b); /* "abc" will be printed */
}
Comparing Strings
#include <stdio.h>
#include <string.h>
void compare(char const *lhs, char const *rhs)
{
int result = strcmp(lhs, rhs); // compute comparison once
if (result < 0) {
printf("%s comes before %s\n", lhs, rhs);
} else if (result == 0) {
printf("%s equals %s\n", lhs, rhs);
} else { // last case: result > 0
printf("%s comes after %s\n", lhs, rhs);
}
}
void main()
{
compare("BBB", "BBB");
compare("BBB", "CCCCC");
compare("BBB", "AAAAAA");
}
free(buffer1);
free(buffer2)
It is important to remember to free up allocated memory, if the reference to the pointer is
lost , it causes a memory leak (that memory is wasted and we can't access that memory
again for the duration of the program run).
Since dynamically allocated memory exists on the heap and not on the stack like normally
allocated variables, it allows us to do things that we couldn't otherwise do.
int* getIntPtr()
{
int n = 5;
// will cause an error, 'n' will not exist after function returns
return &n;
}
int* getIntPtr()
{
int* n =(int*) malloc( sizeof(int));
// n will continue to exist after function returns
return n;
}
Function prototype:
Writing to a file:
#include <stdio.h>
void main
{
// what character will cause us to exit
char endOfList[] = "*";
// open the file, create it if it doesn't exist
FILE* fp = fopen("student_list", "w"); // will get created relative to the
current working directory. (The folder that holds the vcxproj file in case
we're executing it from visual studio, or in the folder that holds the
executable, it we're executing it stand-alone)
// allocate buffer
char buff[100];
// check we could open folder
if(fp!=NULL)
{
do
{
gets_s(str);
if(strcmp(endOfList, str)==0)
{
// the user has typed '*', exit the loop
break;
}
printf("adding %s to the list \n", str);
// write it to the file
frintf(fp, str);
}
while(true) // keep reading
}
fclose(fp);
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
if (!file)
{
perror(path);
return EXIT_FAILURE;
}
/* Get each line until there are none left */
while (fgets(line, MAX_LINE_LENGTH, file))
{
/* Print each line */
printf("%s", line);
}
fclose(file);
}
Function Parameters
In C, all function parameters are passed by value, so modifying what is passed in callee
functions won't a ect caller functions' local variables
#include <stdio.h>
void modify(int v) {
printf("modify 1: %d\n", v); /* 0 is printed */
v = 42;
printf("modify 2: %d\n", v); /* 42 is printed */
}
int main(void) {
int v = 0;
printf("main 1: %d\n", v); /* 0 is printed */
modify(v);
printf("main 2: %d\n", v); /* 0 is printed, not 42 */
return 0;
}
You can use pointers (or references) to let called functions modify caller functions' local
variables.
#include <stdio.h>
void modify(int* v) {
printf("modify 1: %d\n", *v); /* 0 is printed */
*v = 42;
printf("modify 2: %d\n", *v); /* 42 is printed */
}
int main(void) {
int v = 0;
printf("main 1: %d\n", v); /* 0 is printed */
modify(&v);
Since we don't actually know the size of array my_array, the extra size parameter is
needed.
Structures
"Struct" is short for "structured data type".
struct cat {
const char* name;
const char* breed;
int weight;
int age;
};
}
/* enter the cat in show */
void enter_in_cat_show(struct cat c) {
printf("%s has been entered in the cat show.", c.name);
}
void main()
{
struct cat garfield = {"Garfield", "main coon", 200, 14};
record_stats(garfield);
display_stats(garfield);
enter_in_cat_show(garfield);
}
Function Pointers
Function pointers are pointers that point to functions instead of data types. They can be
used to allow variability in the function that is to be called, at run-time.
void main()
{
// create function pointer
// structure is
// <return type> (* <name>)(<arg type 1>,<arg type 2>,...)
int (*my_func_pointer)(int,int);
printf("%d", result);
}
Best Practices
It might be handy to use a typedef instead of declaring the function pointer each time by
hand.