Lecture – Final C

Ryan Robucci

Table of Contents

Variable Qualifiers

auto

register

extern

static

volatile

The keyword volatile indicates that a variables value may be affected outside of the context/scope of sequential execution of code

It prevents the compiler optimizations based on relationships seen in the local sequential code.

There are three primary situations we consider :

  1. Non-processor writes, when using memory-mapped devices or registers
  2. Avoiding compiler optimizations
  3. Variable sharing, global variables accessed by multiple tasks or by interrupt service routines (multi-threaded code)
    • more in this in a later lecture

Example for changes by outside hardware

baseline:

int a,b; 

int main(){ 

  while (b != 0) { 
    //do nothing    
  } 

  a = 1; 

  return; 
} 

The following code might be seen the same way by the compiler:

... 
int main(){ 
  DDRA = 1; 
  while (PINB != 0) { 
    //do nothing and wait   
  } 
 
  PORTA = 1; 
 
  return; 
} 

However,

Avoid optimization (ex:software delay)

Create a loop that won't disappear after optimization:

void delay(void){ 
    volatile int count; 
    for (count=255;count>0;count--) { 
    } 
    return; 
} 

const

Interpretation of const

Read declaration right to left

declaration How to read right to left Usage Notes
const int x;
int const x
x is an integer constant x is a constant integer Can't modify x
const int * x;
int const * x
x is a pointer to an integer constant x is a pointer to a constant integer Can modify where x points, but can't dereference it to modify the value it points to
int * const x; x is a constant pointer to an integer Can't modify where x points, but can dereference it to modify the value it points to
const int  const x;
int const 
 const x;
x is a constant pointer to an integer constant x is a constant pointer to constant integer Can't modify where x points; can't dereference it to modify the value it points to

Review C++ examples here:
http://www.possibility.com/Cpp/const.html

Concept Declarations, definitions, initializations Attempted Code Allowance by a sample compiler
Mutable Variable int x=1; x=2; Allowed ok
Const Variable const int y=1; y=2; Not allowed bad
(int *) pointing to (const int) int x=1;
const int y=1;
int * ptrX = &x;
ptrX=&y; *ptrX=2; Allowed (with warning)
Allowed, bad
C++ and even C supposedly doesn't allow this implicit conversion between from const int * to int * . may produce warning: assignment discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers] . But at least some C compilers allow the modification of y. The the behavior here should be undefined as constants may not even be stored in writeable memory.
Same as previous ptrX = (int *) &y; *ptrX=2; Allowed bad Even with explicit type casting, the behavior is undefined
(const int *) pointing to int int x=1;
const int y=1;
const int * ptrZ = &y;
ptrZ=&x; Allowed ok
Same as previous ptrZ=&x;
(*ptrZ) = 4;
Not allowed bad
(int * const) pointing to int int x=1;
const int y=1;
int * const ptrZ = &x;
ptrZ=&y; Not allowed bad
Same as previous *ptrZ = 2; Allowed ok

Const, pointers and functions pass by value and reference

Generic Pointers

Function Pointers

Full Example:

int function(float,char);

int main(){ 
    int (* ptrFunction)(float,char) = NULL;
    char c = 1; 
    float f = 2; 

    ptrFunction = &function;

    return (*ptrFunction)(f,c) ;
} 

int function(float f,char c){
  return (int f)/(int c); 
} 
• prototype 
 
 
• declaration of function pointer and initialization to NULL
 
 
 
• assignment of function pointer 
   • same as ptrFunction = function; 
• dereference pointer to call function  

A good tutorial can be found at: http://www.newty.de/fpt/fpt.html

Calling functions with gdb

Code sample with function pointer:

#include <stdlib.h>
#include <stdio.h>

int a ;

void function(){
  a=a+1;
}

int main(){
  void (* ptrFunction)() = &function;
  return 0;
}
 
 
• global a, initialized to 0
 
 
• function that
  • increments a
 
 
 
• assignment of a function pointer
• will break here in gdb

Compile, adding debug info for gdb to associate with lines of source file, optimize only as suitable for debugging

gcc -g -Og -Wall function_pointer.c

Start gdb with the compiled program

robucci@sensi:~/GIT/cmpe311/Lectures/CBasics$ gdb a.out 
Reading symbols from ./a.out...
(gdb) b 12
Breakpoint 1 at 0x1141: file function_pointer.c, line 12.
(gdb) run
Starting program: /tank/robucci/GIT/cmpe311/Lectures/CBasics/a.out 

Breakpoint 1, main () at function_pointer.c:12
12	  return 0;
(gdb) print a
$1 = 0
(gdb) call function
$2 = {void ()} 0x555555555129 <function>
(gdb) print a
$3 = 0
(gdb) call function()
(gdb) print a
$4 = 1
(gdb) call (*function)()
(gdb) print a
$5 = 2
(gdb) call (&function)()
(gdb) print a
$6 = 3
(gdb) call ptrFunction()
(gdb) print a
$7 = 4
(gdb) call (*ptrFunction)()
(gdb) print a
$8 = 5
(gdb) call (&ptrFunction)()
Can't take address of "ptrFunction" which isn't an lvalue.
(gdb) 


 
• set breakpoint on line 12
   just before return
• run to breakpoint
 
 
 
 
• initial value of global is 0
 
✗ incorrect call missing arg. list ()
 
 
 
✓call w/ function name and arg list ()
• verify increment of a
 
• call function using dereference
 
 
• call function using address of
 
 
• call via function pointer
 
 
• call using  pointer and dereference
 
 
✗ incorrect call using pointer and &

Calling with Function Pointers (and Parsing with Clang)

Here are examples that will be used with the tool Clang

function_pointer.c:

int a;

void function(){
  a=a+1;
}

int main(){
  void (* ptrFunction)();
  a=0+1;
  ptrFunction = function;
  (function)();
  (*function)();
  (&function)();
  (ptrFunction)();
  (*ptrFunction)();
  (&ptrFunction)();
  (**function)(); 
  (&*function)();
  (*&function)();
}

Use of clang to dump the Abstract syntax tree (AST):

clang -Xclang -ast-dump -fsyntax-only function_pointer.c 

Only one error is generated, for &ptrFunction

Compilation and AST report full listing (see below for segmentation by line number)
function_pointer.c:16:17: error: called object type 'void (**)()' is not a function or function pointer
  (&ptrFunction)(); //function_pointer.c:15:17: error: called object type 'void (**)()' is not a function or function pointer
  ~~~~~~~~~~~~~~^
