@@ -8,7 +8,7 @@ We will focus on common errors, caveats and important concepts that might ease y
C only guarantees minimum and relative size of "int", "short" etc... The range that each type can represent depends on the implementation.
The integer data types range in size from at least 8 bits to at least 32 bits. The C99 standard extends this range to include integer sizes of at least 64 bits.
The integer data types range in size from at least 8 bits to at least 32 bits. The C99 standard extends this range to include integer sizes of at least 64 bits.
The types are ordered by the width, guaranteeing that _wider_ types are at least as large as _narrower_ types. E.g. `long long int` can represents all values that a `long int` can represent.
...
...
@@ -87,7 +87,7 @@ int foo(int* p)
else
return0;
}
intbar()
{
int*p=NULL;
...
...
@@ -158,7 +158,7 @@ int main() {
returnEXIT_FAILURE;
}
free(buf);
strcpy(buf,ftr);// UB
strcpy(buf,ftr);// UB
printf("buf = %s\n",buf);
returnEXIT_SUCCESS;
...
...
@@ -241,7 +241,7 @@ void foo(void *v) {
### Void Pointer
We should also know that void pointers can be **casted** into any type of pointer.
We should also know that void pointers can be **casted** into any type of pointer.
In C, library functions like `malloc`, `calloc` etc. return void pointers, which we can then cast in to any other type of pointers as we need:
...
...
@@ -296,8 +296,8 @@ Arrays in C are very primitive:
- An Array in C does not have the information to its own length, not like `arr.length` in other languages
- Array's bounds are not checked at all
- So we can easily access off the end of an array
- We muss pass the array and its size together to any function that is going to manipulate it
- So we can easily access off the end of an array
- We muss pass the array and its size together to any function that is going to manipulate it
## Strings
...
...
@@ -357,31 +357,30 @@ Objects declared in file scope have `static` storage duration. The lifetime of t
#### Allocated
`Allocated` storage is allocated and deallocated through library functions on requests, using dynamic memory allocation functions.
`Allocated` storage is allocated and deallocated through library functions on requests, using dynamic memory allocation functions.
printf("address of int in allocated memory = %p\n",(void*)ptr_1);
free(ptr_1);/* stop allocated storage duration */
}
}
```
### Dynamic Memory Allocation
...
...
@@ -557,3 +556,82 @@ Using the header guard prevents the function definition of `func` being included
A common practice when picking the identifier to use as a header file guard is to use the salient parts of the file path, filename, and extension, separated by an underscore and written in all capital letters. E.g. `FOO_BAR_BAZ_H` for a file located in `foo/bar/baz.h`.
There are other ways of using the preprocessor directives and macros, this [article](https://en.wikibooks.org/wiki/C_Programming/Preprocessor_directives_and_macros) and [GCC documentation](https://gcc.gnu.org/onlinedocs/cpp/) provide extensive information about them.
## C Program Structure
We've talked about storage duration above. Storage duration and linkage are closely related. In C, you can use the **storage-class specifiers** to specify the storage duration and linkage of an object or a function, they are:
-`auto`: automatic duration and no linkage
-`register`: automatic duration and no linkage; address of this variable cannot be taken (we won't cover this, it's quite rare )
-`static`: static duration and internal linkage
-`extern`: static duration and external linkage
### Linkage
Linkage refers to the ability of an **identifier (variable or function)** to be referred to in other scopes.
C provides three kinds of linkage:
-`none`: The identifier can be referred to only from the scope it is in.
-`external`: The identifier can be referred to from everywhere in the program. (E.g. from other source file).
-`internal`: The identifier can only be referred to within the translation unit that contains the declaration.
There are some implicit rules if no storage-class specifier is provided, the defaults are:
-`extern` for all functions
-`extern` for all objects at file scope
-`auto` for objects at block scope
Let's look at some examples.
```c
// flib.h
#ifndef FLIB_H
#define FLIB_H
voidf(void);// function declaration with external linkage
externintstate;// variable declaration with external linkage
staticconstintsize=5;// definition of a read-only variable with internal linkage
enum{MAX=10};// constant definition
#endif // FLIB_H
```
```c
// flib.c
#include"flib.h"
staticvoidlocal_f(ints){}// definition with internal linkage (only used in this file)
staticintlocal_state;// definition with internal linkage (only used in this file)
intstate;// definition with external linkage (used by main.c)
voidf(void){local_f(state);}// definition with external linkage (used by main.c)
```
```c
// main.c
#include"flib.h"
intmain(void)
{
intx[MAX]={size};// uses the constant and the read-only variable
state=7;// modifies state in flib.c
f();// calls f() in flib.c
}
```
#### Special Use of `static`
Declaring a variable at block scope as static creates an identifier with no linkage, but it does give the variable static storage duration:
```c
#include<stdio.h>
voidfoo(){
staticintcount=0;// count has no linkage but has static storage duration
printf("Function has been called %d times\n",++count);