# HW 3 SSE Ivan Christian ## 1 ``` long func(signed long s_a, signed long s_b) { signed long result; result = s_a / s_b; return result; } ``` ``` long func(signed long s_a, signed long s_b) { signed long result; result = s_a % s_b; return result; } ``` The programs are problematic because it doesnt take into account the fact it might result in a division by zero or integer overflow, since both are dealing with signed int. Division operations are susceptible to divide-by-zero errors. Overflow can occur when two's complement signed integer division there's a possibility that the dividend is equal to the minimum (most negative) value for the signed integer type and the divisor is equal to −1. Division: ``` long func(signed long s_a, signed long s_b) { signed long result; if ((s_b == 0) || ((s_a == LONG_MIN) && (s_b == -1))) { /* Handle error */ } else { result = s_a / s_b; } return result; } ``` Remainder: ``` long func(signed long s_a, signed long s_b) { signed long result; if ((s_b == 0 ) || ((s_a == LONG_MIN) && (s_b == -1))) { /* Handle error */ } else { result = s_a % s_b; } return result; } ``` ## 2 ![](https://i.imgur.com/ocyIEd3.png) The realloc function expands the array with the memory as undefined/uninitialized. Thus it have random values in the array. Thus to fix the code, the expanded array has to be initialized with zeroes. This is done by adding a new arguments “old_count” and “new_count” into the “resize_array” function. Using the memset function, we set the new arrays as 0. ``` #include <stdlib.h> #include <stdio.h> #include <string.h> enum { OLD_SIZE = 10, NEW_SIZE = 20 }; int *resize_array(int *array, size_t old_count, size_t new_count) { if (0 == new_count) { return 0; } int *ret = (int *)realloc(array, new_count * sizeof(int)); if (!ret) { free(array); return 0; } if (new_count > old_count) { memset(ret + old_count, 0, (new_count - old_count) * sizeof(int)); } return ret; } void func(void) { int *array = (int *)malloc(OLD_SIZE * sizeof(int)); if (0 == array) { /* Handle error */ } for (size_t i = 0; i < OLD_SIZE; ++i) { array[i] = i; } array = resize_array(array, OLD_SIZE, NEW_SIZE); if (0 == array) { /* Handle error */ } for (size_t i = 0; i < NEW_SIZE; ++i) { printf("%d ", array[i]); } } ``` ## 3 ![](https://i.imgur.com/qhWeTKw.png) The program will result in an undefined behaviour as it will attempt to deallocate the uninitialized pointer variables if there is a bad_alloc error. The try block will not be executed, resulting in i1 and i2 to not be allocated and the program will then attempt to deallocate them. Thus to fix this, we initialize *i1 and *i2 to nullptr at the beginning of the function: ``` void f() { int *i1 = nullptr, *i2 = nullptr; int *i1, *i2; try { i1 = new int; i2 = new int; } catch (std::bad_alloc &) { delete i1; delete i2; } } ``` ## 4 ![](https://i.imgur.com/glMvhoC.png) The program is non-compliant when either the C() constructor fail to initialize a or b in the initializer list, or when init() fails and throws an exception. Thus, when that happens, allocation for a or b will not be released, depending on the point of failure. In this noncompliant code example, the C::C() constructor might fail to allocate memory for a, might fail to allocate memory for b, or might throw an exception in the init() method. If init() throws an exception, neither a nor b will be released. Likewise, if the allocation for b fails, a will not be released. This compliant solution mitigates the potential failures by releasing a and b if an exception is thrown during their allocation or during init(). ``` struct A {/* ... */}; struct B {/* ... */}; class C { A *a; B *b; protected: void init() noexcept(false); public: C() : a(nullptr), b(nullptr) { try { a = new A(); b = new B(); init(); } catch (...) { delete a; delete b; throw; } } }; ```