TranslationUnitDecl 0x1a06898 <<invalid sloc>> <invalid sloc>
|-TypedefDecl 0x1a07150 <<invalid sloc>> <invalid sloc> implicit __int128_t '__int128'
| `-BuiltinType 0x1a06e30 '__int128'
|-TypedefDecl 0x1a071c0 <<invalid sloc>> <invalid sloc> implicit __uint128_t 'unsigned __int128'
| `-BuiltinType 0x1a06e50 'unsigned __int128'
|-TypedefDecl 0x1a074c8 <<invalid sloc>> <invalid sloc> implicit __NSConstantString 'struct __NSConstantString_tag'
| `-RecordType 0x1a072a0 'struct __NSConstantString_tag'
|   `-Record 0x1a07218 '__NSConstantString_tag'
|-TypedefDecl 0x1a07560 <<invalid sloc>> <invalid sloc> implicit __builtin_ms_va_list 'char *'
| `-PointerType 0x1a07520 'char *'
|   `-BuiltinType 0x1a06930 'char'
|-TypedefDecl 0x1a47960 <<invalid sloc>> <invalid sloc> implicit __builtin_va_list 'struct __va_list_tag [1]'
| `-ConstantArrayType 0x1a07800 'struct __va_list_tag [1]' 1 
|   `-RecordType 0x1a07640 'struct __va_list_tag'
|     `-Record 0x1a075b8 '__va_list_tag'
|-VarDecl 0x1a479d0 <function_pointer.c:1:1, col:5> col:5 used a 'int'
|-FunctionDecl 0x1a47ac8 <line:3:1, line:5:1> line:3:6 used function 'void ()'
| `-CompoundStmt 0x1a47c20 <col:16, line:5:1>
|   `-BinaryOperator 0x1a47c00 <line:4:3, col:7> 'int' '='
|     |-DeclRefExpr 0x1a47b68 <col:3> 'int' lvalue Var 0x1a479d0 'a' 'int'
|     `-BinaryOperator 0x1a47be0 <col:5, col:7> 'int' '+'
|       |-ImplicitCastExpr 0x1a47bc8 <col:5> 'int' <LValueToRValue>
|       | `-DeclRefExpr 0x1a47b88 <col:5> 'int' lvalue Var 0x1a479d0 'a' 'int'
|       `-IntegerLiteral 0x1a47ba8 <col:7> 'int' 1
`-FunctionDecl 0x1a47c90 <line:7:1, line:20:1> line:7:5 main 'int ()'
  `-CompoundStmt 0x1a6a850 <col:11, line:20:1>
    |-DeclStmt 0x1a47e50 <line:8:3, col:25>
    | `-VarDecl 0x1a47de8 <col:3, col:24> col:11 used ptrFunction 'void (*)()'
    |-BinaryOperator 0x1a47ee8 <line:9:3, col:7> 'int' '='
    | |-DeclRefExpr 0x1a47e68 <col:3> 'int' lvalue Var 0x1a479d0 'a' 'int'
    | `-BinaryOperator 0x1a47ec8 <col:5, col:7> 'int' '+'
    |   |-IntegerLiteral 0x1a47e88 <col:5> 'int' 0
    |   `-IntegerLiteral 0x1a47ea8 <col:7> 'int' 1
    |-BinaryOperator 0x1a47f60 <line:10:3, col:17> 'void (*)()' '='
    | |-DeclRefExpr 0x1a47f08 <col:3> 'void (*)()' lvalue Var 0x1a47de8 'ptrFunction' 'void (*)()'
    | `-ImplicitCastExpr 0x1a47f48 <col:17> 'void (*)()' <FunctionToPointerDecay>
    |   `-DeclRefExpr 0x1a47f28 <col:17> 'void ()' Function 0x1a47ac8 'function' 'void ()'
    |-CallExpr 0x1a47fd8 <line:11:3, col:14> 'void'
    | `-ImplicitCastExpr 0x1a47fc0 <col:3, col:12> 'void (*)()' <FunctionToPointerDecay>
    |   `-ParenExpr 0x1a47fa0 <col:3, col:12> 'void ()'
    |     `-DeclRefExpr 0x1a47f80 <col:4> 'void ()' Function 0x1a47ac8 'function' 'void ()'
    |-CallExpr 0x1a48080 <line:12:3, col:15> 'void'
    | `-ImplicitCastExpr 0x1a48068 <col:3, col:13> 'void (*)()' <FunctionToPointerDecay>
    |   `-ParenExpr 0x1a48048 <col:3, col:13> 'void ()'
    |     `-UnaryOperator 0x1a48030 <col:4, col:5> 'void ()' prefix '*' cannot overflow
    |       `-ImplicitCastExpr 0x1a48018 <col:5> 'void (*)()' <FunctionToPointerDecay>
    |         `-DeclRefExpr 0x1a47ff8 <col:5> 'void ()' Function 0x1a47ac8 'function' 'void ()'
    |-CallExpr 0x1a480f8 <line:13:3, col:15> 'void'
    | `-ParenExpr 0x1a480d8 <col:3, col:13> 'void (*)()'
    |   `-UnaryOperator 0x1a480c0 <col:4, col:5> 'void (*)()' prefix '&' cannot overflow
    |     `-DeclRefExpr 0x1a480a0 <col:5> 'void ()' Function 0x1a47ac8 'function' 'void ()'
    |-CallExpr 0x1a48170 <line:14:3, col:17> 'void'
    | `-ImplicitCastExpr 0x1a48158 <col:3, col:15> 'void (*)()' <LValueToRValue>
    |   `-ParenExpr 0x1a48138 <col:3, col:15> 'void (*)()' lvalue
    |     `-DeclRefExpr 0x1a48118 <col:4> 'void (*)()' lvalue Var 0x1a47de8 'ptrFunction' 'void (*)()'
    |-CallExpr 0x1a48218 <line:15:3, col:18> 'void'
    | `-ImplicitCastExpr 0x1a48200 <col:3, col:16> 'void (*)()' <FunctionToPointerDecay>
    |   `-ParenExpr 0x1a481e0 <col:3, col:16> 'void ()':'void ()'
    |     `-UnaryOperator 0x1a481c8 <col:4, col:5> 'void ()':'void ()' prefix '*' cannot overflow
    |       `-ImplicitCastExpr 0x1a481b0 <col:5> 'void (*)()' <LValueToRValue>
    |         `-DeclRefExpr 0x1a48190 <col:5> 'void (*)()' lvalue Var 0x1a47de8 'ptrFunction' 'void (*)()'
    |-ImplicitCastExpr 0x1a486c8 <line:16:3, col:18> '<dependent type>' contains-errors <LValueToRValue>
    | `-RecoveryExpr 0x1a486a0 <col:3, col:18> '<dependent type>' contains-errors lvalue
    |   `-ParenExpr 0x1a482d0 <col:3, col:16> 'void (**)()'
    |     `-UnaryOperator 0x1a482b8 <col:4, col:5> 'void (**)()' prefix '&' cannot overflow
    |       `-DeclRefExpr 0x1a48238 <col:5> 'void (*)()' lvalue Var 0x1a47de8 'ptrFunction' 'void (*)()'
    |-CallExpr 0x1a48798 <line:17:3, col:16> 'void'
    | `-ImplicitCastExpr 0x1a48780 <col:3, col:14> 'void (*)()' <FunctionToPointerDecay>
    |   `-ParenExpr 0x1a48760 <col:3, col:14> 'void ()'
    |     `-UnaryOperator 0x1a48748 <col:4, col:6> 'void ()' prefix '*' cannot overflow
    |       `-ImplicitCastExpr 0x1a48730 <col:5, col:6> 'void (*)()' <FunctionToPointerDecay>
    |         `-UnaryOperator 0x1a48718 <col:5, col:6> 'void ()' prefix '*' cannot overflow
    |           `-ImplicitCastExpr 0x1a48700 <col:6> 'void (*)()' <FunctionToPointerDecay>
    |             `-DeclRefExpr 0x1a486e0 <col:6> 'void ()' Function 0x1a47ac8 'function' 'void ()'
    |-CallExpr 0x1a48840 <line:18:3, col:16> 'void'
    | `-ParenExpr 0x1a48820 <col:3, col:14> 'void (*)()'
    |   `-UnaryOperator 0x1a48808 <col:4, col:6> 'void (*)()' prefix '&' cannot overflow
    |     `-UnaryOperator 0x1a487f0 <col:5, col:6> 'void ()' prefix '*' cannot overflow
    |       `-ImplicitCastExpr 0x1a487d8 <col:6> 'void (*)()' <FunctionToPointerDecay>
    |         `-DeclRefExpr 0x1a487b8 <col:6> 'void ()' Function 0x1a47ac8 'function' 'void ()'
    `-CallExpr 0x1a488e8 <line:19:3, col:16> 'void'
      `-ImplicitCastExpr 0x1a488d0 <col:3, col:14> 'void (*)()' <FunctionToPointerDecay>
        `-ParenExpr 0x1a488b0 <col:3, col:14> 'void ()'
          `-UnaryOperator 0x1a48898 <col:4, col:6> 'void ()' prefix '*' cannot overflow
            `-UnaryOperator 0x1a48880 <col:5, col:6> 'void (*)()' prefix '&' cannot overflow
              `-DeclRefExpr 0x1a48860 <col:6> 'void ()' Function 0x1a47ac8 'function' 'void ()'
