Table of Contents (chapter)
1. introduction:
     pointer and memory, pointer size and types, pinter operations, common user of pointers.
2. Dynamic Memory Management in C:
     Dynamic memory allocation, function, free,
3. Pointers and Functions
     stakc and heap, passing, funciton pointers.
4. Pointers and Arrays
     pionter notation, malloc, 
5. Pointers and Strings
6. Pointers and Structures
7. Security issues and improper use of pointers
8. odds and ends

=================================================================================================
Chapter 1 Introduction

1. The basic concept of  a pointer: it is a variable that stores the address of a memory location.
2. The key to comprehending points is understanding how memory is managed in a C program.
3. Contents of this chapter:
     (1) first section: declare, basic pointer operators, the concept of null;
     (2) second section: pointer size and types, memory models, pointer operators: pointer arithmetic, pointer comparisons
     (3) last section: common uses of pointers, constants and pointers, page25

Section 1: declaration, basic pointer operators, the concept of null

1.1.1 Pointer and Memory
When C program is compiled, it works with three type of memory :
(1) Static / Global
     statically declared variables. global variables.  remain in existence until the program terminates.
(2) Automatic
     declared within a function, created when a function is called.
(3) Dynamic
     allocated from the heap and can be released as necessary.



1.1.2 Why you should become proficient with pointers
The malloc and free functions are used to allocate and release dynamic memory, respectively.
ps: in the new C standard, C11, variable size arrays are supports.


1.1.3 Declaring Pointers
     int num
     int *pi

     num, pi located at address 100 and 104,  both occupy 4 bytes.



1.1.4 How to Read a Declaration
     read them backward.
     const int *pci
     
     1. pci                 : pci is a variable
     2. *pci                : pci is a pointer variable
     3. int *pci           : pci is a pointer variable to an integer
     4. const int *pci : pci is a pointer variable to a constant integer

1.1.5 Address Operator
     The address of operator, &, will return its operand’s address.
     
     num = 0;
     pi = #


     &num = 100;
     num = 0;
     &pi = 104;
     pi = 100;
     *pi = num = 0;
     

     equals to:
     int num;
     int *pi = #

     num = 0;
     pi = num; //error: invalid conversion from ‘int’ to ‘int *'


     pi = (int *)num;

Good Practice:
     int num;
     int *pi;
     pi = #

1.1.6 Displaying Pointer Values
    

     

    


PS printf
%x  hexadcimal
%o  octal number
%p in an implementation-specific manner; typically as a hexadecimal number.
%u unsigned int
%lu  long unsigned, mainly equavelant to %zu
%d signed integer
%s  print string, pass the pointer



1.1.7 Dereferencing a Pointer Using the Indirection Operator
     The indirection operator *, returns the value pointed to by a pointer variable.
    

*pi = 200


1.1.8 Pointer to Functions
     void (*foo)();
     chapter3;

1.1.9 The Concept of Null
 (1) The null concept;
     When NULL is assigned to a pointer, it means the pointer does not point to anything.
     The null concept refers to the idea that a pointer can hold a special value that is not equal to another pointer.
     Two null pointers will always be equal to each other.
 (2) The null pointer constant;
     may or may not be a constant zero.
 (3) The NULL macro;
     is a constant integer zero cast to a pointer to void.
     #define NULL ((void *)0)
     
     NULL 0  
 (4) The ASCII NUL
     is defined as a byte containing all zeros.  0000 0000 ,  it is equivalent to the character ‘\0’, which evaluates to the decimal value zero. 
 (5) A null string;
     a sequence of characters terminated by a zero value. “adsafasfdafdsfaf\0"
 (6) The null statement 
     semicolon

pi = NULL; // it’s not the NULL macro, it means assign NULL to a pointer.   it will interpreted as the binary zero..
A null pointer  and an uninitialised pointer are different. null : does not reference any location, uninitialised : can contain any value;

Interestingly:  we can assign a zero to a pointer, but we can not assign any other integer value.
     pi = 0;
     pi = NULL;
     pi = 100; // Syntax error
     pi = num; // Syntax error

