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 :
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,
PINB is defined as *(volatile uint8_t *)(0x20+0x03)PORTA and DDRA are similar with a different addressCreate a loop that won't disappear after optimization:
void delay(void){ volatile int count; for (count=255;count>0;count--) { } return; }
const float PI=3.14159
PI=3.14;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 |
Passing by reference (using pointers) is required to modify data structures from the caller in the function.
Passing by reference (using pointers) is better than passing large structures by value to functions even when not modifying contents.
For safety and clarity of function intent, always use the const keyword when intending to pass by reference for read-only purposes:
function AddPoly(poly_t * polySum, const poly_t * polyA, const poly_t * polyB); function AddPoly(poly_t polySum[], const poly_t polyA[], const poly_t polyB[]);
void PrintArray(void * ptr, char type, int numElements){ char * c = (char *) ptr; int * i= (int *) ptr; float * f= (float *) ptr; int j=0; switch (type) { case 'c': for(;j<numElements; j++) printf("%c\n",*(c+j)); break; case 'i': for(;j<numElements; j++) printf("%d\n",*(i+j)); break; case 'f': for(;j<numElements; j++) printf("%f\n",*(f+j)); break; } }
Functions identifiers are like array identifiers in that they refer to a location in memory where code is stored like an array identifier refers to a location in memory where an array of data is stored.
Variable pointers to functions can be created, modified, and passed as arguments to other functions.
Lets look at some declarations and assignments and then an example function that has a function pointer as an argument:
int (* ptrFunction)(float,char);
Since * has a lower precedence than the () that is appended to function names, we must be careful.
(arguments) with high precedence, move to the left, and interpret reading right-to-left
int * ptrFunction(float,char);
(int *) ptrFunction (float,char);int (* ptrFunction)(float,char);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
Explicit Dereference Operator is Optional with Function Calls
(functions are all pointers anyway)
return (*ptrFunction)(f,c) ; //dereference pointer to // call function
Can be replaced with
return ptrFunction(f,c) ; //dereference pointer to // call function
(*function)(f,c) or (&function)(f,c) instead of function(f,c)Address of a Function is the same as the function
Along the same lines, this access to the address of a function
ptrFunction = &function;
is the same as
ptrFunction = function;
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 &
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
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:
int a;
|-VarDecl 0x1a479d0 <function_pointer.c:1:1, col:5> col:5 used a 'int'
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
a=a+1;
}
int main(){
`-FunctionDecl 0x1a47c90 <line:7:1, line:20:1> line:7:5 main 'int ()'
`-CompoundStmt 0x1a6a850 <col:11, line:20:1>
void (* ptrFunction)();
|-DeclStmt 0x1a47e50 <line:8:3, col:25>
| `-VarDecl 0x1a47de8 <col:3, col:24> col:11 used ptrFunction 'void (*)()'
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
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 ()'
(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 ()'
(*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 ()'
(&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 ()'
(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 (*)()'
(*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 (*)()'
(&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.
(**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 ()'
(&*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 ()'
(*&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 ()'
}
Lets create a function to find a value in an array satisfying some condition (test)
First, lets define some test functions
_Bool IsOdd(int x)( return x%2); _Bool IsNegative(int x)( return x<0); _Bool IsPrime(int x)( r = 0;);
Protoype (declaration):
int FindIndexOfFirstAcceptable(const int array[], int length, _Bool (* SomeTestFunction)(int));
or, since prototypes don't require argument identifier
int FindIndexOfFirstAcceptable(const int *, int, _Bool (*)(int));
last argument is
Function Definition:
int FindIndexOfFirstAcceptable(const int array[], int length, _Bool (* SomeTestFunction)(int,int)) { //definition int i,savedIndex; assert(SomeTestFunction!= NULL); //check for null pointer and //exiting is better than seg fault //NULL defined in <stddef.h> as (void*)0 savedIndex = -1; i==0; while (i<length && savedIndex==-1){ if ( (*SomeTestFunction)(array[i]) ){ savedIndex = i; } i++; } return savedIndex; }
The function call
int a[] = {1,2,3}; i = FindIndexOfFirstAcceptable(a,3,IsOdd);
qsort is part of the c standard library, meaning that it should be available on most platforms
#include <stdlib.h>void qsort(void *base, size_t nmemb, size_t size, int(*compare)(const void *, const void *));
base is the pointer to the start of the array to be sortednmemb is the number of elements in the arraysize is the sizeof each element in the arraycompare is a pointer to a function that you provide to compare the array elements which must
Example
#include <stdio.h> #include <stdlib.h> #include <string.h> //returns negative if b%8 > a%8, // positive if a%8 > b %8, else 0 int Mod8Compare (const void * ptrA, const void * ptrB){ const int *ptrX = (const int * ) ptrA; const int *ptrY = (const int * ) ptrB; return ((*ptrX)%8 - (*ptrY) % 8); } void main(){ int a[]={5,6,7,8,9,10}; int length=6; int i; qsort( (void *)a , (size_t)length , sizeof(int) , Mod8Compare ); printf("%d",a[0]); for (i = 1; i<6; ++i){ printf(",%d",a[i]); } }
• first generic pointer to a const • second generic pointer to a const • cast to pointer to a specific type • cast to pointer to a specific type • example comparison function • call to qsort • pointer to first element with explicit casting to void pointer • number of elements in the array • size of each element in bytes • custom comparison function • print elements of array, now sorted by custom comparison
Call
> ./a.out
Output
8,9,10,5,6,7
_Bool f=2; sets f to 1)You may include <stdbool.h> to get the following:
#define bool _Bool
#define true 1
#define false 0
#include <complex.h> provides
#define complex _Complex
#define imaginary _Imaginary
_Complex_I is (const float _Complex) iImaginary_I (const float _Imaginary) iI is _Imaginary_I or if that is not available then it is Complex_Igcc complex_code.c -lm
#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 our last c keyword
⚠ C++ does not have standard support for restrict, but typically a compiler-specific variant is available https://gcc.gnu.org/onlinedocs/gcc/Restricted-Pointers.html , https://en.wikipedia.org/wiki/Restrict
The restrict keyword is intended only to qualify pointers -- specifically relationships between/among pointers
The compiler will assume that two restricted pointers do not point to the same memory address.
The compiler's assumption allows it more aggressively optimize code by not having to account for pointers changing each other's pointee values.
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):
Distinct(&d,&d); would result inrestrict.c:32:23: warning: passing argument 1 to ‘restrict’-qualified parameter aliases with argument 2 [-Wrestrict]
32 | printf("%d\n",Distinct(&d,&d));
| ^~ ~~
const int PI=1; int TAU=PI; //no problem making a copy of a const variable TAU=2*TAU; //finally tau = 2*pi
int a[]={0,1,2,3}; int * const ptrFirst = &a[0]; int * ptrSecond = ptrFirst; // no problem making a copy of a const variable ptrSecond = ptrFirst+1; // finally second points to second value
int func(int scratch){ scratch+=1; return(scratch); }
result=func(i); involves
scratchi int scratch=i;scratchint 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
(32 from C89, bold: 5 new to C99)
https://en.cppreference.com/w/c/language/operator_precedence
[ ] is first is precedence, but when looking at declarations, starting from the right,
[ ] from the right to the left of the thing it is modifying in order to interpret, and| code | parse | interpretation |
|---|---|---|
int a[3]; |
int [3] a; | a is a three element array of integers |
int (*ptrRow)[3]; |
int [3] * ptrRow; (*ptrRow)[3] is an int |
ptrRow is a pointer to 3-element array of integers |
int *ptrRows[3]; |
int * [3] ptrRows;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 [3] [2] m; | m is an 2-element array of 3-element array of integers |
int (*ptrMatrix)[2][3]; |
int [3] [2] * ptrMatrix last int: (*ptrMatrix)[1][2] |
ptrMatrix is a pointer to 2x3 matrix |
int (*ptrRows[2])[3]; |
int [3] * [2] ptrRows; last int: (*ptrRows[1])[2] |
2-element array of pointers to 3-element-arrays of integers |
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:
Length 4:
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:
Three rows:
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:
Three layers:
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
const for sizes in the example was not sufficient, had to use #define to avoid inference of variable-length arraysResult 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)
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
Here a multi-dimensional array will be allocated explicitly on the heap
1-D array:
int *row_=malloc(sizeof( int[ROW_LEN] ));
or
int *row_=malloc(sizeof(int)*ROW_LEN);
or
int *row_=malloc(sizeof(*row_)*ROW_LEN);
2-D array:
int (*arr__)[ROW_LEN]=malloc(sizeof( int[NUM_ROWS][ROW_LEN] ));
or
int (*arr__)[ROW_LEN]=malloc(sizeof(int)*ROW_LEN*NUM_ROWS);
or
int (*arr__)[ROW_LEN]=malloc(sizeof(*arr__)* NUM_ROWS);
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 ) ); }
Allocation of 4 sub-arrays on the heap
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)
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 =
Example:
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
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);
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);
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]);
Before 20,14 After 14,14
Before 20,14
After 14,14
With a pointer array
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
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
The same cannot be said if one attempts to allocate an contiguous array of FAM structs,
int main(){ struct flexArray struct;
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
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
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$
valgrind can be invoked with file-descriptor tracking and help discover files left open
note that files stdin,stdout,stderr (3 files) may always be reported at open at exit
==2709907== FILE DESCRIPTORS: 3 open (3 std) at exit.
file_left_open.c:
#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
> gcc -Wall file_left_open.c
--track-fds=yestext.txt
# as first character and should be printed> valgrind --track-fds=yes ./a.out file.txt
==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 ./a.outfull-check hint on line 19:
==1305028== Memcheck, a memory error detector ==1305028== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al. ==1305028== Using Valgrind-3.17.0 and LibVEX; rerun with -h for copyright info ==1305028== Command: ./a.out ==1305028== Before 20,14 After 14,14 ==1305028== ==1305028== HEAP SUMMARY: ==1305028== in use at exit: 24 bytes in 1 blocks ==1305028== total heap usage: 2 allocs, 1 frees, 1,048 bytes allocated ==1305028== ==1305028== LEAK SUMMARY: ==1305028== definitely lost: 24 bytes in 1 blocks ==1305028== indirectly lost: 0 bytes in 0 blocks ==1305028== possibly lost: 0 bytes in 0 blocks ==1305028== still reachable: 0 bytes in 0 blocks ==1305028== suppressed: 0 bytes in 0 blocks ==1305028== Rerun with --leak-check=full to see details of leaked memory ==1305028== ==1305028== For lists of detected and suppressed errors, rerun with: -s ==1305028== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
--tool=memcheck assumed by default--vgdb=<no|yes|full> [default: yes]
valgrind --leak-check=full --track-fds=yes a.out
valgrind --leak-check=full --track-fds=yes a.out text.txt
--log-fd=1, (default is STDERR stream 2), so that it may be captured by other tools or easily dumped to a file:valgrind --tool=memcheck --leak-check=full --track-fds=yes --log-fd=1 a.out > valgrindreport.txt
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
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
Result
extra.c: In function ‘int main()’: extra.c:6:11: warning: comparison with string literal results in unspecified behavior [-Waddress] 6 | switch(s=="hi") { | ~^~~~~~ extra.c:8:11: warning: this statement may fall through [-Wimplicit-fallthrough=] 8 | printf("greetings"); | ~~~~~~^~~~~~~~~~~~~ extra.c:9:3: note: here 9 | case 0: | ^~~~
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.