1 error generated.

Compilation and AST report segmented by line number:

  1. int a;

    |-VarDecl 0x1a479d0 <function_pointer.c:1:1, col:5> col:5 used a 'int'
    
  2.   

  3. void function(){

    |-FunctionDecl 0x1a47ac8 <line:3:1, line:5:1> line:3:6 used function 'void ()'
    | `-CompoundStmt 0x1a47c20 <col:16, line:5:1>
    |   `-BinaryOperator 0x1a47c00 <line:4:3, col:7> 'int' '='
    |     |-DeclRefExpr 0x1a47b68 <col:3> 'int' lvalue Var 0x1a479d0 'a' 'int'
    |     `-BinaryOperator 0x1a47be0 <col:5, col:7> 'int' '+'
    |       |-ImplicitCastExpr 0x1a47bc8 <col:5> 'int' <LValueToRValue>
    |       | `-DeclRefExpr 0x1a47b88 <col:5> 'int' lvalue Var 0x1a479d0 'a' 'int'
    |       `-IntegerLiteral 0x1a47ba8 <col:7> 'int' 1
    
  4.   a=a+1;

  5. }

  6.  

  7. int main(){

    `-FunctionDecl 0x1a47c90 <line:7:1, line:20:1> line:7:5 main 'int ()'
      `-CompoundStmt 0x1a6a850 <col:11, line:20:1>
    
  8. void (* ptrFunction)();

       |-DeclStmt 0x1a47e50 <line:8:3, col:25>
       | `-VarDecl 0x1a47de8 <col:3, col:24> col:11 used ptrFunction 'void (*)()'
    
  9. a=0+1;

        |-BinaryOperator 0x1a47ee8 <line:9:3, col:7> 'int' '='
        | |-DeclRefExpr 0x1a47e68 <col:3> 'int' lvalue Var 0x1a479d0 'a' 'int'
        | `-BinaryOperator 0x1a47ec8 <col:5, col:7> 'int' '+'
        |   |-IntegerLiteral 0x1a47e88 <col:5> 'int' 0
        |   `-IntegerLiteral 0x1a47ea8 <col:7> 'int' 1
    
  10. ptrFunction = function;

        |-BinaryOperator 0x1a47f60 <line:10:3, col:17> 'void (*)()' '='
        | |-DeclRefExpr 0x1a47f08 <col:3> 'void (*)()' lvalue Var 0x1a47de8 'ptrFunction' 'void (*)()'
        | `-ImplicitCastExpr 0x1a47f48 <col:17> 'void (*)()' <FunctionToPointerDecay>
        |   `-DeclRefExpr 0x1a47f28 <col:17> 'void ()' Function 0x1a47ac8 'function' 'void ()'
    
  11. (function)();

        |-CallExpr 0x1a47fd8 <line:11:3, col:14> 'void'
        | `-ImplicitCastExpr 0x1a47fc0 <col:3, col:12> 'void (*)()' <FunctionToPointerDecay>
        |   `-ParenExpr 0x1a47fa0 <col:3, col:12> 'void ()'
        |     `-DeclRefExpr 0x1a47f80 <col:4> 'void ()' Function 0x1a47ac8 'function' 'void ()'
    
  12. (*function)();

        |-CallExpr 0x1a48080 <line:12:3, col:15> 'void'
        | `-ImplicitCastExpr 0x1a48068 <col:3, col:13> 'void (*)()' <FunctionToPointerDecay>
        |   `-ParenExpr 0x1a48048 <col:3, col:13> 'void ()'
        |     `-UnaryOperator 0x1a48030 <col:4, col:5> 'void ()' prefix '*' cannot overflow
        |       `-ImplicitCastExpr 0x1a48018 <col:5> 'void (*)()' <FunctionToPointerDecay>
        |         `-DeclRefExpr 0x1a47ff8 <col:5> 'void ()' Function 0x1a47ac8 'function' 'void ()'
    
  13. (&function)();

        |-CallExpr 0x1a480f8 <line:13:3, col:15> 'void'
        | `-ParenExpr 0x1a480d8 <col:3, col:13> 'void (*)()'
        |   `-UnaryOperator 0x1a480c0 <col:4, col:5> 'void (*)()' prefix '&' cannot overflow
        |     `-DeclRefExpr 0x1a480a0 <col:5> 'void ()' Function 0x1a47ac8 'function' 'void ()'
    
  14. (ptrFunction)();

        |-CallExpr 0x1a48170 <line:14:3, col:17> 'void'
        | `-ImplicitCastExpr 0x1a48158 <col:3, col:15> 'void (*)()' <LValueToRValue>
        |   `-ParenExpr 0x1a48138 <col:3, col:15> 'void (*)()' lvalue
        |     `-DeclRefExpr 0x1a48118 <col:4> 'void (*)()' lvalue Var 0x1a47de8 'ptrFunction' 'void (*)()'
    
  15. (*ptrFunction)();

        |-CallExpr 0x1a48218 <line:15:3, col:18> 'void'
        | `-ImplicitCastExpr 0x1a48200 <col:3, col:16> 'void (*)()' <FunctionToPointerDecay>
        |   `-ParenExpr 0x1a481e0 <col:3, col:16> 'void ()':'void ()'
        |     `-UnaryOperator 0x1a481c8 <col:4, col:5> 'void ()':'void ()' prefix '*' cannot overflow
        |       `-ImplicitCastExpr 0x1a481b0 <col:5> 'void (*)()' <LValueToRValue>
        |         `-DeclRefExpr 0x1a48190 <col:5> 'void (*)()' lvalue Var 0x1a47de8 'ptrFunction' 'void (*)()'
    
  16. (&ptrFunction)();

        |-ImplicitCastExpr 0x1a486c8 <line:16:3, col:18> '<dependent type>' contains-errors <LValueToRValue>
        | `-RecoveryExpr 0x1a486a0 <col:3, col:18> '<dependent type>' contains-errors lvalue
        |   `-ParenExpr 0x1a482d0 <col:3, col:16> 'void (**)()'
        |     `-UnaryOperator 0x1a482b8 <col:4, col:5> 'void (**)()' prefix '&' cannot overflow
        |       `-DeclRefExpr 0x1a48238 <col:5> 'void (*)()' lvalue Var 0x1a47de8 'ptrFunction' 'void (*)()'
    
    function_pointer.c:16:17: error: called object type 'void (**)()' is not a function or function pointer
      (&ptrFunction)(); //function_pointer.c:15:17: error: called object type 'void (**)()' is not a function or function pointer
      ~~~~~~~~~~~~~~^
    
    1 error generated.
    
  17. (**function)();

        |-CallExpr 0x1a48798 <line:17:3, col:16> 'void'
        | `-ImplicitCastExpr 0x1a48780 <col:3, col:14> 'void (*)()' <FunctionToPointerDecay>
        |   `-ParenExpr 0x1a48760 <col:3, col:14> 'void ()'
        |     `-UnaryOperator 0x1a48748 <col:4, col:6> 'void ()' prefix '*' cannot overflow
        |       `-ImplicitCastExpr 0x1a48730 <col:5, col:6> 'void (*)()' <FunctionToPointerDecay>
        |         `-UnaryOperator 0x1a48718 <col:5, col:6> 'void ()' prefix '*' cannot overflow
        |           `-ImplicitCastExpr 0x1a48700 <col:6> 'void (*)()' <FunctionToPointerDecay>
        |             `-DeclRefExpr 0x1a486e0 <col:6> 'void ()' Function 0x1a47ac8 'function' 'void ()'
    
  18. (&*function)();

        |-CallExpr 0x1a48840 <line:18:3, col:16> 'void'
        | `-ParenExpr 0x1a48820 <col:3, col:14> 'void (*)()'
        |   `-UnaryOperator 0x1a48808 <col:4, col:6> 'void (*)()' prefix '&' cannot overflow
        |     `-UnaryOperator 0x1a487f0 <col:5, col:6> 'void ()' prefix '*' cannot overflow
        |       `-ImplicitCastExpr 0x1a487d8 <col:6> 'void (*)()' <FunctionToPointerDecay>
        |         `-DeclRefExpr 0x1a487b8 <col:6> 'void ()' Function 0x1a47ac8 'function' 'void ()'
    
  19. (*&function)();

        `-CallExpr 0x1a488e8 <line:19:3, col:16> 'void'
          `-ImplicitCastExpr 0x1a488d0 <col:3, col:14> 'void (*)()' <FunctionToPointerDecay>
            `-ParenExpr 0x1a488b0 <col:3, col:14> 'void ()'
              `-UnaryOperator 0x1a48898 <col:4, col:6> 'void ()' prefix '*' cannot overflow
                `-UnaryOperator 0x1a48880 <col:5, col:6> 'void (*)()' prefix '&' cannot overflow
                  `-DeclRefExpr 0x1a48860 <col:6> 'void ()' Function 0x1a47ac8 'function' 'void ()'
    
  20. }

Passing Function Pointers Example

Function Pointer and Generic Pointer Standard Use Case: QSort

_Bool

Complex Numbers

#include <math.h> // provides M_PI
#include <complex.h>

#undef I
#define J _Complex_I

#include <stdio.h>

int main(){
  double complex c=conj(3+3*J);
  printf ("%f %+fj\n",creal(c),cimag(c));
  printf ("magitude:%f angle:%f PI \n",cabsf(c),carg(c)/M_PI);
}
3.000000 -3.000000j 
magitude:4.242640 angle:-0.250000 PI 

Don't forget to link the math libray, last

don't forget to link the math library -lm after the code

gcc complex_code.c  -lm

gcc -lm complex_code.c

restrict (⚠ not standard C++)

Example:

#include <stdio.h> 

/* Prototypes */
int Winner  ( int *, int *);
int Distinct( int *, int *);

int Winner( int * ptrA,int * ptrB){

  if (*ptrA >= *ptrB){

    *ptrB = 0;


    return *ptrA;

  } else{
    *ptrA = 0;
    return *ptrB;
  }
}

int Distinct(int * restrict ptrA,int * restrict ptrB){
  if (*ptrA >= *ptrB){
    *ptrB = 0;
    return *ptrA;
  } else{
    *ptrA = 0;
    return *ptrB;
  }
}

int main(){ 
  int a=2, b=1, c=2, d=2;

  int * ptrD=&d;


  printf("Winner input was %d\n",Distinct(&a,&b));
  printf("Winner input was %d\n",Winner(&c,&c));
  printf("Winner input was %d\n",Distinct(&d,ptrD));
 
  return 0; 
}
 
 
• functions intended to 
  •return larger of two input pointee values
  •and reset the smaller-valued variable
 
• not using restrict keyword
 
• fetch the first and second value
 
• set smaller pointee variable to 0
  •possibly modifying other pointer's target
 
• compiler must (conditionally) fetch 
   already fetched value
 
 
 
  
 
 
• using restrict keyword
• fetch values from memory
• modify one variable
• with restrict compiler may return 
   already fetched value
   potentially already in register
 
 
 
 
 
• all inputs will be the same (2)
 
• example indirection so that a smart
   compiler won't detect duplicate 
   addresses below
✓ Winner input was 2
✗ Winner input was 0
✓ Winner input was 2

Compile:

> gcc -Wall -O3 restrict.c

Call:

> ./a.exe 

Result:

2
0
2

Note on compiler warning (-Wrestrict is included with -Wall):

Compatible Function Definitions with Augmented Qualifiers on Parameters

int func1(int);
int func2(int);
int func3(int);
int func4(int);
int func5(int);
int func6(int);
int func7(int);
int func8(int const);
int func9(const int);

int func10(int *);
int func11(int *);
int func12(int *);
int func13(int *);
int func14(int *);
int func15(int * const);

int main(){
  return 0;
}

//       (int) <-prototype
int func1(int i){
  return i+1;
};

//       (int) <-prototype
int func2(int const i){
  return i+1;
};

//       (int) <-prototype
int func3(const int i){
  i=i+1;
  return i;
};

//       (int) <-prototype
int func4(register int i){
  return i+1;
};

//       (int) <-prototype
int func5(volatile int i){
  return i+1;
};

//       (int) <-prototype
int func6(auto int i){
  return i+1;
};

//       (int) <-prototype
int func7(static int i){
  return i+1;
};

//       (int const) <-prototype
int func8(int i){
  return i+1;
};

//       (const int) <-prototype
int func9(int i){
  return i+1;
};


//        (int *) <-prototype
int func10(int * p){
  return *p;
};


//        (int *) <-prototype
int func11(int * const p){
  return *p;
};

//        (int *) <-prototype
int func12(int * volatile p){
  return *p;
};

//        (int *) <-prototype
int func13(int * restrict p){
  return *p;
};

//        (int *) <-prototype
int func14(int const * p){
  return *p;
};

//        (int * const) <-prototype
int func15(int * p){
  return *p;
};
 
 
 
 
 
 
 
 
• copying a const value is irrelevant
• const ignored
• const int, int const   are equivalent
 
 
 
 
 
• const ignored
 
 
 
 
 
 
 
 
 
 
✓ ok to locally restrict copy of variable
 
 
 
 
✓ ok to locally restrict copy of variable
✗ error: assignment of read-only parameter ‘i’
  • internal use of variable is restricted by 
    keyword const in the definition
 
 
✓ no problem with local suggestion register
 
 
 
 
✓ no problem with local qualifier volatile
 
 
 
 
✗ error: storage class specified for parameter ‘i’
   • you may not affect the lifetime of a parameter
 
 
 
✗ error: storage class specified for parameter ‘i’
 
 
 
 
✓ const from this prototype is ignored
 
 
 
 
✓ const from this prototype is ignored
 
 
 
 
 
✓ const from this prototype is ignored
 
 
 
 
 
✓ no problem locally restricting copy of variable
 
 
 
 
✓ no problem with local qualifier volatile
 
 
 
 
✓ qualifier restrict applies to compilation of function
 
 
 
 
✗ error: conflicting types for ‘func14’
 
 
 
 
• const from prototype is ignored

C99 keyword list

(32 from C89, bold: 5 new to C99)

  1. auto
  2. break
  3. case
  4. char
  5. const
  6. continue
  7. default
  8. do
  9. double
  10. else
  11. enum
  12. extern
  13. float
  14. for
  15. goto
  16. if
  17. inline
  18. int
  19. long
  20. register
  21. restrict
  22. return
  23. short
  24. signed
  25. sizeof
  26. static
  27. struct
  28. switch
  29. typedef
  30. union
  31. unsigned
  32. void
  33. volatile
  34. while
  35. _Bool
  36. _Complex
  37. _Imaginary

Arrays of Pointers and Pointers to Arrays

https://en.cppreference.com/w/c/language/operator_precedence

code parse interpretation
int a[3]; int \leftarrow [3] \leftarrow a; a is a three element array of integers
int (*ptrRow)[3]; int \leftarrow [3] \leftarrow * \leftarrow ptrRow;
(*ptrRow)[3] is an int
ptrRow is a pointer to 3-element array of integers
int *ptrRows[3]; int \leftarrow * \leftarrow [3] \leftarrowptrRows;
ptrRow[3]
*ptrRows[3] is an int is a pointer to an int
ptrRows is a array of three pointers to an int
int m[2][3]; int \leftarrow [3] \leftarrow [2] \leftarrow m; m is an 2-element array of 3-element array of integers
int (*ptrMatrix)[2][3]; int \leftarrow [3] \leftarrow [2] \leftarrow * \leftarrow ptrMatrix
last int: (*ptrMatrix)[1][2]
ptrMatrix is a pointer to 2x3 matrix
int (*ptrRows[2])[3]; int \leftarrow [3] \leftarrow * \leftarrow [2] \leftarrow ptrRows;
last int: (*ptrRows[1])[2]
2-element array of pointers to 3-element-arrays of integers

Multi-Dimensional Arrays with Fixed-Inner-Dimensions


1D:

The number of elements does not affect the location of the existing elements, and so the compiler does not need to know the length to compile the function

Length 3:

0x70000 0x70004 0x70008 arr1d[1] arr1d[2] arr1d[0]

Length 4:

0x70000 0x70004 0x70008 0x7000c arr1d[3] arr1d[1] arr1d[2] arr1d[0]


2D:

The number of rows does not affect the location of the existing elements, and so the compiler does not need to know the number of rows.
THe number of elements in a row does affect the location of elements in following rows, so the compiler uses this.

(Assuming sizeof(int)==4):

Two rows:

0x70000 0x70004 0x70008 0x7000c arr2d[1][2] 0x70010 arr2d[1][0] arr2d[1][1] 0x70014 arr2d[0][1] arr2d[0][2] arr2d[0][0] row 0 row 1

Three rows:

0x70000 0x70004 0x70008 0x7000c arr2d[1][2] arr2d[2][0] arr2d[2][1] arr2d[2][2] 0x70010 arr2d[1][0] arr2d[1][1] 0x70014 0x7001c arr2d[0][1] arr2d[0][2] arr2d[0][0] 0x70024 0x70020 row 0 row 1 row 2


3D:

The number of layers does not affect the location of the existing elements, and so the compiler does not need to know that dimention
The number of elements in a row does, and the number of rows in a layer both affect the location of elements, so the compiler uses these.

(Assuming sizeof(int)==4):

Two layers:

0x7000c 0x70000 0x70004 0x70008 arr3d[0][1][2] arr3d[1][0][0] arr3d[1][0][1] arr3d[1][0][2] arr3d[1][1][0] arr3d[1][1][1] arr3d[1][1][2] 0x70010 arr3d[0][1][0] arr3d[0][1][1] 0x70014 0x7001c arr3d[0][0][1] arr3d[0][0][2] arr3d[0][0][0] 0x70024 0x70028 0x7002c 0x70030 0x70020 layer 1 row 0 layer 0 row 0 row 1 row 1

Three layers:

0x7000c 0x70000 0x70004 0x70008 arr3d[0][1][2] arr3d[1][0][0] arr3d[1][0][1] arr3d[1][0][2] arr3d[1][1][0] arr3d[1][1][1] arr3d[1][1][2] 0x70010 arr3d[2][0][0] arr3d[0][1][0] arr3d[0][1][1] 0x70014 arr3d[2][0][1] 0x7001c arr3d[2][0][2] arr3d[0][0][1] arr3d[0][0][2] arr3d[0][0][0] 0x70030 0x70024 0x70028 0x7002c 0x70020 0x70034 0x70038 0x7003c 0x70044 0x70048 layer 0 0x70040 layer 1 arr3d[2][1][2] arr3d[2][1][0] arr3d[2][1][1] layer 2


Sample code to print a 1D array, which is used by a function that prints a 2D array, which is used by a function that prints a 3D array.

#include <stdio.h>

#define ROW_LENGTH 3
#define NUM_ROWS 2

void print1d(int len,
                int arr_[/*ignored*/])
{
    for (int i=0;i<len;i++) printf("%3d",arr_[i]);
}


void print2d(int numRows,
                int arr__[/*ignored*/][ROW_LENGTH])
{

    for (int r=0;r<numRows;r++) {
        printarr1d(ROW_LENGTH,arr__[r]);
        printf("\n");
    }
}


void print3d(int numLayers,
            int arr___[/*ignored*/][NUM_ROWS][ROW_LENGTH])
{


    for (int l=0;l<numLayers;l++) {
        printarr2d(NUM_ROWS,arr___[l]);
          if (l<(numLayers-1)){
              printf("\n");
          }
    }
}

int main(){
  int arr3d[][2][3]={{{10,20,30}},{}};
  print3d(2,arr3d);
}
 
 
 
 
 
• provided outermost dim.
• 1-D array
 •outer dimension ignored
    a.k.a. length 
 
 
 
• outermost dim provided
• 2-D array
  •outer dimension ignored
    a.k.a. #rows
  •inntermost dimension fixed
    a.k.a. #cols 
 
 
 
 
 
•outermost dim provided
• 3-D array
   •outer dimension ignored
     a.k.a. #layers 
  •inntermost dimensions fixed
    a.k.a. #row,#cols


Result of Printing 3-D array as two (layers) 2-D arrays:

 10 20 30
  0  0  0

  0  0  0
  0  0  0

Array representation within each function:

Revealing Types at Each Level using GDB:
breaking within print1d confirms the above statements

#0  print1d (len=3, arr_=0x7fffffffdbc0) at mda.c
#1  0x0000555555555224 in print2d (numRows=2, arr__=0x7fffffffdbc0) at mda.c
#2  0x0000555555555284 in print3d (numLayers=2, arr___=0x7fffffffdbc0) at mda.c
#3  0x000055555555531a in main () at mda.c
(gdb) select-frame 0
(gdb) print arr_
$1 = (int *) 0x7fffffffdbc0
(gdb) select-frame 1
(gdb) print arr__
$2 = (int (*)[3]) 0x7fffffffdbc0
(gdb) select-frame 2
(gdb) print arr___
$3 = (int (*)[2][3]) 0x7fffffffdbc0
(gdb) 

Variable-Length Arrays (VLA) (⚠ not standard C++)

Some versions of C allow variables to be used for lengths of local arrays (arrays which may be typically implemented on the stack)

int func(int n){
  int arr[n],sum=0;
  for(int i=0;i<n;i++) arr[i]=i;
  for(int i=0;i<n;i++) sum+=arr[i];
  return sum;
}
#include <stdio.h>


void printarr1d(int len,
                int arr_[/*ignored*/])
{

  for (int i=0;i<len;i++) printf("%3d",arr_[i]);
}


void printarr2d(int numRows,
                int rowLength,
                int arr__[/*ignored*/][rowLength])
{

  for (int r=0;r<numRows;r++) {
    printarr1d(rowLength,arr__[r]);
    printf("\n");
  }
}


void printarr3d(int numLayers,
                int numRows,int rowLength,
                int arr___[/*ignored*/][numRows][rowLength])
{

  for (int l=0;l<numLayers;l++) {
    printarr2d(numRows,rowLength,arr___[l]);
    if (l<(numLayers-1)){
      printf("\n");
    }
  }
}

int main()
{
  int arr3d[][2][3]={{{10,20,30}},{}};
  printarr3d(2,2,3,arr3d);
}
 
 
 
 
• 1-D array
 •outer dimension ignored
    a.k.a. length 
 
 
 
 
• variable outermost dim
• variable innermost dim
• 2-D array after inner dim.
 •outer dimension ignored
    a.k.a. #rows 
 
 
 
 
 
 
 
• variable outermost dim
•variable innermost dims
• 3-D array after inner dims
 •outer dimension ignored
    a.k.a. #layers 


gdb session revealing implementation of variable-length arrays:

(gdb) backtrace
#0  printarr1d (len=3, arr_=0x7fffffffdbc0)
#1  0x0000555555555241 in printarr2d (numRows=2, rowLength=3, arr__=0x7fffffffdbc0)
#2  0x0000555555555317 in printarr3d (numLayers=2, numRows=2, rowLength=3, arr___=0x7fffffffdbc0)
#3  0x00005555555553c0 in main ()
(gdb) select-frame 0
(gdb) print arr_
$1 = (int *) 0x7fffffffdbc0
(gdb) select-frame 1
(gdb) print arr__
$2 = (int (*)[variable length]) 0x7fffffffdbc0
(gdb) select-frame 2
(gdb) print arr___
$3 = (int (*)[variable length][variable length]) 0x7fffffffdbc0

Dynamic Allocation of Contiguous Multi-Dimensional Arrays

Full Example:

#include <stdio.h>
#include <stdlib.h>

#define ROW_LEN 3
#define NUM_ROWS 2

int main (){
 int (*arr__)[ROW_LEN]=malloc(sizeof(int)*ROW_LEN*NUM_ROWS);



 if (arr__ != NULL) {
  for (int r = 0; r < NUM_ROWS; r++) {
   for (int c = 0; c < ROW_LEN; c++) {
     arr__[r][c] = r*10+c;
   }
  }




 printf("%p : %2d\n",&arr__[0][0],arr__[0][0]);
 printf("%p : %2d\n",&arr__[0][1],arr__[0][1]);
 printf("%p : %2d\n",&arr__[0][2],arr__[0][2]);
 printf("%p : %2d\n",&arr__[1][0],arr__[1][0]);
 printf("%p : %2d\n",&arr__[1][1],arr__[1][1]);
 printf("%p : %2d\n",&arr__[1][2],arr__[1][2]);

}
 
 
 
 
 
 
 
arr__ is pointer to a 3-element array
arr__ +1 points to the following array
arr__[1] is same as *(arr__ +1) 
 
• check allocation
 
• initialize to
      0     1     2
    10   11   12
• to visit all addresses 
  subsequently offset by size(int)
 
•print:
0x55903e07d2a0 :  0
0x55903e07d2a4 :  1
0x55903e07d2a8 :  2
0x55903e07d2ac : 10
0x55903e07d2b0 : 11
0x55903e07d2b4 : 12
 
  if (arr__ != NULL) {
    for (int r = 0; r < NUM_ROWS; r++) {
      for (int c = 0; c < ROW_LEN; c++) {
        *(&arr__[0][0] + r*ROW_LEN + c) = r*10+c; /* element r,c is at offset (r*ROW_LEN+c)*sizeof(int)*/
      }
    }
  }
  
  printf("%p : %2d\n",&arr__[0][0] + 0*ROW_LEN + 0, *(  &arr__[0][0] + 0*ROW_LEN + 0  ) );
  printf("%p : %2d\n",&arr__[0][0] + 0*ROW_LEN + 1, *(  &arr__[0][0] + 0*ROW_LEN + 1  ) );
  printf("%p : %2d\n",&arr__[0][0] + 0*ROW_LEN + 2, *(  &arr__[0][0] + 0*ROW_LEN + 2  ) );
  printf("%p : %2d\n",&arr__[0][0] + 1*ROW_LEN + 0, *(  &arr__[0][0] + 1*ROW_LEN + 0  ) );
  printf("%p : %2d\n",&arr__[0][0] + 1*ROW_LEN + 1, *(  &arr__[0][0] + 1*ROW_LEN + 1  ) );
  printf("%p : %2d\n",&arr__[0][0] + 1*ROW_LEN + 2, *(  &arr__[0][0] + 1*ROW_LEN + 2  ) );
 
}

0x90000 0x90004 0x90008 arr2d[1][1] arr2d[1][2] ON HEAP 0x9000c arr2d[0][2] arr2d[1][0] 0x90010 0x90014 arr2d[0][0] arr2d[0][1]

Dynamic Allocation of Sub-arrays for Non-Contiguous Multi-Dimensional Arrays

Allocation of 4 sub-arrays on the heap

STACK 0x70000 0x70004 arr2d[3] arr2d[2] 0x70008 0x93030 arr2d[1] 0x9302c 0x93028 arr2d[0] arr2d[3][0] arr2d[3][1] arr2d[3][2] 0x90004 0x90008 arr2d[1][1] arr2d[1][2] arr2d[2][0] arr2d[2][1] 0x90000 0x91004 HEAP arr2d[2][2] arr2d[0][0] arr2d[0][1] arr2d[0][2] 0x9501c 0x95020 0x95024 0x9100c arr2d[1][0] 0x91008

Code:

#include <stdlib.h>

int main(){
  int * arr2d[4];

  arr2d[0] = (int *) malloc(3 * sizeof(int));
  arr2d[1] = (int *) malloc(3*  sizeof(int));
  arr2d[2] = (int *) malloc(3*  sizeof(int));
  arr2d[3] = (int *) malloc(3*  sizeof(int));

  arr2d[3][2]=32;

  return 0;
}

Verification of operation using command-line gdb commands:

> gcc -g -Og -Wall non_contig.c 
> gdb ./a.out -ex "b 13" -ex "run" -ex  "print arr2d[3][2]"
GNU gdb (Ubuntu 10.2-0ubuntu1~20.04~1) 10.2
Copyright (C) 2021 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./a.out...
Breakpoint 1 at 0x11c3: file non_contig.c, line 13.
Starting program: /tank/robucci/GIT/cmpe311/Lectures/CBasics/a.out 

Breakpoint 1, main () at non_contig.c:13
13	  return 0;
$1 = 32
(gdb) 

Ragged Arrays

Jagged or Ragged arrays are multidimensional arrays with irregular member (e.g. row) lengths
The offset of every element is not easily, directly computable, and requires either having a sum of the previous rows or storing the summations as a table/arrays of offsets
element address = [(Σi=0i=r1rowLength[i])+c]×sizeof(int)\left[\left(\Sigma_{i=0}^{i=r-1} \texttt{rowLength}[i]\right)+\texttt c\right] \times \texttt{sizeof}(\texttt{int})

Example:

Offset table with contiguos data block

STACK 0x70000 0x70008 arr2d[2] 5 arr2d[1] 2 0x70004 arr2d[0] 0 0x70008 fisrt 0x9000c 0x90010 0x90014 offset 0x90008 STACK OR HEAP 0x90000 0x90004 offset arr2d[0][0] arr2d[0][1] arr2d[1][0] arr2d[1][1] arr2d[1][2] arr2d[2][0] row 1 (length 3) row 2 (length 1) row 0 (length 2)

to achieve the following in one contiguous array and then copy the last element of row1 to row2

row0[] = {0,1}
row1[] = {12,13,14}
row2[] = {25}

maintain the offset of each row as the sum of the preceding row lengths

storage on stack

int length [] = {2,3,1}
int array[]   = {0,1,  12,13,14,  25}; //on stack
int rowOffsets =  0,    2,         5;
int sourceRow=1;
int sourceCol=3;
int destRow=2;
int destCol=1;
array[rowOffsets[destRow]+destCol] = * (&array[0]+rowOffsets[sourceRow]+sourceCol);

storage on heap

int length [] = {2,3,1}
int * array   = malloc(sizeof(int)*(2+3+1)); //on heap
array[0]=0;
array[1]=1;
array[2]=12;
array[3]=13;
array[4]=14;
array[6]=25;
int rowOffsets =  0,    2,         5;
int sourceRow=1;
int sourceCol=3;
int destRow=2;
int destCol=1;
array[rowOffsets[destRow]+destCol] = * (&array[0]+rowOffsets[sourceRow]+sourceCol);

With base pointer table

STACK 0x70000 0x70008 arr2d[2] arr2d[1] 0x70010 arr2d[0] 0x90008 0x9000c 0x90010 0x90014 STACK OR HEAP 0x90000 0x90004 arr2d[0][0] arr2d[0][1] arr2d[1][0] arr2d[1][1] arr2d[1][2] arr2d[2][0] row 1 (length 3) row 2 (length 1) row 0 (length 2)

Allocated on stack:

int row0[] = {0,1};
int row1[] = {12,13,14};
int row2[] = {20};
int * (arr2d[3]) = {row0,row1,row2};

printf("Before %d,%d\n",arr2d[2][0],arr2d[1][2]);
arr2d[2][0]=arr2d[1][2];
printf("After  %d,%d\n",arr2d[2][0],arr2d[1][2]);
Before 20,14
After  14,14

Allocated on heap:

int * arr1d   = malloc((2+3+1)*sizeof(int)); //on heap
arr1d[0]=0;
arr1d[1]=1;
arr1d[2]=12;
arr1d[3]=13;
arr1d[4]=14;
arr1d[5]=20;
int * (arr2d[3]) = {&arr1d[0],&arr1d[2],&arr1d[5]};

printf("Before %d,%d\n",arr2d[2][0],arr2d[1][2]);
arr2d[2][0]=arr2d[1][2];
printf("After  %d,%d\n",arr2d[2][0],arr2d[1][2]);
▶︎
all
running...
Before 20,14
After  14,14
Before 20,14
After  14,14

Non-contigous Allocation for Ragged Array

With a pointer array

STACK 0x70000 0x70008 arr2d[2] arr2d[1] 0x70010 arr2d[0] 0x91004 arr2d[1][2] arr2d[2][0] 0x90004 0x91008 0x90000 HEAP arr2d[0][0] arr2d[0][1] 0x9501c arr2d[1][0] arr2d[1][1] 0x9100c row 1 (length 3) row 0 (length 2) row 2 (length 1)

int * arr2d[3];

//on heap
arr2d[0] = malloc(sizeof(int)*2); //{0,1};
arr2d[1] = malloc(sizeof(int)*3); //{12,13,14};
arr2d[1] = malloc(sizeof(int)*1); //{25};

arr2d[0][0]=0;
arr2d[0][1]=1;
arr2d[1][0]=12;
arr2d[1][1]=13;
arr2d[1][2]=14;
arr2d[2][0]=20;

printf("Before %d,%d\n",arr2d[2][0],arr2d[1][2]);
arr2d[2][0]=arr2d[1][2];
printf("After  %d,%d\n",arr2d[2][0],arr2d[1][2]);
Before 20,14
After  14,14

Flexible Array Members (FAM) (⚠ not standard C++)

Example:

#include <stdio.h>
#include <stdlib.h>

struct flexArray{
  int myLength;
  signed char values[];
};


void init (struct flexArray * pArr){
  for (int i=0;i< pArr->myLength;i++)
    pArr->values[i]=-1*i;
}

int main(){
  struct flexArray  
    * pArr = malloc(sizeof(int)+3*sizeof(char));
  if (pArr) {

    pArr->myLength=3;

    init(pArr);
  


    for (int i=0;i< pArr->myLength;i++)
      printf("%d ",pArr->values[i]);
  }
}
 
 
 
 
 
• last element is flexible
 
 
 
 
• use of inbuilt length
 
 
 
 
 
• allocate enough for members
• check allocation
 
• initialize
 
✓known offset to values array
   within struct 
✓ability to offset to other elements
   within array
0 -1 -2 

The length of the values array, when placed last, does not affect the position of other members

HEAP 0x70000 0x70004 0x70008 values[1] length values[0]

0x90000 0x90004 0x90008 HEAP 0x9000c values[1] values[2] length values[0]

The same cannot be said if one attempts to allocate an contiguous array of FAM structs,

0x90000 0x90004 0x90008 values[0] values[1] HEAP 0x90000 values[1] length 0x90004 0x90008 length values[0]

HEAP 0x90000 0x90004 0x90008 int values[0] values[1] values[2] 0x9000c values[1] values[2] 0x90010 0x90014 length values[0] 0x9001c 0x90018

One can create a linked list with such nodes

#include <stdio.h>
#include <stdlib.h>

struct flexArray{
  int myLength;
  struct flexArray * next;
  signed char values[];
};

void init (struct flexArray * pArr){
  for (int i=0;i< pArr->myLength;i++)
    pArr->values[i]=-1*i;
}

int main(){
  struct flexArray  
    * pNode0 = malloc(sizeof(int)+3*sizeof(char));
  struct flexArray  
    * pNode1 = malloc(sizeof(int)+5*sizeof(char));


  pNode0->myLength=3;
  pNode1->myLength=5;
  pNode0->next = pNode1;
  pNode1->next = NULL;
  init(pNode0);
  init(pNode1);

  printf("%d",pNode0->next->values[4]);

  return 0;
}

result:

-4

0x90010 0x90014 next values[0] values[1] values[2] myLength 0x9000c 0x90400 0x90404 values[3] values[4] 0x9040c 0x90000 0x90004 0x90410 Heap 0x90414 0x90418 0x9041c myLength next values[0] values[1] values[2] NULL

Or create an array of pointers to varying FAMs like this:

#include <stdio.h>
#include <stdlib.h>

struct flexArray{
  int myLength;
  struct flexArray * next;
  signed char values[];
};

void init (struct flexArray * pArr){
  for (int i=0;i< pArr->myLength;i++)
    pArr->values[i]=-1*i;
}

int main(){
  struct flexArray * ptrs [2];
  ptrs[0] = malloc(sizeof(int)+3*sizeof(char));
  ptrs[1] = malloc(sizeof(int)+5*sizeof(char));

  ptrs[0]->myLength=3;
  ptrs[1]->myLength=5;
  ptrs[0]->next = ptrs[1];
  ptrs[1]->next = NULL;
  init(ptrs[0]);
  init(ptrs[1]);

  printf("%d",ptrs[0]->next->values[4]);

  return 0;
}

result:

-4

0x90000 0x90004 values[2] myLength values[0] 0x90008 values[1] 0x9000c Heap values[1] values[2] 0x70000 0x70008 0x90400 0x90404 Stack 0x90408 0x9040c ptrs[0] ptrs[1] myLength values[0] 0x90414 0x90410 values[4] values[3]

Valgrind, memcheck tool, and check files left open

Valgrind is composed of many tools, one being the memcheck tool

Here is some code which fails to perform a "deep free"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct dt1 {
  int length;
  char * name;
} NODE_t;

void makeNode(NODE_t ** ppNode,const char * name){
  *ppNode=NULL; //default
  int len=strlen(name);
  char * string = malloc(sizeof(char)*(len+1));
  if (string==NULL){
    return;  
  }
  *ppNode = malloc(sizeof(NODE_t));
  if ((*ppNode)==NULL){
    free(string);
    return;
  }

  (*ppNode)->length=len;
  (*ppNode)->name=string;
  strcpy((*ppNode)->name,name);
}

int main(){
  
  NODE_t  * ptrNode;
  const char * name="Robucci";

  makeNode (&ptrNode,name);
  //free(ptrNode->name);
  free(ptrNode);
  ptrNode=NULL;

  printf("leak ?");
  return 0;
}
 
 
 
 
• typedef for data struct
• bookkeeping variable length
• variable-length data pointer
 
• function to create node and 
  modify a pointer to point to it
  requires pointer to a pointer
    (initialize to NULL)
• allocate memory for data array
• verify allocation
• on failure, default NULL pointer 
   is left assigned to node pointer
• allocate memory for struct
• verify allocation
• free data array on failure
 
 
 
• assign length
• assign location of allocation for data
•copy data to allocated memory for member
 
 
 
 
• define a node pointer
• generate data
 
• generate data node using provided data
• omitted member deallocation
• top-level deallocation
• bookkeeping, assign NULL pointer
 

https://valgrind.org/docs/manual/quick-start.html

robucci@sensi:~/GIT/cmpe311/Lectures/CBasics$ valgrind --leak-check=full ./a.out 
==800496== Memcheck, a memory error detector
==800496== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==800496== Using Valgrind-3.17.0 and LibVEX; rerun with -h for copyright info
==800496== Command: ./a.out
==800496== 
leak ?==800496== 
==800496== HEAP SUMMARY:
==800496==     in use at exit: 8 bytes in 1 blocks
==800496==   total heap usage: 3 allocs, 2 frees, 1,048 bytes allocated
==800496== 
==800496== 8 bytes in 1 blocks are definitely lost in loss record 1 of 1
==800496==    at 0x4842839: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==800496==    by 0x109219: makeNode (in /tank/robucci/GIT/cmpe311/Lectures/CBasics/a.out)
==800496==    by 0x109288: main (in /tank/robucci/GIT/cmpe311/Lectures/CBasics/a.out)
==800496== 
==800496== LEAK SUMMARY:
==800496==    definitely lost: 8 bytes in 1 blocks
==800496==    indirectly lost: 0 bytes in 0 blocks
==800496==      possibly lost: 0 bytes in 0 blocks
==800496==    still reachable: 0 bytes in 0 blocks
==800496==         suppressed: 0 bytes in 0 blocks
==800496== 
==800496== For lists of detected and suppressed errors, rerun with: -s
==800496== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
robucci@sensi:~/GIT/cmpe311/Lectures/CBasics$ 

uncomment //free(ptrNode->name);

robucci@sensi:~/GIT/cmpe311/Lectures/CBasics$ valgrind --leak-check=full ./a.out 
==808576== Memcheck, a memory error detector
==808576== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==808576== Using Valgrind-3.17.0 and LibVEX; rerun with -h for copyright info
==808576== Command: ./a.out
==808576== 
leak ?==808576== 
==808576== HEAP SUMMARY:
==808576==     in use at exit: 0 bytes in 0 blocks
==808576==   total heap usage: 3 allocs, 3 frees, 1,048 bytes allocated
==808576== 
==808576== All heap blocks were freed -- no leaks are possible
==808576== 
==808576== For lists of detected and suppressed errors, rerun with: -s
==808576== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
robucci@sensi:~/GIT/cmpe311/Lectures/CBasics$ 

Check for any file left open

#include <stdio.h>
#include <stdlib.h>

FILE * openFile(const char * filename){
  FILE * filePtr = fopen(filename, "r");
  if (filePtr == NULL) {
    printf("error opening file %s\n",filename);
    exit(1);
  }
  return filePtr;
}

int main(int argc, char* argv[])
{
    FILE* filePtr;
    char c;
  
    filePtr = openFile(argv[1]);
    c = fgetc(filePtr);
    printf("First char: %c\n",c);
  
    //fclose(filePtr);
  
    return 0;
}
 
 
 
• function to open file
• attempt to open file
• verification
• error message
• exit
 
• returned file pointer
 
 
 
 
 
 
 
• pass command-line argument to function
• get first character
• print first character
 
• ommitted file close
 
==3670692== Memcheck, a memory error detector
==3670692== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==3670692== Using Valgrind-3.17.0 and LibVEX; rerun with -h for copyright info
==3670692== Command: ./a.out text.txt
==3670692== 
First char: #
==3670692== 
==3670692== FILE DESCRIPTORS: 4 open (3 std) at exit.
==3670692== Open file descriptor 3: text.txt
==3670692==    at 0x49918DB: open (open64.c:48)
==3670692==    by 0x49161C5: _IO_file_open (fileops.c:189)
==3670692==    by 0x4916521: _IO_file_fopen@@GLIBC_2.2.5 (fileops.c:281)
==3670692==    by 0x4908F3D: __fopen_internal (iofopen.c:75)
==3670692==    by 0x4908F3D: fopen@@GLIBC_2.2.5 (iofopen.c:86)
==3670692==    by 0x1091CB: openFile (in ./a.out)
==3670692==    by 0x109224: main (in ./a.out)
==3670692== 
==3670692== 
==3670692== HEAP SUMMARY:
==3670692==     in use at exit: 472 bytes in 1 blocks
==3670692==   total heap usage: 3 allocs, 2 frees, 2,008 bytes allocated
==3670692== 
==3670692== LEAK SUMMARY:
==3670692==    definitely lost: 0 bytes in 0 blocks
==3670692==    indirectly lost: 0 bytes in 0 blocks
==3670692==      possibly lost: 0 bytes in 0 blocks
==3670692==    still reachable: 472 bytes in 1 blocks
==3670692==         suppressed: 0 bytes in 0 blocks
==3670692== Rerun with --leak-check=full to see details of leaked memory
==3670692== 
==3670692== For lists of detected and suppressed errors, rerun with: -s
==3670692== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
 
 
 
 
 
• std out
 
• 4 files
  open at 
  exit
  •text.txt
   left open
 
 
 
 
• function
  that 
  opened
  file

After uncommenting fclose(filePtr) on line 22:

Re Compilation

> gcc -Wall file_left_open.c 

invocation of valgrind

> valgrind --track-fds=yes ./a.out text.txt

Clean result

==3670919== Memcheck, a memory error detector
==3670919== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==3670919== Using Valgrind-3.17.0 and LibVEX; rerun with -h for copyright info
==3670919== Command: ./a.out text.txt
==3670919== 
First char: #
==3670919== 
==3670919== FILE DESCRIPTORS: 3 open (3 std) at exit.
==3670919== 
==3670919== HEAP SUMMARY:
==3670919==     in use at exit: 0 bytes in 0 blocks
==3670919==   total heap usage: 3 allocs, 3 frees, 2,008 bytes allocated
==3670919== 
==3670919== All heap blocks were freed -- no leaks are possible
==3670919== 
==3670919== For lists of detected and suppressed errors, rerun with: -s
==3670919== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
 
 
 
 
 
• std out
 
✓ 3 files
  (clean)

Valgrind options and tools

Linking the math library

Many common math functions are available most platforms when the math library (libm) is included in the linking.

Use the -lm option AFTER the source code

gcc my_computations.c -lm

List of functions:
https://docs.oracle.com/cd/E86824_01/html/E54772/libm-3lib.html

Some note that platform-optimized varients of math functions may be availble in a separate library, and using libm as a fallback

https://hpc.llnl.gov/software/mathematical-software/libm#:~:text=LIBM is the standard C,optimized implementations of LIBM functions.

Extra Warnings

gcc option -Wextra provides addtional warnings, though most apply only to C++

One useful additional check on C code is case fallthrough:

#include <stdlib.h>
#include <stdio.h>

int main(){
  char s[]="hi";
  switch(s=="hi") {
  case 1:
    printf("greetings");
  case 0:
    return 1;
  }

}

Compile with both Wall and Wextra enabled

> g++ -Wall -Wextra -x c extra.c 

gcc manpage:

-Wextra
    This enables some extra warning flags that are not enabled by -Wall. 
    (This option used to be called -W.  The older name is still supported, 
    but the newer name is more descriptive.)
    -Wclobbered -Wcast-function-type -Wdeprecated-copy (C++ only) -Wempty-body 
    -Wignored-qualifiers -Wimplicit-fallthrough=3 -Wmissing-field-initializers 
    -Wmissing-parameter-type (C only) -Wold-style-declaration (C only) 
    -Woverride-init -Wsign-compare (C only) -Wstring-compare 
    -Wredundant-move (only for C++) -Wtype-limits -Wuninitialized 
    -Wshift-negative-value (in C++03 and in C99 and newer)
    -Wunused-parameter (only with -Wunused or -Wall) 
    -Wunused-but-set-parameter (only with -Wunused or -Wall)
    The option -Wextra also prints warning messages for the following cases:
    *   A pointer is compared against integer zero with "<", "<=", ">", or ">=".
    *   (C++ only) An enumerator and a non-enumerator both appear in a conditional expression.
    *   (C++ only) Ambiguous virtual bases.
    *   (C++ only) Subscripting an array that has been declared "register".
    *   (C++ only) Taking the address of a variable that has been declared "register".
    *   (C++ only) A base class is not initialized in the copy constructor of a derived class.