Skip to main content

Dynamic Allocation in C

Dynamic Allocation

1. Calling Function 

-When calling a function, the array is created on the stack. And the variables on the stack exit only until the function ends. After it ends, the stack will be used for a new stack frame. 

2. Dynamic memory allocation

-Dynamic memory allocation allows a programmer to request a specific amount memory to be allocated on the heap. Since it is not in the stack, it is not freed when the function returns.

-Instead, the programmer must explicitly free the memory when using it. 

-The heap changes sizes as program runs.

-The memory allocation library manages the free memory in the heap. When a request cannot be satisfied from the existing free blocks of memory, the allocation library requests that the upper boundary of the heap be increased. 

-We can use realloc function to allocate a new block of memory, copying original one to that place and free the old one. 

3. Dynamic memory allocation

-We can use malloc to allocate dynamic memory.
void * malloc(size_t size);
void * means the return value has type void *: a pointer to something.
size_t is an alias for an appropriately-size unsigned int type
size is how many bytes to allocate

-malloc requires that you tell it how much memory you need in bytes! But writing a specific number of bytes is incorrect in almost all cases. 
    1) Portability. The ability to compile code in different system and still have it work correctly. 
    2) Maintainability. 

-We should let the C compiler calculate the size of the type for us using the sizeof operator
malloc(sizeof(int))

-The sizeof operator is evaluated at compile time, not run-time.

-A good way to use call malloc is to use
int * myArray = malloc (6 * sizeof())
In this way, the call to malloc would continue to be correct with no additional changes. 

-When we use
p = malloc (6 * sizeof(*p))
malloc function evaluates a pointer to that memory location. And when we perform a assignment statement, we are taking the address location of new area in memory, and we are writing that address to p.

4. Failure

-If there's no enough space for heap to fulfill current request, malloc will return NULL instead of a valid pointer. So, it is a good idea to check if the value is NULL or not before using this pointer.

5. Fixing initArray

-After learning about malloc, we can use it to fix previous problem.
int * initArray (int howLarge) {
    int * array = malloc (howLarge * sizeof (*array));
    if(array != NULL) {
        for(int i = 0; i < howLarge; i++) {
            array[i] = i;
        }
    }
    return array;
}

6. Shallow vs Deep Coping 

polygon_t * o2 = p1; 
It just copies pointer, not he object it points to. 

-If we use malloc, we could create a copy, but its sub-element points to the same as original ones. If we change original sub-element, then it will also change. This is shallow copy.

-If we want two completely distinct polygon objects, we should make a deep copy.
polygon_t * p2 = malloc(sizeof(*p2));
p2 -> num_points = p1 -> num_points;
p2 -> points = malloc(p2->num_points * sizeof(*p2->points));
for(size_t i = 0; i < p2 -> num_points; i++) {
    p2->points[i] = p1->points[i];
}

COMPARASION

-polygon_t * p2 = p1;  //points to the same address

-polygon_t * p2 = malloc(sizeof(*p2)); //different object, but sub-element points to the same address
*p2 = *p1;

And the statement in previous section. //deep copy

-segmentation fault occurs when a program attempts to access a memory location that it is not allowed to access
-dangling points means you want to access a location that has been deallocated in memory.

7. free

-Memory on the heap must be explicitly freed by the programmer. We use free to free the memory.
void free (void * ptr)
Here void means memory is freed, but user receives no explicit feedback
void * means the type matches the return value of malloc

-If p points at something other than the start of a block in the heap, it is an error and bad things will happen.

-We should firstly free sub-pointers in the pointer, then free the pointer.

8. Memory Leaks

-When you lose all references to a block of memory, and the memory is still allocated, this is said to be leaked memory. 

-Programs that go slower after one day or two may be the result of leaked memory.

-We should run our programs in Valgrind to be sure we get the following message:
    All heap blocks were freed -- no leaks are possible

9. Common Problems with free

-1. Free the same block of memory more than one time: double freeing

-2. Free something that is not at the start of the block return by malloc

-3. Free a variable that is not on the heap

-When we request for 4 places, for example, the malloc gives us 8,  the first four is the information of this allocation, and the last four is your data. 

10. realloc

-If we need more than what we wanted before, we should use realloc.