1.1.10 To NULL or not to NULL
     using NULL or 0 is preference.
     NULL should not be used in contexts other than pointers.
     

PS: We are accustomed to overloaded operators:
     (1) * , asterisk,   used to declare a pointer, to dereference a pointer, or to multiply.
     (2) 0, zero, used to assign null pointer, or the integer zero.

1.1. 11 Pointer to void
     A pointer to void is a general-purpose pointer used to hold references to any data type.
     void *pv;

     Two interesting properties:
     (1) A pointer to void will have the same representation an memory alignment as a pointer to char;
     (2) A pointer to void will never be equal to another pointer. 

     Any pointer can be assigned to a pointer to void.It can then be cast back to its original pointer type.

     void *pv = pi;
     pi = (int *)pv;

     // sizeof
     size_t size = sizeof(void *); // Legal
     size_t size = sizeof(void); // Illegal

1.1.12 Global and static pointers
     If a pointer is declared as global or static , it is initialised to NULL when the program starts.
    

     Static and global variables are frequently placed in a data segment separate from the data segment used by the stack and heap.
   
 
(2) second section: pointer size and types, memory models, pointer operators: pointer arithmetic, pointer comparisons

1.2.1 Pointer size and types
     pointer to data:  same size;
     pointer to function: different from the size of a pointer to data.

     e.g. Windows: 32 or 64 bits, 4 or 8 bytes
            DOS/Win3.1  16 or 32 bits, 2 or 4 bytes;

1.2.2 Memory Models
  
1.2.3 Predefined Pointer-Related Types
     Four predefined types:
     (1) size_t   :   safe type for sizes
     (2) ptrdiff_t :  created to handle pointer arithmetic
     (3) intptr_t   and   uintprt_t  :   used to storing pointer addresses;

1.2.3.1 Understanding size_t
     The type size_t represents the maximum size any object can be in C.
     It is an unsigned integer.
     The size_t type is used as the return type for the sizeof operator and as the argument to many functions, including malloc and strlen, among others.

GOOD PRACTICE
     it is good practice to use size_t when declaring variables for sizes such as the number of characters and array indexes.

sizet_t is implementation-specific in stdio.h and stdlib.h defined as follows:
     
     #ifndef  __SIZE_T
     #define _SIZE_T
     typedef unsigned int size_t;
     #endif

     Normally, the maximum possible value for size_t is SIZE_MAX.

     size_t can be used to store a pointer, but it is not a good idea .  intptr_t is a better choice.

     printf(“%zu\n”, sizet);   // instead of using %d

   

     

1.2.3.2 using sizeof operator with pointers
     printf("Size of *char : %d\n", sizeof(char*));
     Size of *char : 8

1.2.3.3 Using intptr_t and uintptr_t
     The types intptr_t and uintptr_t are used to storing pointer addresses.
     For most operations intptr_t is preferred

     include <stdint.h>
     intptr_t *pi1 = &num;

     
PS: If you ever need to cast a pointer into an integer type, always use intptr_t. ----stackoverflow

1.2.4 Pointer Operator
     pointer arithmetic and comparisons.


1.2.5 Pointer Arithmetic
     (1) Adding an integer to a pointer; 
     (2) Subtracting an integer from a pointer ;
     (3) Subtracting two pinter from each other;
     (4) Comparing pointers;

1.2.5.1 Adding an integer to a pointer
     When we add an integer to a pointer, the amount added is the product of the integer times the number of bytes of the underlying data type.
     common primitive data type sizes:
    
    
    
     

PS: When an array name is used by itself, it returns the address of an array, which is also the address of the first element of the array.

1.2.5.2 Pointers to void and addition
     Syntax warning, incremented by four.

1.2.5.3 Subtracting an integer from a pointer
     it’s same with adding

1.2.5.4 Subtracting two pointers
     The difference by subtracting two pointers is not normally very useful except for determine the order of elements in an array.
    

     ptrdiff_t is a portable way to express the difference between two pointers.

1.2.5. Comparing pointers
     used to determine the relative ordering of the array’s elements.

