* Where do we put the values that a program manipulate? * Is there any program value that is not in memory? * Which kinds of memory exist, in terms of hardware? - registers, RAM, disk, etc * How is the program memory organized? - static - stack - heap * What is located in the static memory area? - program - global variables - data marked as static * Where are the contents of the array x stored in the program below? #include int main() { int x[] = {2, 3, 5, 7, 11, 13, 17, 19}; for (int i : x) { std::cout << i << std::endl; } return 0; } * How can you see the assembly version of this program? clang++ -S -std=c++11 static_1.cpp * Can you locate the array in the assembly code? * What about strings, where are they located in memory? #include int main () { std::string str = "Hello"; str[1] = 'u'; std::cout << str << std::endl; return 0; } * Can you check where are the global variables located? #include int global_var = 13; int main() { int local_var = 17; std::cout << local_var << std::endl; std::cout << global_var << std::endl; } * Can you find the global variable in the assembly program? clang++ -S -std=c++11 global_1.cpp * What about local_var, can you find it in the assembly program? * What happened to local_var? * What will be printed by the program below: #include int getAndSet() { static int counter = 0; return ++counter; } int main() { std::cout << getAndSet() << std::endl; std::cout << getAndSet() << std::endl; std::cout << getAndSet() << std::endl; } * Where is variable counter stored? * Can you see counter in the assembly version of that program? * How many variables 'counter' exist in the program below? #include int getAndSet_1() { static int counter = 0; return ++counter; } int getAndSet_2() { static int counter = 0; return ++counter; } int main() { for (int i = 0; i < 7; i++) { std::cout << "1) " << getAndSet_1() << std::endl; std::cout << "2) " << getAndSet_2() << std::endl; } } * Can you check the assembly of this program? * What if I had implemented it like this code below? How many 'counters' would we have? #include static int counter = 0; int getAndSet_1() { return ++counter; } int getAndSet_2() { return ++counter; } int main() { for (int i = 0; i < 7; i++) { std::cout << "1) " << getAndSet_1() << std::endl; std::cout << "2) " << getAndSet_2() << std::endl; } } * Why do static allocation has this name? * Which program variables are located in the stack? * Stack allocation is a form of dynamic memory allocation. Why? * Can you write a quick factorial function? #include int fact (int n) { int aux = n; if (aux > 1) { return aux * fact(aux - 1); } else { return 1; } } int main(int argc, char** argv) { std::cout << fact(argc) << std::endl; return 0; } * What is the address of variable 'aux'? * Can you print this address whenever the function 'fact' is called? #include #include int fact (int n) { int aux = n; printf("(%p, %d)\n", &aux, n); if (aux > 1) { return aux * fact(aux - 1); } else { return 1; } } int main(int argc, char** argv) { std::cout << fact(argc) << std::endl; return 0; } * What do you notice: is the address increasing or decreasing? * What would happen if we did not have stack allocation, i.e., if all the memory allocation were static? * Which data is allocated in the stack? - Return address - Parameters - Local variables - Address of previous stack. * What is the program below going to print? #include unsigned* getMem() { unsigned x[10]; int i; for (i = 0; i < 10; i++) { x[i] = i; } return x; } int main () { unsigned *p1 = getMem(); unsigned *p2 = getMem(); p1[0] = 2; p2[0] = 13; printf("p1 = %d\n", p1[0]); } * What's the problem with that program? * How to fix this program? #include unsigned* getMem() { unsigned *x = new unsigned[10]; int i; for (i = 0; i < 10; i++) { x[i] = i; } return x; } int main () { unsigned *p1 = getMem(); unsigned *p2 = getMem(); p1[0] = 2; p2[0] = 13; printf("p1 = %d\n", p1[0]); } * Is there any other problem with this program? #include unsigned* getMem() { unsigned *x = new unsigned[10]; int i; for (i = 0; i < 10; i++) { x[i] = i; } return x; } int main () { unsigned *p1 = getMem(); unsigned *p2 = getMem(); p1[0] = 2; p2[0] = 13; printf("p1 = %d\n", p1[0]); delete p1; delete p2; } * What would happen if you run this program below: #include unsigned* getMem() { unsigned *x = new unsigned[10]; int i; for (i = 0; i < 10; i++) { x[i] = i; } return x; } int main () { unsigned *p1 = getMem(); p1[0] = 2; printf("p1 = %d\n", p1[0]); delete p1; printf("p1 = %d\n", p1[0]); } * What is the problem of this kind of program? * What is the program below going to print? #include unsigned* getMem() { unsigned *x = new unsigned[10]; int i; for (i = 0; i < 10; i++) { x[i] = i; } return x; } int main () { unsigned *p1 = getMem(); p1[0] = 2; printf("p1 = %d\n", p1[0]); delete p1; unsigned *p2 = getMem(); p2[0] = 13; printf("p2 = %d\n", p2[0]); p1[0] = 19; printf("p2 = %d\n", p2[0]); } * Will we always have enough free space to allocate memory? * What would be the limit of memory that we can allocate? * Can you check it with this program below? #include #include unsigned* getMem(unsigned long long size) { unsigned *x = new unsigned[size]; return x; } int main (int argc, char** argv) { unsigned long long size = atol(argv[1]); unsigned *p1 = getMem(size); printf("p1 = %d\n", p1[0]); } $> clang++ allocSpace_0.cpp $> ./a.out 36000000000000 * How could you handle the memory problem? #include #include #include unsigned* getMem(unsigned long long size) { unsigned *x; try { x = new unsigned[size]; } catch (const std::bad_alloc&) { fprintf(stderr, "Error: can't allocate region\n"); exit(0); } return x; } int main (int argc, char** argv) { unsigned long long size = atol(argv[1]); unsigned *p1 = getMem(size); printf("p1 = %d\n", p1[0]); } * What if you want to call some special function if the error happens? #include #include #include void out_of_memory() { std::cout<<"This function kicks in when we run out of memory\n"; std::exit (1); } unsigned* getMem(unsigned long long size) { unsigned *x; try { x = new unsigned[size]; } catch (std::bad_alloc& excepObj) { std::cout << "Out of Memory error: " << excepObj.what() << '\n'; exit(0); } return x; } int main (int argc, char** argv) { std::set_new_handler(out_of_memory); unsigned long long size = atol(argv[1]); unsigned *p1 = getMem(size); printf("p1 = %d\n", p1[0]); } * Is there any other problem with this program? $> ./a.out # without arguments: Segmentation fault: 11 * What does it mean a segmentation fault? * How can you avoid this kind of problem? #include #include #include void out_of_memory() { std::cerr<<"This function kicks in when we run out of memory\n"; std::exit (1); } unsigned* getMem(unsigned long long size) { unsigned *x; try { x = new unsigned[size]; } catch (std::bad_alloc& excepObj) { std::cerr << "Out of Memory error: " << excepObj.what() << '\n'; exit(0); } return x; } int main (int argc, char** argv) { if (argc <= 1) { std::cerr << "Syntax: cmd \n"; return 1; } else { std::set_new_handler(out_of_memory); unsigned long long size = atol(argv[1]); unsigned *p1 = getMem(size); printf("p1 = %d\n", p1[0]); } } * What else are pointers good for? * Can you implement a swap function, that swaps the contents of two integers? #include void swap(int x, int y) { int tmp = x; x = y; y = tmp; } int main() { int a = 2; int b = 3; std::cout << "a = " << a << ", b = " << b << std::endl; swap(a, b); std::cout << "a = " << a << ", b = " << b << std::endl; } * What is the problem with this implementation? * How can we fix it? #include void swap(int* x, int* y) { int tmp = *x; *x = *y; *y = tmp; } int main() { int a = 2; int b = 3; std::cout << "a = " << a << ", b = " << b << std::endl; swap(&a, &b); std::cout << "a = " << a << ", b = " << b << std::endl; } * Can you implement the swap function without the auxiliary variable? #include void swap(int* x, int* y) { *x = *x ^ *y; *y = *x ^ *y; *x = *x ^ *y; } int main() { int a = 2; int b = 3; std::cout << "a = " << a << ", b = " << b << std::endl; swap(&a, &b); std::cout << "a = " << a << ", b = " << b << std::endl; } * What is the problem with using pointers? * What does this program print? #include void swap(int* x, int* y) { *x = *x ^ *y; *y = *x ^ *y; *x = *x ^ *y; } int main() { int a = 2; int b = 3; std::cout << "a = " << a << ", b = " << b << std::endl; swap(&a, &a); std::cout << "a = " << a << ", b = " << b << std::endl; } * Can you explain what is going on with this program? * What else are pointers good for? * Can you spot any inefficiency in this code below? #include #include struct Point { double x; double y; }; double sum_x(std::vector vec) { double sum = 0; for (auto p: vec) { sum += p.x; } return sum; } int main(int argc, char** argv) { const unsigned long NumPoints = argc == 2 ? atol(argv[1]) : 0; std::vector vec; for (int i = 0; i < NumPoints; i++) { Point p; p.x = i; p.y = i; vec.push_back(p); } std::cout << sum_x(vec) << std::endl; } * How could you improve it? #include #include struct Point { double x; double y; }; double sum_x(std::vector *vec) { double sum = 0; for (auto p: *vec) { sum += p.x; } return sum; } int main(int argc, char** argv) { const unsigned long NumPoints = argc == 2 ? atol(argv[1]) : 0; std::vector vec; for (int i = 0; i < NumPoints; i++) { Point p; p.x = i; p.y = i; vec.push_back(p); } std::cout << sum_x(&vec) << std::endl; } * What do you think is the relative time between these two programs? $> clang++ -O1 -std=c++11 vec_0.cpp -o vec_0.exe $> clang++ -O1 -std=c++11 vec_1.cpp -o vec_1.exe $> time -p ./vec_0.exe 8000000 real 0.52 user 0.34 sys 0.17 $> time -p ./vec_1.exe 8000000 real 0.40 user 0.29 sys 0.10 * Is it necessary to free any memory for this program? * When is it necessary to free memory?