void * realloc (void * ptr, size_t size);
void* means the same return type as malloc; location of new memory
ptr means the pointer to the original memory allocated via malloc
size is the new size request

-realloc effectively resizes a malloc region of memory.

-Same as malloc, if fail, return NULL, if success, return the address

-It doesn't have to be near the original location in the heap

-It will create a new address with new length, and then copy original values to it and then free original values.

11. getline

-We can use getline to read a string of any length and allocate memory that lasts after the function returns. 
ssize_t getline (char ** linep,
                        size_t * linecapp,
                        FILE * stream);

- Here size_t returns the number of characters written(counting the '\n', not counting '\0') or -1 if an error occurs.
- char ** linep means the pointer to a malloced buffer where the line will be written to (or NULL if getline should perform malloc)
- size_t * linecapp means the pointer to the size of the malloced buffer
- FILE * stream is the pointer to the file to be read

- It will use linep if it is not NULL, to start with. But the buffer is not long enough, getline realloc the buffer as needed.

-If linep is NULL, getline malloc a new buffer.

-getline function returns -1 on an error (including end of file) and the number of bytes read on success. 

-return type is ssize_t, which stands for "signed size_t" - that is, the signed integer type, which has the same number of bytes as size_t (so it can return -1)

summary& understanding  after reading <<All of Programming>> chapter 12

Comments

Popular posts from this blog

Templates

  Template - Polymorphism is the ability of the same code to operate on different types. This ability to operate on multiple types reduces code duplication by allowing the same piece of code to be reused across the different types it can operate on. - Polymorphism comes in a variety of forms. What we are interested in at the moment is parametric polymorphism, meaning that we can write our code so that it is parameterized over what type it operates on.  -That is, we want to declare a type parameter T and replace int with T in the above code. -Then, when we want to call the function, we can specify the type for T and get the function we desire. C++ provides parametric polymorphism through templates. Templated Functions - We can write a templated function by using the keyword template followed by the template parameters in angle brackets (<>). - Unlike function parameters, template parameters may be types, which are specified with typename where the type of the parameter wo...

useMemo的使用场景

 useMemo是用来缓存 计算属性 的。 计算属性是函数的返回值,或者说那些以返回一个值为目标的函数。 有些函数会需要我们手动去点击,有些函数是直接在渲染的时候就执行,在DOM区域被当作属性值一样去使用。后者被称为计算属性。 e.g. const Component = () => { const [ params1 , setParams1 ] = useState ( 0 ); const [ params2 , setParams2 ] = useState ( 0 ); //这种是需要我们手动去调用的函数 const handleFun1 = () => { console . log ( "我需要手动调用,你不点击我不执行" ); setParams1 (( val ) => val + 1 ); }; //这种被称为计算属性,不需要手动调用,在渲染阶段就会执行的。 const computedFun2 = () => { console . log ( "我又执行计算了" ); return params2 ; }; return ( < div onClick = { handleFun1 } > //每次重新渲染的时候我就会执行 computed: { computedFun2 () } </ div > ); }; 上面的代码中,在每次点击div的时候,因为setParams1的缘故,导致params1改变,整个组件都需要重新渲染,也导致comptedFunc2()也需要重新计算。如果computedFunc2的计算量很大,这时候重新计算会比较浪费。 可以使用useMemo: const Com = () => { const [ params1 , setParams1 ] = useState ( 0 ); const [ params2 , setParams2 ] = useState ( 0 ); //这种是需要我们手动去调用的函数 const handleFun1 ...

Valgrind

  Using Valgrind to check memory How to use Valgrind -To valgrind the program, run the valgrind command and give it our program name as an argument. -For example, if we want to run ./myProgram hello 42, we can simply run Valgrind ./myProgram hello 42.  Uninitialized Values -When we run the program, we may use uninitialized values. It needs to be fixed. Valgrind can tell us about the use of uninitialized values. But it only tell when the control flow of the program depends on the unitialized value. For example, uninitialized value appears in the conditional expression of an if, or a loop, or in the switch statement. -If we want to know where the uninitialized value is from, we can use Valgrind    --track-origins=yes ./myProgram -Using -fsanitize=address can find a lot of problems that Memcheck cannot.  -We can use Valgrind with GDB to debug. We can run: --vgdb=full --vgdb-error=0., then Valgrind will stop on the first error that it encounters and give control to ...