1.3 Common Uses of Pointers, constants and pointers
 
1.3.1 Multiple Levels of Indirection
     pinter to pointer…, double pointer.
     main( argc, argv) 
     




1.3.2 Constants and Pointers

1.3.2.1 Pointer to a constant
     This means the pointer cannot be used to modify the value it is referring .
     The pointer value  is not constant.

     const int limit = 100;
     const int *pci = &limit;



     The declaration of pci as a pointer to a constant integer means:
     (1) pci can be assigned to point to different constant integers;
     (2) pci can be assigned to point to different non constant integers ;
     (3) pci can be dereferenced for reading purposes;
     (4) pci cannot be dereferenced to change what it points to.
 
 by the way: const int *pci = int const *pci;

1.3.2.2 Constant pointers to nonconstants
     int num = 5;
     int *const cpi = &num;


     (1) cpi must be initialised to a non constant variable;
     (2) cpi can not be modified;
     (3) The data pointed to by cpi can be modified


1.3.2.3 Constant pointers to constant
     const int * const cpci = &limit; 
     
1.3.2.4 Pointer to (constant pointer to constant)
     const int * const cpci = &limit;
     const int * const *pcpci;

    


=================================================================================================

Chapter 2 : Dynamic Memory Management in C

     A C program executes within a runtime system, which supports the stack and heap.
     Memory management is central to all programs.

     implicit memory management:
     (1) variables: allocated to the enclosing function’s stack frame;
     (2) global and static variables: memory is placed in the application’s data segment; 

PS: C99 VLA Variable Length Arrays, the size is determined at runtime and not at compile time. However, once created, arrays still do not change size.

     contents of this chapter:
     section 1: memory allocated and freed, malloc, realloc, free
     section 2: dangling pointers problem
     section 3: alternate techniques for managing memory


Section 1: memory allocated and freed, malloc, realloc, free

2.1.1 Dynamic Memory Allocation
     The basic steps used for dynamic memory allocation in C are:
     (1) Use a malloc type function to allocate memory;
     (2) Use this memory to support the application;
     (3) Deallocate the memory using the free function.

    

    
     malloc: The malloc function single argument specifies the number of bytes to allocate.  If successful, it returns a pointer to memory allocated from the heap. If it fails, it returns a null pointer.

     Each time the malloc function is called, a corresponding call to the free function must be made when the application is done with merry to avoid memory leaks.

GOOD PRACTICE:  Always assign NULL to a freed pointer.
     


    
     


     

    

2.1.2 Memory Leaks
     A memory leak occurs when allocated memory is never used again but is not freed.  This can happen when:
     (1) The memory’s address is lost;
     (2) The free function is never invoked though it should be. also called a hidden leak.

     

2.1.2.1 losing the address
     
     



2.1.2.2  Hidden memory leaks
     programmer oversight..

2.1.3  Dynamic Memory Allocation Functions 
     (1) malloc
     (2) realloc
     (3) calloc
     (4) free
     in the sodlib.h


? The memory allocated will be aligned according to the pointer’s data type. For example, a four-type integer would be allocated on an address boundary evenly divisible by four…  ??? why???


2.1.4 Using the malloc function
     from the heap;  the number of bytes is its single argument;  return type is a pointer to void.
     if memory is not available, NULL is returned.

     prototype: 
          void * malloc(size_t);
     typical use:
          int *pi = (int*) malloc(sizeof(int));
     
     The following steps are performed when the malloc function is executed:
     (1) Memory is allocated from heap;
     (2) The memory is not modified or otherwise cleared;
     (3) The first byte’s address is returned.

GOOD PRACTICE: Since the malloc function may return a NULL value if it is unable to allocate memory...
     int *pi = (int*) malloc(sizeof(int));
     if (pi != NULL) {
          // Pointer should be good
     } else {
          // Bad pointer
     }

2.1.4.1 To cast or not to cast
    explicit casting is a good practice.
   
INTERESTING: By default, C assumes functions return an integer… if you fail to include a prototype for malloc, it will complain when you try to assign an integer to a pointer.

2.1.4.2 Failing to allocate memory
     int *pi;
     printf(“%d\n”, *pi);

 
2.1.4.3 Not using the right size for the malloc function
     double *pd = (double*) malloc(NUMBER_OF_DOUBLES * sizeof(double);

2.1.4.4 Determining the amount of memory allocated
     dependente
2.1.4.5 Using malloc with static and global pointers
     You cannot use a function call when initializing a static or global variable.
     static int *pi = malloc(sifzeof(int)); // error

     static inside a function:
          static int *pi;
          pi = malloc(sizeof(int));

          it’s ok.  but not good for global, since it is outside the function.
     

2.1.5 Using the calloc Function
     The calloc funciona will allocate and clear memory at the same time. calloc = clear allocation
     prototype:
          void * calloc(size_t numElements, size_t elementSize);

     free means its contents are set to all binary zeros.
     Originally, this function was used to aid in the allocation of memory for arrays.

     if calloc is unable to allocate memory, a null pointer is returned and the global viable, errno, is set to ENOMEM(out of memory).
     ENOMEM is POSIX error code...
     
     typical use:
          int *pi = calloc(5, sizeof(int));

     equals:
          int *pi = malloc(5 * sizeof(int));
          memset(pi, 0, 5 * sizeof(int)); 
     // The memset function will fill a block with a value(0). 

2.1.6 Using the realloc Function
     The realloc function will decrease or increace the amount of memory allocated to a pointer.
     prototype:
          void *realloc(void *ptr, size_t size);


    

    
    
     we did not change the string to fit into the eight-byte block.

2.1.7 The alloca function and Variable length arrays
     The alloca function allocates memory by placing it in the stack frame for the function. When the function returns, the memory is automatically freed.
     Variale Length Arrays(VLAs), allocation of memory is done at runtime and memory is allocated as part of the stack frame. 

2.1.8 Deallocating Memory using the free Function
     prototype:
          void free(void *ptr);
     typical use:
          int *pi = (int*) malloc(sizeof(int));
          free(pi);



     it’s dangling pointer, pi….

2.1.9  Assigning NULL to a Freed Pointer
     int *pi = (int*) malloc(sizeof(int));
     free(pi);
     pi = NULL;
    

2.1.10 Double Free
     int *pi = (int*) malloc(sizeof(int));
     *pi = 5;
     free(pi);
     ...
     free(pi); //runtime exception

another:
     p1 = (int*) malloc(sizeof(int));
     int *p2 = p1;
     free(p1);
     ...
     free(p2); // runtime exception

2.1.11 The Heap and System Memory
     when free happens, the heap manager does not necessarily return memory to the OS.

2.1.12 Freeing Memory upon Program Termination
     Whether memory should be deallocated prior to program termination is application specific.


section 2: dangling pointers problem

     If a pointer still references the original memory after it has been freed, it is called a dangling pointer.
     
     example1:
     int *pi = (int*) malloc(sizeof(int));
     *pi = 5;
     printf(“*p : %d\n”, *pi);
     free(pi);

     *pi = 10; // the result of this action is unpredictable .



     example2:
     int *pi;

     {
          int tmp = 5;
          pi = &tmp;
     }
     // pi is now a dangling pointer
2.2.1 Dealing with Dangling Pointers
     (1) Setting a pointer to NULL after freeing it.
     (2) Writing special functions to replace the free function. (page 70)
     (3) Some system (runtime/debugger) will overwrite data when it is freed.  Visual Studio use 0xCC, 0xCD, or 0xDD
     (4) Use third-party tools to detect dangling pointers and other problems.

     print pointer address  or using assert macro.

2.2.2 Debug version support for detecting memory leaks
     Microsoft

section 3: alternate techniques for managing memory

2.3.1 Dynamic Memory Allocation Technologies
     ...
2.3.2 Garbage Collection in C
     ...
2.3.3 Resource Acquisition is initializtion
     RAII is a technique in C++
2.3.4 Using Exception Handlers
     Exception handler is not a standard part of C.  Microsoft VS C has.