Advanced_C
-
Upload
ramachandram-jangili -
Category
Documents
-
view
216 -
download
0
Transcript of Advanced_C
-
8/7/2019 Advanced_C
1/117
ADVANCED C
-Sushma
-Ukil
-
8/7/2019 Advanced_C
2/117
Memory Management A process is an "invocation" or "activation" of a program. A program is a
list of instructions for execution by a computer. To run the program it needs to be copied (or loaded) into the main
computer memory and the central processing unit is told to start reading
(and obeying) instructions taken from that area of memory. The activity of
executing the program's instructions is called running the process.
A portion of memory needs to be allocated to a process. The actual
allocation of such an area of memory is a task for the loader.
The memory area allocated to a program will usually be split into several
sub-areas for particular.
-
8/7/2019 Advanced_C
3/117
Memory management
The code segment
This is known as the text area in Unix parlance and simply contains the
executable code of the program(both the program binary and any shared
library it loads).
If there are several processes running the same program there will still
only be one code area as it is identical for all processes.
The current state of a process is determined by its various data areas.
All memory pages of code segment are marked as read-only and are
shared with any other process using the same program file and/or shared
library files.
The kernel process exchange (or switch) mechanism will activate a
particular process by indicating to the hardware where the next
instruction is to be read from what mapping is to be used to access data
-
8/7/2019 Advanced_C
4/117
Memory Management
The data segment
This holds the data being processed by the program, it's size is initiallydetermined by the loader from information in the binary file whichspecifies the amount of initialized data to be copied form the binary fileand the amount of un initialized space to be allocated to the process.
On some systems the space may be initialized to zero but this is notuniversal and such areas may contain "droppings" left over from previousprocesses, a possible security problem. On older Unix systems the uninitialized space is known as bss from a PDP/11 assembler mnemonic.
The Unix size command will give the memory requirement informationfor a binary file.
This segment can grow as the process runs and needs more virtualmemory.
bash$ size testhand2 92763 + 7564 + 2320 = 102647
The above example shows that loading the binary file testhand2 willrequire 102647 bytes of memory with 92763 bytes for code, 7564 bytes
for initialized data and 2320 bytes for non-initialized data.
-
8/7/2019 Advanced_C
5/117
Memory Management
The stack segment
Whenever a function is called a stack frame is created tostore data related to the current invocation of the functionsuch as the return address, saved state, local copies of
parameters and, of course, local data. The stack is normally built within the data area of the
process and the memory allocated to the stack can beexpanded as needed.
The stack and the arena need to be kept separate from the
initialized and un initialized data areas, this is normallyachieved by using widely separated parts of a large virtualaddress space.
-
8/7/2019 Advanced_C
6/117
Memory Management
Heap memory
The heap is a portion of memory allocated dynamically (as needed, atruntime) for the use of the process.
Whenever the malloc or calloc functions are used in C for instance,they reserve space in heap memory.
Stack
The stackportion of memory, on the other hand, is used by the processto aid the invocation of functions.
Every time a function is called, the process reserves a portion of stackmemory to store the values of parameters passed to the functions aswell as for results returned by the functions and the local variablesused within the functions.
The stack is also where space for all declared data types and structures
is reserved at compile time.
-
8/7/2019 Advanced_C
7/117
Memory ManagementThe Stack And Local Variables
The stack is used by the process to store the chain of functions which arecurrently in the middle of execution.
Each function call causes the process to add an execution frame to thetop of the stack. This execution frame would contain the contents of CPUregisters as they were before the function call was made, the returnaddress (i.e. where in the code this function was invoked, so we can
return there when the function returns), the parameters passed to thefunction and all local variables of the function.
Thus, when we have function 'main' calling function 'a' which callsfunction 'b', we will have 3 frames on the stack.
The 'allocation' of a stack frame does not require performing realmemory allocation - as the space for the stack was reserved during
process startup. Instead, this is done by merely marking a part of thestack as containing the frame, and copying data to that frame.
Note that local variables are "allocated" by simply advancing the stackpointer beyond their location on the stack, so allocating local variablestakes a fixed amount of time, no matter their size.
When a function returns - its stack frame is freed by simply modifyingthe stack pointer to point below its frame.
-
8/7/2019 Advanced_C
8/117
Memory ManagementThe Stack And Local Variables(cont..)
The local variables are not initialized - they just contain the values thatwere accidentally placed in the memory locations these variablesoccupy.
Consider a situation in which a function was called, its variables usedand given some values.
Later the function returned, and its frame was released.
Then, another function was called. The stack frame of this new function is located in the same place in
memory as the frame of the former function, so the new function'slocal variables will get the values that were left there by the localvariables of the former function.
This can explain why the values of un initialized local variables areoften neither 0, nor look like total garbage.
Since all local variables are stored in consecutive memory on the stack,if we take a pointer to such a variable, and later on write right above orright below this pointer, we'll be modifying the contents of anotherlocal variable, or even that of the function's return address.
-
8/7/2019 Advanced_C
9/117
Memory ManagementThe Stack And Local Variables(cont..)
For example, consider the following code:int foo()
{
int numbers[2];
int j; j = 2;
printf("j - %d\n", j);numbers[2] = 3;
printf("j - %d\n", j);
}
During execution of this function, we first assign 2 to 'j', and thus the
first print command will show "j - 2". Then, we try to assign a value to'numbers[2]'. However, the 'numbers' array only has 2 cells - 0 and 1.Writing into subscript '2' of this array will cause us to write just beyondthe array (which is fully located on the stack).
The variable 'j' just happens to be stored in that location in memory, andthus, the value '3' will be actually assigned to 'j'.
Our second print command will thus show "j - 3".
-
8/7/2019 Advanced_C
10/117
Memory Management
The Stack And Local Variables(cont..)
Note that this assumes that the variables are stored in memory in the
same order as they were declared inside the function's code. With some
compilers, this might not be the case, and the out-of-range assignment
might overwrite a different variable, or a part of the stack that does not
hold variables, leading to other unexpected results.
Note: local variables (as well as function parameters) might be stored
in registers, rather than on the stack. This could be either because we
used the 'register' keyword when declaring these variables, or because
the compiler's optimization chose to place a variable in a register. Of
course, such variables cannot be over-written by stack overflows.
-
8/7/2019 Advanced_C
11/117
Memory Management
Dynamic Memory Allocation
Memory can be allocated dynamically by the calls
malloc()
calloc()
realloc()
The prototype for malloc is:
void *malloc(size_t size);
malloc takes in a size_t and returns a void pointer.
Why does it return a void pointer? Because it doesn't matter to mallocto what type this memory will be used for.
Let's see an example of how malloc is used:
int *ip;ip = malloc(5 * sizeof(int)); /* .. OR .. */
ip = malloc(5 * sizeof(ip));
Pretty simplistic. sizeof(int) returns the sizeof an integer on themachine, multiply by 5 and malloc that many bytes.
The second malloc works because it sends what ip is pointing to, whichis an int.
-
8/7/2019 Advanced_C
12/117
Memory Management
Dynamic Memory Allocation(cont..) Wait... we're forgetting something. AH! We didn't check
for return values.
Here's some modified code:#define INITIAL_ARRAY_SIZE 5
/* ... code ... */
int *ip;
if ((ip = malloc(INITIAL_ARRAY_SIZE * sizeof(int))) == NULL)
{
(void)fprintf(stderr, "ERROR: Malloc failed");(void)exit(EXIT_FAILURE); /* or return EXIT_FAILURE; */
}
Now our program properly prints an error message andexits gracefully if malloc fails.
-
8/7/2019 Advanced_C
13/117
Memory Management
Dynamic Memory Allocation(cont..) calloc works like malloc, but initializes the memory to zeroif possible.
The prototype is:
void *calloc(size_t nmemb, size_t size);
bzero fills the first n bytes of the pointer to zero.
Prototype:
void bzero(void *s, size_t n);
If you need to set the value to some other value (or just as ageneral alternative to bzero), you can use memset:
void *memset(void *s, int c, size_t n);
where you can specify c as the value to fill for n bytes ofpointer s.
-
8/7/2019 Advanced_C
14/117
Memory Management
Dynamic Memory Allocation(cont..)
What if we run out of allocated memory during the run-time of our
program and need to give our collection of items more memory?
Use realloc, it's prototype:
void *realloc(void *ptr, size_t size);
realloc takes in the pointer to the original area of memory to enlargeand how much the total size should be.
So let's give it a try:
ip = realloc(ip, sizeof(ip) + sizeof(int)*5);
Ah... Now we have some more space, by simply giving it the sizeof the
complete array and then adding 5 spaces for ints. "I'm a genius!" you say... and then your program segfaults, core dumps
and you get laughed at.
STOP! This is NOT how you use realloc.
Again. The above example is wrong. Why?
-
8/7/2019 Advanced_C
15/117
Memory Management
Dynamic Memory Allocation(cont..) First, sizeof(ip) does not give the size of the allocated space originally
allocated by malloc (or a previous realloc).
Using sizeof() on a pointer only returns the sizeof the pointer, which isprobably not what you intended.
Also, what happens if the realloc on ip fails? ip gets set to NULL, and
the previously allocated memory to ip now has no pointer to it. Now we have allocated memory just floating in the heap without a
pointer. This is called a memory leak. This can happen from sloppyrealloc's and not using free on malloc'd space. So what is the correctway? Take this code for example:
int *tmp;
if ((tmp = realloc(ip, sizeof(int) * (INITIAL_ARRAY_SIZE + 5))) ==NULL)
{
/* Possible free on ip? Depends on what you want */
fprintf(stderr, "ERROR: realloc failed");
}ip = tmp;
-
8/7/2019 Advanced_C
16/117
Memory ManagementDynamic Memory Allocation(cont..)
Now we are creating a temporary pointer to try a realloc. If it fails, then
it isn't a big problem as we keep our ip pointer on the original memory
space.
Also, note that we specified the real size of our original array and now
are adding 5 more ints (so 4bytes*(5+5) = 40bytes, on a typical 32-bit
machine). Now that we can malloc, calloc, and realloc we need to be able to free
the memory space if we have no use for it anymore. Like we mentioned
above, any memory space that loses its pointer or isn't free'd is a
memory leak.
So what's the prototype for free? Here it is:void free(void *ptr);
free simply takes in a pointer to free. Not challenging at all. Note that
free can take in NULL, as specified by ANSI.
-
8/7/2019 Advanced_C
17/117
Memory Management
Assigning memory to segments
Compiler and assembler generate an object file (containing code and
data segments) from each source file
Linker combines all the object files for a program into a singleexecutable object file, which is complete and self-sufficient
Loader (part of OS) loads an executable object file into memory at
location(s) determined by the operating system
Program (as it runs) uses new and malloc to dynamically allocate
memory, gets space on stack during function calls
-
8/7/2019 Advanced_C
18/117
Memory Management
LinkingFunctions of a linker:
Combine all files and libraries of a program
Regroup all the segments from each file together (one big data segment,etc.)
Adjust addresses to match regrouping Result is an executable program
Contents of object files:
File header size and starting address(in memory) of each segment
Segments for code and initialized data Symbol table (symbols, addresses)
External symbols (symbols, location)
Relocation information (symbols, location)
Debugging information
For UNIX details, type man a.out
-
8/7/2019 Advanced_C
19/117
Memory Management
Why is Linking Difficult? When assembler assembles a file, it may find external references
symbols it doesnt know about (e.g., printf, scanf)
Compiler just puts in an address of 0 when producing the object code
Compiler records external symbols and their location (in object file) in
a crossreference list, and stores that list in the object file Linker must resolve those external references as it links the files
together
Compiler doesnt know where program will go in memory (if
multiprogramming, always 0 for uniprogramming)
Compiler just assumes program starts at 0 Compiler records relocation information (location of addresses to be
adjusted later), and stores it in the object file
-
8/7/2019 Advanced_C
20/117
Memory Management
Loading The loader loads the completed program into memory where it can be
executed
Loads code and initialized data segments into memory at specifiedlocation
Leaves space for uninitialized data (bss) Returns value of start address to operating system
Alternatives in loading
Absolute loader loads executable file at fixed location
Relocatable loader loads the program at an arbitrary memorylocation specified by OS (needed for multiprogramming, not foruniprogramming)
Assembler and linker assume program will start at location 0
When program is loaded, loader modifies all addresses by adding thereal start location to those addresses
-
8/7/2019 Advanced_C
21/117
Memory Management
Running the Program Static Memory Allocation
Compiling, linking, and loading is sufficient for static memory
Code, constants, static variables
In other cases, static allocation is not sufficient:
Need dynamic storage programmer may not know how much memorywill be needed when program runs
Use malloc or new to get whats necessary when its necessary
For complex data structures (e.g., trees),
allocate space for nodes on demand
OS doesnt know in advance which procedures will be called (would be
wasteful to allocate space for every variable in every procedure in advance)
OS must be able to handle recursive procedures
-
8/7/2019 Advanced_C
22/117
Memory management
Running the Program Dynamic Memory Allocation
Dynamic memory requires two fundamental operations: Allocate dynamic storage
Free memory when its no longer needed
Methods vary for stack and heap
Two basic methods of allocation: Stack (hierarchical) Good when allocation and freeing are somewhat predictable
Typically used:
to pass parameters to procedures
for allocating space for local variables inside a procedure
for tree traversal, expression evaluation,parsing, etc. Use stack operations: push and pop
Keeps all free space together in a structured organization
Simple and efficient, but restricted
-
8/7/2019 Advanced_C
23/117
Memory Management
Running the Program Dynamic Memory Allocation
(cont.) Two basic methods of allocation:
Heap
Used when allocation and freeing are not predictable
Typically used:
for arbitrary list structures, complex data organizations, etc.
Use new ormalloc to allocate space, use deleteorfreeto release space
System memory consists of allocated areas and free areas (holes)
Problem: eventually end up with many small holes, each too small to be useful
This is called fragmentation, and it leads to wasted memory
Fragmentation wasnt a problem with stack allocation, since we always add/delete
from top of stack Solution goal: reuse the space in the holes in such a way as to keep the number of
holes small, and their size large
Compared to stack: more general, less efficient, more difficult to implement
-
8/7/2019 Advanced_C
24/117
Strings Stings in C are stored as null character, '\0', terminated character arrays.
This means that the length of a string is the number of characters itcontains plus one to store the null character.
The subscripts used for the array start with zero (0).
Common string operations include finding lengths, copying, searching,replacing and counting the occurrences of specific characters and words.
#include needs to be included when string functions are used
The following line declares a char array called str.
char str[15];
C provides fifteen consecutive bytes of memory.
Only the first fourteen bytes are usable for character storage, because onemust be used for the string-terminating null.
The following is a representation of what would be in RAM, if the string"Hello, world!" is stored in this array.
Characters: H e l l o , w o r l d !
Hex values: 48 65 6C 6C 6F 2C 20 77 6F 71 6C 64 21 00
Subscripts: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
-
8/7/2019 Advanced_C
25/117
StringsCommon String Functions
strcpystrcpy copies a string, including the null character terminator from thesource string to the destination. This function returns a pointer to thedestination string, or a NULL pointer on error. Its prototype is:char *strcpy(char *dst, const char *src);
strncpy
strncpy is similar to strcpy, but it allows the number of characters tobe copied to be specified. If the source is shorter than the destination,than the destination is padded with null characters up to the lengthspecified. This function returns a pointer to the destination string, or aNULL pointer on error. Its prototype is:char *strncpy(char *dst, const char *src, size_t len);
strcatThis function appends a source string to the end of a destinationstring. This function returns a pointer to the destination string, or aNULL pointer on error. Its prototype is:char *strcat(char *dst, const char *src);
-
8/7/2019 Advanced_C
26/117
Strings
Common String Functions(Cont..)strncatThis function appends at most N characters from the source string to theend of the destination string. This function returns a pointer to thedestination string, or a NULL pointer on error. Its prototype is:char *strncat(char *dst, const char *src, size_t N);
strcmpThis function compares two strings. If the first string is greater than thesecond, it returns a number greater than zero. If the second string isgreater, it returns a number less than zero. If the strings are equal, itreturns 0. Its prototype is:int strcmp(const char *first, const char *second);
strncmpThis function compares the first N characters of each string. If the firststring is greater than the second, it returns a number greater than zero. Ifthe second string is greater, it returns a number less than zero. If thestrings are equal, it returns 0. Its prototype is:int strncmp(const char *first, const char *second, size_t N);
-
8/7/2019 Advanced_C
27/117
StringsCommon String Functions(Cont..)
strlenThis function returns the length of a string, not counting the nullcharacter at the end. That is, it returns the character count of the string,without the terminator. Its prototype is:size_t strlen(const char *str);
strchrThis function finds the first occurrence of character C in string STR,returning a pointer to the located character. It returns NULL pointerwhen there is no match
char * strchr (char *STR, char C)
strstr
This function is like `strchr' but searches for string SEARCH in stringSTR, returning a pointer into the STR that is the first character of thematch found in STR. It returns NULL pointer if no match was found.If SEARCH is an empty string, it returns STR.
char * strstr (const char *STR, const char *SEARCH)
-
8/7/2019 Advanced_C
28/117
Bitwise Operators Bitwise operators apply the same operation to matching bits in value on
each side of operator (the one's complement is unary and works only onthe value to it's right)
Result for each bit position determined only by the bit(s) in that
position
Results for each operator summarized in the following table
a
b
and a & b
or a | b
e clusive or a b
one's complement ~a
-
8/7/2019 Advanced_C
29/117
Bitwise Operators
Shift Operators
Operators >> and
-
8/7/2019 Advanced_C
30/117
Bitwise OperatorsShift operators(contd..) Can use
-
8/7/2019 Advanced_C
31/117
Bitwise Operators
Shift operators(contd..)
Can use >> to divide unsigned integral value by a power of two.
1 bit divides by 2, 2 bits divides by 4, 3 bits divides by 8, n bits divides
by 2n
On some implementations, shifting may be much faster than dividing
(but division makes for much clearer code)
Shifting signed values may fail because for negative values the result
never gets past -1:
-5 >> 3is -1 and not 0 like -5/8
Using right shift to divide:
unsigned long x = 75, y;
...
x >>= 2; /* x = 75 / 4 = 18 */
y = x >> 1; /* y = ( x / 2 = 9 ) x is not changed */
-
8/7/2019 Advanced_C
32/117
Structures
Structure in C is a collection of items of different types. You can think
of a structure as a "record" is in Pascal or a class in Java withoutmethods.
So how is a structure declared and initialized? Let's look at anexample:
struct student
{char *first;
char *last;
char SSN[9];
float gpa;
char **classes;};
struct student student_a, student_b;
Structures :: DeclarationandSyntax
-
8/7/2019 Advanced_C
33/117
Structures
Another way to declare the same thing is:
struct
{
char *first;
char *last;
char SSN[10];float gpa;
char **classes;
} student_a, student_b;
As you can see, the tag immediately after struct is optional. But in the
second case, if you wanted to declare another struct later, you couldn't.
-
8/7/2019 Advanced_C
34/117
Structures The "better" method of initializing structs is:
struct student_t
{
char *first;
char *last;
char SSN[10];
float gpa;
char **classes;
} student, *pstudent;
Now we have created a student_t student and astudent_t pointer. The pointer allows us greaterflexibility (e.g. Create lists of students).
You could initialize a struct just like an arrayinitialization. But be careful, you can't initialize thisstruct at declaration time because of the pointers.
To access fields inside struct C has a special operator forthis called "member of" operator denoted by . (period).For example, to assign the SSN ofstudent_a:
strcpy(student_a.SSN, "111223333\0");
-
8/7/2019 Advanced_C
35/117
Structures
Nested structures
Structures can contain other structures as members; in other words,
structures can nest. Consider the following two structure types:
struct first_structure_type
{
int integer_member;float float_member;
};
struct second_structure_type
{
double double_member;struct first_structure_type struct_member;
};
The first structure type is incorporated as a member of the second
structure type.
-
8/7/2019 Advanced_C
36/117
Structures
Nested structures(Contd..)
You can initialize a variable of the second type as follows:
struct second_structure_type demo;
demo.double_member = 12345.6789;
demo.struct_member.integer_member = 5;
demo.struct_member.float_member = 1023.17;
The member operator . is used to access members of structures that are
themselves members of a larger structure.
No parentheses are needed to force a special order of evaluation
A member operator expression is simply evaluated from left to right.
In principle, structures can be nested indefinitely.
-
8/7/2019 Advanced_C
37/117
Typedef There is an easier way to define structs or you could "alias" types you
create.
For example:
typedef struct
{
char *first;
char *last;char SSN[9];
float gpa;
char **classes;
} student; student student_a;
Now we get rid of those silly struct tags. You can use typedef for non-structs: typedef long int *pint32; pint32 x, y, z; x, y and z are all
pointers to long ints. typedef is your friend. Use it.
-
8/7/2019 Advanced_C
38/117
Union Unions are declared in the same fashion as structs, but have a
fundamental difference. Only one item within the union can be used at
any time, because the memory allocated for each item inside the unionis in a shared memory location. Why you ask? An example first:
struct conditions
{
float temp;
union feels_like{
float wind_chill;
float heat_index;
}
} today; As you know, wind_chill is only calculated when it is "cold" and
heat_index when it is "hot". There is no need for both. So when youspecify the temp in today, feels_like only has one value, either a floatfor wind_chill or a float for heat_index.
Types inside of unions are unrestricted, you can even use structs
within unions.
-
8/7/2019 Advanced_C
39/117
Enum What if you wanted a series of constants without creating a new type?
Enter enumerated types. Say you wanted an "array" of months in ayear:
enum e_months {JAN=1, FEB, MAR, APR, MAY, JUN,JUL, AUG, SEP, OCT, NOV, DEC};
typedef enum e_months month;
month currentmonth;currentmonth = JUN; /* same as currentmonth = 6; */
printf("%d\n", currentmonth);
We are enumerating the months in a year into a type called month.You aren't creating a type, because enumerated types are simplyintegers. Thus the printf statement uses %d, not %s.
If you notice the first month, JAN=1 tells C to make the enumerationstart at 1 instead of 0.
Note: This would be almost the same as using:
#define JAN 1
#define FEB 2
#define MAR 3 /* ... etc ... */
-
8/7/2019 Advanced_C
40/117
Static Variables A static variable is local to particular function. However, it is only
initialised once (on the first call to function).
Also the value of the variable on leaving the function remains intact.On the next call to the function the the static variable has the samevalue as on leaving.
To define a static variable simply prefix the variable declaration withthe static keyword. For example:
void stat(); /* prototype fn */ main() { int i; for (i=0;i
-
8/7/2019 Advanced_C
41/117
Bitfields BitFields allow the packing of data in a structure. This is especially
useful when memory or data storage is at a premium. Typical examples:
Packing several objects into a machine word. e.g. 1 bit flags can be compacted -- Symbol tables in compilers.
Reading external file formats -- non-standard file formats could be readin. E.g. 9 bit integers.
C lets us do this in a structure definition by putting :bitlength after thevariable. i.e.
struct packed_struct
{
unsigned int f1:1;
unsigned int f2:1;
unsigned int f3:1;
unsigned int f4:1;
unsigned int type:4;
unsigned int funny_int:9;
} pack;
Here the packed_struct contains 6 members: Four 1 bit flags f1..f3, a 4 bit
type and a 9 bit funny_int.
-
8/7/2019 Advanced_C
42/117
Bitfields
C automatically packs the above bit fields as compactly as
possible, provided that the maximum length of the field isless than or equal to the integer word length of thecomputer. If this is not the case then some compilers mayallow memory overlap for the fields whilst other wouldstore the next field in the next word
NOTE:
Only n lower bits will be assigned to an n bit number. Sotype cannot take values larger than 15 (4 bits long).
Bit fields are always converted to integer type forcomputation.
You are allowed to mix ``normal'' types with bit fields.
The unsigned definition is important - ensures that no bitsare used as a flag.
Bi fi ld
-
8/7/2019 Advanced_C
43/117
BitfieldsBit Fields: Practical Example
Frequently device controllers (e.g. disk drives) and the operating systemneed to communicate at a low level. Device controllers contain several
registers which may bepacked together in one integer We could define this register easily with bit fields:
struct DISK_REGISTER
{
unsigned ready:1;
unsigned error_occured:1;
unsigned disk_spinning:1;
unsigned write_protect:1;
unsigned head_loaded:1;
unsigned error_code:8;
unsigned track:9; u
nsigned sector:5;unsigned command:5; };
To access values stored at a particular memory address,DISK_REGISTER_MEMORY we can assign a pointer of the abovestructure to access the memory via:
struct DISK_REGISTER *disk_reg = (struct DISK_REGISTER *)
DISK_REGISTER_MEMORY;
-
8/7/2019 Advanced_C
44/117
Bitfields The disk driver code to access this is now relatively straightforward:
/* Define sector and track to start read */disk_reg->sector = new_sector;
disk_reg->track = new_track;
disk_reg->command = READ; /* wait until operation done, ready will
be true */
while ( ! disk_reg->ready ) ; /* check for errors */if (disk_reg->error_occured)
{ /* interrogate disk_reg->error_code for error type */
switch (disk_reg->error_code) ......
}
A note of caution: Portability
Bit fields are a convenient way to express many difficult operations.
However, bit fields do suffer from a lack of portability between
platforms
integers may be signed or unsigned
-
8/7/2019 Advanced_C
45/117
Bitfields
Many compilers limit the maximum number of bits in the bit fieldto the size of an integer which may be either 16-bit or 32-bit
varieties.
Some bit field members are stored left to right others are stored
right to left in memory.
If bit fields too large, next bit field may be stored consecutively inmemory (overlapping the boundary between memory locations) or
in the next word of memory.
If portability of code is a premium you can use bit shifting and
masking to achieve the same results but not as easy to express or read.
For example:
unsigned int *disk_reg = (unsigned int *)
DISK_REGISTER_MEMORY; /* see if disk error occured */
disk_error_occured = (disk_reg 0x40000000) >> 31;
-
8/7/2019 Advanced_C
46/117
Typecasting
C is one of the few languages to allow coercion, that is forcing one
variable of one type to be another type. C allows this using the castoperator (). So:int integernumber;
float floatnumber=9.87;
integernumber=(int)floatnumber;
assigns 9 (the fractional part is thrown away) to integernumber.
And:
int integernumber=10;
float floatnumber;
floatnumber=(float)integernumber;
assigns 10.0 to floatnumber.
-
8/7/2019 Advanced_C
47/117
Typecasting
Coercion can be used with any of the simple data types including char,
so:int integernumber;
char letter='A';
integernumber=(int)letter;
assigns 65 (the ASCII code for `A') to integernumber.
Some typecasting is done automatically -- this is mainly with integercompatibility.
A good rule to follow is: If in doubt cast.
Another use is the make sure division behaves as requested:
If we have two integers internumber and anotherint and we want theanswer to be a float then :
e.g. floatnumber = (float) internumber / (float) anotherint;ensures floating point division.
P d M
-
8/7/2019 Advanced_C
48/117
Preprocessor and Macros Preprocessor includes, substitutes and selects text to form finished
source code prior to actual compile
#include used to copy in statements from other files, usually headerfiles with .h extension
#define used to define macros
simple substitution
function-like substitution with arguments
#undefused to remove previous definition Conditional commands used to selectively include or exclude
statements
#if, #elif, #else, #endif
#ifdefand #ifndeftest if a name is defined
defined() operator test if a name is defined
Predefined macros
__FILE__Source file name
__LINE__Current source line number
__DATE__Date compiled
__TIME__Time compiled
__TIMESTAMP__Compile date/time
-
8/7/2019 Advanced_C
49/117
Preprocessor and Macros
#include statement #include copies code from external files
Files copied generally have .h file extension and considered "header
file"
Usually included at beginning of source module
May be nested - included file may contain #include
Use of brackets or quotes determines where compiler searches for the
included file
#include "stuff2.h"
If file name is in quotes, looks first in the same location as the module
with the #include statement
#include
If file name is in brackets, searches according to an implementation-
defined rule.
-
8/7/2019 Advanced_C
50/117
Preprocessor and Macros
#define statement
We already saw how #define can be used to define a simplesubstitution text for a symbol.
Format
#define identifier replacment-text
#define PI 3.14159#define NUM_ROW (sizeof(ax)/sizeof(*ax))
#define LOG_FILE "hacmp.log"
Definition can also be function-like, with arguments
#define identifier(arg1,arg2) repl-text
When macro is invoked, argument tokens appearing in themacro replacement text are replaced with the text for thatargument in the macro invocation.
-
8/7/2019 Advanced_C
51/117
Preprocessor and Macros
#ifdirective
#ifis followed by a intger constant expression. If the expression is not zero, the statement(s) following the #ifare
compiled, otherwise they are ignored.
#ifstatements are bounded by a matching #endif, #else or#elif
Macros, if any, are expanded, and any undefined tokens are replacedwith 0 before the constant expression is evaluated
Relational operators and integer operators may be used
Expression examples
#if 1
#if 0
#if ABE == 3
#if ZOO < 12
#if ZIP == 'g'
#if (ABE + 2 - 3 * ZIP) > (ZIP - 2)
In most uses, expression is simple relational, often equality test
#if SPARKY == '7'
P d M
-
8/7/2019 Advanced_C
52/117
Preprocessor and Macros#else directive
#else marks the beginning of statement(s) to be compiled if the preceding#i
for #elif expression is zero (false) Statements following #else are bounded by matching #endif
Examples #if OS = 'A' system( "clear" ); #else system( "cls" ); #endif
#elifdirective#elifadds an else-if branch to a previous #if
A series of#elif's provides a case-select type of structure
Statement(s) following the #elifare compiled if the expression is notzero, ignored otherwise
Expression is evaluated just like for#if
Examples
#if TST == 1 z = fn1( y );
#elif TST == 2 z = fn2( y, x );
#elif TST == 3 z = fn3( y, z, w );
#endif ...
#if ZIP == 'g' rc = gzip( fn );
#elif ZIP == 'q' rc = qzip( fn );
#else rc = zip( fn );
#endif
-
8/7/2019 Advanced_C
53/117
Preprocessor and Macros Conditonal compilation
The following preprocessor directives are used for conditional
compilation. Conditional compilation allows statements to be included oromitted based on conditions at compile time. #if #else #elif #endif #ifdef#ifndef
In the following example, the printfstatements are compiled when thesymbol DEBUG is defined, but not compiled otherwise
/* remove to suppress debug printf's*/#define DEBUG
...
x = ....
#ifdef DEBUG
printf( "x=%d\n" );
#endif...
y = ....;
#ifdef DEBUG
printf( "y=%d\n" );
#endif ...
-
8/7/2019 Advanced_C
54/117
Pointers
Pointers provide an indirect method of accessing variables.
You may be wondering, what is the point of this (no pun intended)? Why don't I just make all variables without the use of pointers?
It's because sometimes you can't.
What if you needed an array of ints, but didn't know the size of thearray before hand?
What if you needed a string, but it grew dynamically as the programran?
They are all solved through the use of pointers. Pointers are alsoessential in creating larger custom data structures, such as linked lists.
A pointer when declared is just a reference. DECLARING APOINTER DOES NOT CREATE ANY SPACE FOR THE
POINTER TO POINT TO. We will tackle this dynamic memoryallocation issue later.
A pointer is a reference to an area of memory in the heap. The heap isa dynamically allocated area of memory when the program runs.
P i t
-
8/7/2019 Advanced_C
55/117
Pointers Pointers are declared by using the * infront of the variable identifier.
For example:
int *ip;float *fp = NULL;
char *a;
This declares a pointer, ip, to an integer.
Let's say we want ip to point to an integer.
The second line declares a pointer to a float, but initializes the pointer topoint to the NULL pointer. The NULL pointer points to a place inmemory that cannot be accessed. NULL is useful when checking forerror conditions and many functions return NULL if they fail.
Third line declares pointer to a char.
int x = 5;
int *ip;
ip = &x;
The operator is to specify the address-ofx. Thus, the pointer, ip ispointing to x by assigning the address of x.
The * dereferences the pointer to the value.
So, printf("%d %d\n", x, *ip); would print 5 5 to the screen.
P i t
-
8/7/2019 Advanced_C
56/117
Pointers
What is the output of this program?
main()
{int i = 54;
float a = 3.14;
char *ii, *aa;ii = & i;
aa = & a;
printf(\nAddress contained in ii = %u, ii);
printf(\nAddress contained in ii = %u, ii);
printf(\nValue at the address contained in ii = %d, *ii);printf(\nValue at the address contained in ii = %d, *aa);
}
Binary equivalent of 54
2008 2009
i
Binary equivalent of 3.14
a
7006 7007 7008 7009
-
8/7/2019 Advanced_C
57/117
Pointers
Output
ii and aa are declared as char pointers. Still the statements ii = i and aa= a work.
The addresses 2008 and 7006 get stored in ii and cc which are printed
through the first two printf()s.
However the program falters at the next two printf()s.
This is so since ii is a character pointer *ii gives value at address 2008and not the one present in 2008 and 2009.
Similarly *aa gives the value at 7006 and not the one contained in 7006,
7007, 7008 and 7009.
Moral is if you wish to access an integer value stored in a variable using
its address its necessary that the address be stored in an integer pointer.Likewise if you wish to access a float value stored in a variable using
its address its necessary to store the address in a float pointer.
P i t
-
8/7/2019 Advanced_C
58/117
Pointers
Functions Returning Pointers
The way functions return an int, a float, a double or any other data type,it can even return a pointer.
However, to make a function return a pointer it has to be explicitlymentioned in the calling function as well as in the function declaration.
Example:
main()
{
int *p;
int *fun();
p=fun();
printf(\n%u,p);
}
int *fun()
{
int i=20;
return ( i);
P i t
-
8/7/2019 Advanced_C
59/117
Pointers
Pointer Arithmetic
C is one of the few languages that allows pointer arithmetic.
In other words, you actually move the pointer reference by anarithmetic operation.
For example:
int x = 5,
*ip = x; ip++;
On a typical 32-bit machine,
*ip would be pointing to 5 after initialization.
But ip++; increments the pointer 32-bits or 4-bytes.
So whatever was in the next 4-bytes, *ip would be pointing at it. Pointer arithmetic is very useful when dealing with arrays, because
arrays and pointers share a special relationship in C.
P i t
-
8/7/2019 Advanced_C
60/117
Pointers
Pointer Arithmetic(Contd)
What is the output?
main()
{
float *fun(float *);
float p=23.5, *q;
q = p;
printf(\nq before call = %u,q);
q = fun( p);
printf(\nq after call = %u,q);
}
float *fun(float *r)
{
r = r+1;
return(r);
}
-
8/7/2019 Advanced_C
61/117
Pointers
Pointer Arithmetic(Contd)
Output
q before call = 5498
q after call = 5502
In main(), q has been declared as a float pointer. It means q is avariable capable of holding the address of a float. Through q= p the
address of p, a float value is stored in q and then printed out through
the printf(). This is the value before fun() is called. When fun() is
called the address of p is sent to it and is collected in r. At this juncture
r contains 5498. When r is incremented it would become 5502. r is a
float pointer and on incrementing by 1 it would point to the next float
which would be present 4 bytes hence, since every float is 4 bytes
long. The return statement then return this address 5502 back to main()
P i t
-
8/7/2019 Advanced_C
62/117
PointersPointer Arithmetic(Contd)
Pointers can be incremented and can be decremented as well, to point toearlier locations. Thus the following operations can beperformed
(a) Addition of a number to a pointer. For example,
int i=4,*j,*k;
j=&i;
j=j+1;
j=j+9;
k=j+3;(b) Subtraction of a number from a pointer. For example,
int i=4,*j,*k;
j=&i;
j=j-2;
j=j-5;
k=j-6;
A word of caution Do not attempt the following operations on pointers..They never work out
(a) Addition of two pointers
(b) Multiplying a pointer with a number
(c) Dividing a pointer with a number
P i t
-
8/7/2019 Advanced_C
63/117
Pointers
What is the output?main()
{
int a[]={10,20,30,40,50};
int j;
for (j=0;j
-
8/7/2019 Advanced_C
64/117
Pointers
Output
Error message:Lvalue required in function main
Whenever we mention the name of the array, we get its base address.
Therefore for the first time through the loop, the printf() should print
the value at its base address. There is no problem up to this. The
problem lies in the next statement, a++. Since C does not perform
bounds checking on an array, the only thing it remembers about anarray once declared is its base address. And a++ attempts to change
this base address, which C wont allow because if it does so, it would
be unable to remember the beginning of the array. Anything which can
change in compilers language is called lvalue. Since value of a cannot
be changed through ++, it flashes the error saying L value required sothat ++operator can change it.
-
8/7/2019 Advanced_C
65/117
Pointers
What is the output?main()
{float a[]={13.24,1.5,1.5,5.4,3.5};
Float *j,*k;
j = a;
k = a + 4;
j = j * 2;
k = k / 2;
printf(\n%f %f,*j,*k);
}
Pointers
-
8/7/2019 Advanced_C
66/117
Pointers
Output
Error message: Illegal use ofpointer infunction main
j and k have been declared as pointer variables, whichwould contain the addresses of floats. In other words, j andk are the pointers. To begin with, the base address of thearray a[] is stored in j. The next statement is perfectlyacceptable; the address of the 4th float from the baseaddress is stored in k. The next two statements areerroneous. This is because the only operations that can be
performed on pointers are addition and subtraction.Multiplication and division of a pointer is not allowed.Hence the error message.
Pointers
-
8/7/2019 Advanced_C
67/117
Pointers
Pointers to structs
Sometimes it is useful to assign pointers to structures.
Declaring pointers to structures is basically the same as declaring anormal pointer:
struct student *student_a;
But how do we dereference the pointer to the struct and its fields? You
can do it in one of two ways, the first way is: printf("%s\n", (*student_a).SSN);
This would get the SSN in student_a. Messy and the readability ishorrible! Is there a better way? Of course, programmers are lazy! :)
To dereference, you can use the infix operator: ->. The above example
using the new operator: printf("%s\n", student_a->SSN);
If we malloc'd space for the structure for *student_a could we startassigning things to pointer fields inside the structure? No. You mustmalloc space for each individual pointer within the structure that isbeing pointed to.
Pointers
-
8/7/2019 Advanced_C
68/117
Pointers
Common error while using pointers: Using an uninitialized pointer.
Remember, declaring a pointer variable simply allocates a cell that
can hold a pointer - it does not place a value in the cell. So, for
example, a code fragment like:
{int * iptr;
*iptr = 2;
. . .
}
will attempt to place the value, 2, in the cell pointed to by iptr;
however, iptr has not been initialized, so some garbage value will
be used as the address of there to place the value. On some
systems this may result in an attempt to access an illegal address,
and a memory violation. Avoid this error by remembering to
initialize all pointer variables before they are used.
-
8/7/2019 Advanced_C
69/117
Pointers
Common error while using pointers(Contd)
Instead of using a pointer to an object, a pointer to a pointer
is used.
Consider a function, read_int(). It reads an integer and stores it
where its argument points. The correct version is:
void read_int(int * pn){
scanf("%d", pn);
}
pn is a pointer to the object where the integer is to be stored. When
passing the argument to scanf(), we pass the pointer, pn, NOT pn.
-
8/7/2019 Advanced_C
70/117
Pointers
Common error while using pointers(Contd)
Confusion between the address of operator and the dereferenceoperator.
...
calling_func(...)
{
int x;
called_func(*x); /* should be x */ ... }
...
called_func(int px) /* should be * px */
{
...
}
A useful mnemonic aid is that the ` address of'' operator is the ``and''symbol, --- both start with letter, a.
F nction Pointers
-
8/7/2019 Advanced_C
71/117
Function Pointers
Function Pointers are pointers, i.e. variables, which point to the
address of a function
Ex: int (*pt2Function) (float, char, char);
//assign an address to the function pointer
int DoIt (float a, char b, char c)
{
printf("DoIt\n);
return a+b+c;
}
int DoMore(float a, char b, char c)
{
printf("DoMore\n";return a-b+c;
}
pt2Function = DoMore; //assignment
pt2Function = DoIt; //alternative using address operator
Function Pointers
-
8/7/2019 Advanced_C
72/117
Function Pointers
Comparing Function Pointers
The comparison-operator (==) is used for comparing functionEx: //comparing function pointers
if(pt2Function == DoIt)
Printf("pointer points to DoIt\n);"
Calling a Function using a Function Pointer
There are two alternatives to call a function using a function pointer:
Use the name of the function pointer instead of the name of thefunction.
Explicitly dereference it.
Ex: //calling a function using a function pointer
int result1 = pt2Function (12, 'a', 'b');
int result2 = (*pt2Function) (12, 'a', 'b');
Function Pointers
-
8/7/2019 Advanced_C
73/117
Function Pointers
Returning a Function Pointer
A function pointer can be a function's return value.float (*GetPtr1(const char opCode))(float, float)
{
if(opCode == '+')
return &Plus;
if(opCode == '-')return &Minus; }
// using a typedef
typedef float(*pt2Func)(float, float);
pt2Func GetPtr2(const char opCode)
{
if(opCode == '+')
return &Plus;
if(opCode == '-')
return &Minus;
}
-
8/7/2019 Advanced_C
74/117
Function Pointers
// execute example code
void Return_A_Function_Pointer()
{Printf( "Executing 'Return_A_Function_Pointer\n');
float (*pt2Function)(float, float); // define a function pointer
pt2Function=GetPtr1('+'); // get function pointer from function
'GetPtr1Printf(pt2Function(2, 4) \n); // call function using the
pointer
Printf(pt2Function=GetPtr2('-') \n); // get function pointerfrom function 'GetPtr2
Printf(pt2Function(2, 4)\n); // call function using the pointer
}
Function pointers are usually used
(a) In writing memory resident programs
(b) In writing viruses or vaccines to remove the viruses
Function Pointers
-
8/7/2019 Advanced_C
75/117
Function Pointers
Arrays of Function Pointers// 2.8 How to Use Arrays of Function Pointerstypedef int(*pt2Function)(float, char, char);
// illustrate how to work with an array of function pointers
Void Array_Of_Function_Pointers()
{
printf("Executing Array_Of_Function_Pointers\n);// is an array with 10 pointers to functions which return an int //
and take a float and two char
pt2Function funcArr[10]; // assign the function's address - 'DoIt' and'DoMore' are suitable functions // like defined above in 2.1-4
funcArr[0] = &DoIt;
funcArr[1] = &DoMore; /* more assignments */
// calling a function using an index to address the function pointer
printf(funcArr[1](12, 'a', 'b')\n);
printf(funcArr[0](12, 'a', 'b')\n);
}
Libraries
-
8/7/2019 Advanced_C
76/117
Libraries
A " library'' is simply a file containing compiled code (and
data) that is to be incorporated later into a program
A library is a file containing several object files, that can
be used as a single entity in a linking phase of a program.
Libraries allow programs to be more modular, faster to
recompile, and easier to update. Normally the library is indexed, so it is easy to find
symbols (functions, variables and so on) in them.
For this reason, linking a program whose object files are ordered
in libraries is faster than linking a program whose object files are
separate on the disk.
Also, when using a library, we have fewer files to look for and
open, which even further speeds up linking.
Lib i
-
8/7/2019 Advanced_C
77/117
Libraries can be divided into three types: static libraries
Collections of object files that are linked into the programduring the linking phase of compilation.
shared libraries
Linked into the program in two stages. First, during compile time, the linker verifies that all the
symbols (functions, variables and the like) required by theprogram, areeither linked into theprogram, or in one of itsshared libraries.
When theprogram is started, a program in the system
(called a dynamic loader) checks out which shared librarieswere linked with theprogram, loads them to memory, andattaches them to the copy of theprogram in memory
dynamically loaded (DL) libraries. Loaded and used at any time while a program is running
Libraries
Static Libraries
-
8/7/2019 Advanced_C
78/117
Static Libraries
Static libraries are simply a collection of ordinary
object files Conventionally, static libraries end with the ``.a''
suffix.
This collection is created using the ar (archiver)
program. Static libraries permit users to link to programs
without having to recompile its code, savingrecompilation time.
Static libraries are often useful for developers ifthey wish to permit programmers to link to theirlibrary, but don't want to give the library sourcecode
Static Libraries
-
8/7/2019 Advanced_C
79/117
Static Libraries
To create a static library, or to add
additional object files to an existing static
library, command to be used is:
ar rcs my_library.a file1.o file2.o
Static library can be used by invoking it as partof the compilation and linking process when
creating a program executable.
If gcc(1) is used to generate executable, -l option
can be used to specify the link the library
Shared Libraries
-
8/7/2019 Advanced_C
80/117
Shared Libraries
Shared libraries are libraries that are loaded by
programs when they start. When a shared library is installed properly, all
programs that start afterwards automatically usethe new shared library
Every shared library has a special name called the``soname''.
The soname has the prefix ``lib'', the name of thelibrary, the phrase ``.so'', followed by a period and
a version number that is incremented wheneverthe interface changes (as a special exception, thelowest-level C libraries don't start with ``lib'').
Shared Libraries
-
8/7/2019 Advanced_C
81/117
A fully-qualified soname includes as a prefix the directory
it's in; on a working system a fully-qualified soname issimply a symbolic link to the shared library's ``real name''.
/usr/lib/libreadline.so.3 is a fully-qualified soname
Shared libraries must be placed somewhere in the
filesystem The GNU standards recommend installing by default all
libraries in /usr/local/lib
What's the advantage of creating executables using
Dynamic Libraries? The executable is much smaller thanwith static libraries. If it is a standard library that can be
installed, there is no need to compile it into the executable
at compile time!
Shared Libraries
-
8/7/2019 Advanced_C
82/117
Shared Libraries
Creating shared or dynamic libraries is simple also.Using the previous example, to create a shared library
gcc -fPIC -c objfile1.c
gcc -fPIC -c objfile2.c
gcc -fPIC -c objfile3.c
gcc -shared -o libmylib.so objfile1.o objfile2.o objfile3.o
The -fPIC option is to tell the compiler to create PositionIndependent Code (create libraries using relativeaddresses rather than absolute addresses because these
libraries can be loaded multiple times). The -shared option is to specify that an architecture-
dependent shared library is being created.
However, not all platforms support this flag
-
8/7/2019 Advanced_C
83/117
Shared Libraries
Now we have to compile the actualprogram using the libraries:
gcc -o foo -L. -lmylib foo.o
Notice it is exactly the same as creatinga static library. Although, it is compiledin the same way, none of the actuallibrary code is inserted into theexecutable, hence the dynamic/sharedlibrary.
Note: You can automate this processusing Makefiles!
Dynamic Loaded Libraries
-
8/7/2019 Advanced_C
84/117
Dynamic Loaded Libraries
Dynamically loaded (DL) libraries are
libraries that are loaded at times other thanduring the startup of a program. They'reparticularly useful for implementing pluginsor modules, because they permit waiting toload the plugin until it's needed
They're also useful for implementinginterpreters that wish to occasionally
compile their code into machine code anduse the compiled version for efficiencypurposes, all without stopping
Lib i
-
8/7/2019 Advanced_C
85/117
Libraries The key to making your program work with
dynamic libraries is through theLD_LIBRARY_PATH enviornment variable.
To display this variable, at a shell: echo$LD_LIBRARY_PATH Will display this variable ifit is already defined.
If it isn't, you can create a wrapper script foryour program to set this variable at run-time.Depending on your shell, simply use setenv(tcsh, csh) or export (bash, sh, etc)commands.
If you already have LD_LIBRARY_PATHdefined, make sure you append to thevariable, not overwrite it!
-
8/7/2019 Advanced_C
86/117
Libraries
For example:
setenv LD_LIBRARY_PATH/path/to/library:${LD_LIBRARY_PATH}
would be the command you would use if you had tcsh/csh and alreadyhad an existing LD_LIBRARY_PATH.
If you didn't have it already defined, just remove everything right ofthe :.
An example with bash shells:
exportLD_LIBRARY_PATH=/path/to/library:${LD_LIBRARY_PATH}
Again, remove the stuff right of the : and the : itself if you don'talready have an existing LD_LIBRARY_PATH.
If you have administrative rights to your computer, you can install theparticular library to the /usr/local/lib directory and permanently add anLD_LIBRARY_PATH into your .tcshrc, .cshrc, .bashrc, etc. file.
Make command
-
8/7/2019 Advanced_C
87/117
Make command
Make allows a programmer to easilykeep track of a project by maintainingcurrent versions of their programs from
separate sources. Make can automate various tasks for
not only compiling proper branch ofsource code from the project tree, but
helping to automate other tasks, suchas cleaning directories, organizingoutput, and even debugging.
Make and Makefiles
-
8/7/2019 Advanced_C
88/117
Make and Makefiles
Make reads its instructions from text files.
An initialization file is read first, followed by the makefile.
The initialization file holds instructions for all makes and is used tocustomize the operation of Make.
Make automatically reads the initialization file whenever it starts up.Typically the initialization file is named make.ini and it resides in the
directory of make.exe and mkmf.exe. The name and location of theinitialization file is discussed in detail on Page .
The makefile has instructions for a specific project. The default name ofthe makefile is literally makefile, but the name can be specified with acommand-line option.
With a few exceptions, the initialization file holds the same kind of
information as does a makefile. Both the initialization file and themakefile are composed of the following components: comments,dependency lines, directives, macros, response files, rules and shelllines.
Make and Makefiles
-
8/7/2019 Advanced_C
89/117
Make and MakefilesContinued Makefile Lines
Lines in the makefile can be very long. For easier reading a long line can bebroken up by putting \enter as the last characters of this line and the rest ofthis (logical) line on the next (physical) line of the makefile. For example:
first_part_of_line second_part_of_line
is the same as:
first_part_of_line\
second_part_of_line
Comments
The simplest makefile statement is a comment, which is indicated by thecomment character #. All text from the comment character to the end of theline is ignored. Here is a large comment as might appear in a makefile todescribe its contents:
#
#Makefile for Opus Make 6.1
#
# Compiler: Microsoft C 6.0
# Linker: Microsoft Link 5.10
The comment character could also be used at the end of another makefilestatement:
somemakefilestatement# a comment
Make and Makefiles
-
8/7/2019 Advanced_C
90/117
Make and MakefilesComments and Continued Makefile Lines
If \enter appears on a commented line, the comment acts until the end of the line andthe following line is still continued. For example:
line_one \line_two#more_line_two \
line_three
is the same as:
line_one line_twoline_three
Rules
A rule tells Make both when and how to make a file. As an example, suppose yourproject involves compiling source files main.c and io.c then linking them to produce theexecutable project.exe. Withholding a detailed explanation for a bit, here is a makefileusing Borland C which will manage the task of making project.exe:
The ExampleMakefile
project.exe : main.obj io.obj
tlink c0s main.obj io.obj, project.exe,, cs /Lf:\bc\lib
main.obj : main.cbcc ms c main.c
io.obj : io.c
bcc ms c io.c
This makefile shows three rules, one each for making project.exe, main.obj, and io.obj.The rules as shown above are called explicit rules since they are supplied explicitly inthe makefile. Make also has inference rules that generalize the make process.
Make and Makefiles
-
8/7/2019 Advanced_C
91/117
Make and MakefilesDependency Lines: When to Build a Target
The lines with the colon : in them are called dependencylines. They determine when the target is to be rebuilt.
To the left of the colon is the target of the dependency. Tothe right of the colon are the sources needed to make thetarget. A dependency line says the target depends on the
sources. For example, the line:project.exe : main.obj io.obj
states that project.exe depends on main.obj and io.obj. Atrun time Make compares the time that project.exe was lastchanged to the times main.obj and io.obj were last changed.
If either source is newerthan project.exe, Make rebuildsproject.exe. The last-changed time is the target's time as itappears in the file-system directory. This time is also knownas the target's timestamp.
Make and Makefiles
-
8/7/2019 Advanced_C
92/117
Make and MakefilesTheMake Process is Recursive
It is a basic feature of Make that a target's sources are made before thetimestamp comparison occurs. The line:
project.exe : main.obj io.obj
implies make main.obj and io.obj before comparing their timestampswith project.exe. In turn:
main.obj : main.c
says make main.c before comparing its timestamp with main.obj.You can see that if main.c is newer than main.obj, main.obj will berebuilt. Now main.obj will be newer than project.exe, causingproject.exe to be rebuilt.
Additional Dependencies
In C and in other programming languages it is possible to include thecontents of a file into the file currently being compiled. Since thecompiled objectdepends on the contents of the included file, we addthe included file as a source of the objectfile.
Make and Makefiles
-
8/7/2019 Advanced_C
93/117
Make and Makefiles Assume each of main.c and io.c include def.h. We can either change
two dependency lines in the makefile:
main.obj : main.c becomes main.obj : main.c def.hio.obj : io.c becomes io.obj : io.c def.h
or add a new line which lists only the additional dependencies:
main.obj io.obj : def.h
Notice that there are two targets on the left of the colon. This linemeans that both main.obj and io.obj depend on def.h. Either of these
methods are equivalent. The example makefile now looks like:project.exe : main.obj io.obj
tlink c0s main.obj io.obj, project.exe,, cs /Lf:\bc\lib
main.obj : main.c
bcc ms c main.c
io.obj : io.c
bcc ms c io.c
main.obj io.obj : incl.h
Make and Makefiles
-
8/7/2019 Advanced_C
94/117
Make and MakefilesShell Lines: How to Build a Target [Top]
The indented lines that follow each dependency line are called shelllines. Shell lines tell Make how to build the target. For example:
project.exe : main.obj io.obj
tlink c0s main.obj io.obj, project.exe,, cs /Lf:\bc\lib
tells Make that making project.exe requires running the program tlinkto link main.obj and io.obj. This shell line would be run only if
main.obj or io.obj was newer than project.exe. For tlink, c0s is the small model start-up object file and the cs is the
small model library. The /Lf:\bc\lib flag tells tlink that the start-upobject file and library files can be found in the f:\bc\lib directory.
A target can have more than one shell line, listed one after the other,such as:
project.exe : main.obj io.obj
echo Linking project.exe
tlink c0s main.obj io.obj, project.exe,, cs /Lf:\bc\lib >tlink.out
The first line shows that command processor commands can beexecuted by Make. The second line shows redirection of output, where
the output of the tlink program is redirected to the tlink.out file.
Make and Makefiles
-
8/7/2019 Advanced_C
95/117
After each shell line is executed, Make checks the shell line exitstatus. By convention, programs return a 0 (zero) exit status if theyfinish without error and non-zero if there was an error. The first shellline returning a non-zero exit status causes Make to display themessage:
OPUS MAKE: Shell line exit status exit_status. Stop.
This usually means the program being executed failed. Some
programs return a non-zero exit status inappropriately and you canhave Make ignore the exit status by using a shell-lineprefix. Prefixesare characters that appear before the program name and modify theway Make handles the shell line. For example:
project.exe : main.obj io.obj
tlink c0s main.obj io.obj, project.exe,, cs /Lf:\bc\lib
The prefix tells Make to ignore the exit status of shell line. If theexit status was non-zero Make would display the message:
OPUS MAKE: Shell line exit status exit_status (ignored)
Make and Makefiles
-
8/7/2019 Advanced_C
96/117
Make and Makefiles
Macros
The example makefile is reproduced here:project.exe : main.obj io.obj
tlink c0s main.obj io.obj, project.exe,, cs /Lf:\bc\lib
main.obj : main.c
bcc ms c main.c
io.obj : io.cbcc ms c io.c
main.obj io.obj : def.h
We see that the text main.obj io.obj occurs repeatedly. To cut downon the amount of repeated text, we can use a macro definition to assign asymbol to the text.
Defining Macros in theMakefile
A macro definition line is a makefile line with a macro name, an equalssign =, and a macro value. In the makefile, expressions of the form$(name) or ${name} are replaced with value. If the macro name is asingle letter, the parentheses or braces are optional (i.e. $X, $(X) and
${X} all mean the value of macro X).
Make and Makefiles
-
8/7/2019 Advanced_C
97/117
Make and Makefiles Here is the above example written with the introduction of four macros:
OBJS = main.obj io.obj
MODEL = sCC = bcc
CFLAGS = m$(MODEL)
project.exe : $(OBJS)
tlink c0$(MODEL) $(OBJS), project.exe,, c$(MODEL) /Lf:\bc\lib
main.obj : main.c$(CC) $(CFLAGS) c main.c
io.obj : io.c $(CC) $(CFLAGS) c io.c
$(OBJS) : incl.h
The value of the OBJS macro is the list of object files to be compiled.The macro definitions for MODEL, CC and CFLAGS were introduced
so that it is easier to change the compiler memory model, name of the Ccompiler and its options.
Make automatically imports environment variables as macros, so you canreference an environment variable such as PATH with the makefileexpression $(PATH).
Make and Makefiles
-
8/7/2019 Advanced_C
98/117
Defining Macros on the Command Line
Macros can be defined on the Make command line. For example:
make CFLAGS=ms
would start up Make and define the macro CFLAGS with the value ms. Macros defined on the command line take precedence overmacros of the same name defined in the makefile.
If a command-line macro contains spaces, it must be enclosed in
double quotes as in:make "CFLAGS=-ms -z -p"
Run-TimeMacros
Make defines some special macros whose values are set dynamically.
These macros return information about the current target being built.As examples, the .TARGET macro is name of the current target, the.SOURCE macro is the name of the inferred source (from an inferencerule) or the first of the explicit sources and the .SOURCES macro isthe list of all sources.
Make and Makefiles
-
8/7/2019 Advanced_C
99/117
Make and Makefiles
Using run-time macros the example can be written:OBJS = main.obj io.obj
CC = bcc
MODEL = s
CFLAGS = m$(MODEL)
project.exe : $(OBJS)
tlink c0$(MODEL) $(OBJS), $(.TARGET),, c$(MODEL)/Lf:\bc\lib
main.obj : main.c
$(CC) $(CFLAGS) c $(.SOURCE)
io.obj : io.c
$(CC) $(CFLAGS) c $(.SOURCE)$(OBJS) : incl.h
As you can see, the shell lines for updating main.obj and io.obj areidentical when run-time macros are used. Run-time macros areimportant for generalizing the build process with inference rules.
Make and Makefiles
-
8/7/2019 Advanced_C
100/117
Macro Modifiers
Macros are used to reduce the amount of repeated text. They are alsoused in inference rules to generalize the build process. We often wantto start with the value of a macro and modify it in some manner. Forexample, to get the list of source files from the OBJS macro we can do:
SRCS = $(OBJS,.obj=.c)
This example uses the from=to macro modifier to replace the from
text in the expansion of OBJS with the to
text. The result is that$(SRCS) is main.c io.c. In general, to modify a macro expand itwith:
$(name,modifier[,modifier...])
Each modifieris applied in succession to the expanded value ofname.Each modifier is separated from the next with a comma.
Filename Components
There is a set of macro modifiers for accessing parts of file names. Forexample, with the macro definition:
SRCS = d:\src\main.c io.asm
Make and Makefiles
-
8/7/2019 Advanced_C
101/117
Some of the modifiers are:
Modifier, and description Example Value
D, the directory $(SRCS,D) d:\src .E, the extension (or suffix) $(SRCS,E) .c .asm
F, the file name $(SRCS,F) main.c io.asm
Tokenize
Another modifier is the Wstr modifier, which replaces whitespacebetween elements of the macro with str, a string. The strcan be a mixof regular characters and special sequences, the most importantsequence being \n which represents a newline character (like hittingthe enter key). For example:
$(OBJS,W space +\n) is main.obj +
io.obj
Other Modifiers
Other modifiers include: @ (include file contents), LC (lowercase), UC (upper case), M (member) and N (non-member). TheM and N modifiers and the S (substitute) modifier use regularexpressions for powerful and flexible pattern-matching. See Page formore information on all macro modifiers.
Make and Makefiles
-
8/7/2019 Advanced_C
102/117
Inference Rules
Inference rules generalize the build process so you don't have to give an
explicit rule for each target. As an example, compiling C source (.cfiles) into object files (.obj files) is a common occurrence. Rather thanrequiring a statement that each .obj file depends on a like-named .c file,Make uses an inference rule to infer that dependency. The sourcedetermined by an inference rule is called the inferred source.
Inference rules are rules distinguished by the use of the character %
in the dependency line. The % (rule character) is a wild card,matching zero or more characters. As an example, here is an inferencerule for building .obj files from .c files:
%.obj : %.c
$(CC) $(CFLAGS) c $(.SOURCE)
This rule states that a .obj file can be built from a corresponding .c filewith the shell line $(CC) $(CFLAGS) c $(.SOURCE). The .c and.obj files share the same root of the file name.
When the source and target have the same file name except for theirextensions, this rule can be specified in an alternative way:
.c.obj :
$(CC) $(CFLAGS) c $(.SOURCE)
Make and Makefiles
-
8/7/2019 Advanced_C
103/117
The alternative form is compatible with Opus Make prior to
this version and with other make utilities and is discussed inmore detail on Page .
Make predefines the %.obj : %.c inference rule as listedabove so the example we have been working on nowbecomes much simpler:
OBJS = main.obj io.objCC = bcc
MODEL = s
CFLAGS = m$(MODEL)
project.exe : $(OBJS)
tlink c0$(MODEL) $(OBJS), $(.TARGET),, c$(MODEL)/Lf:\bc\lib
$(OBJS) : incl.h
Make and Makefiles
-
8/7/2019 Advanced_C
104/117
Response Files
For MS-DOS, OS/2 Win95 there is a rather severe restriction on thelength of a shell line with the result that the shell line is often too shortfor many compilers and far too short for linkers and librarians.
To overcome this restriction many programs can receive command-lineinput from a response file. Opus Make has two kinds of support forresponse files: automatic response files, where Make decides when tobuild a response file or; inline response files, where you write responsefile-creating statements directly in the makefile.
Automatic Response Files
Make has predefined support for several linkers, librarians andcompilers, and you can augment Make's support by writing your owndefinitions. With Make's predefined support you can just add the
following statement to your makefile:.RESPONSE.LINK : tlink
This tells Make that the program tlink accepts LINK-style response files.When a shell line executes tlink, Make checks if the shell line is longerthan allowed by the operating system and automatically produces aresponse file if necessary.
Make and Makefiles
-
8/7/2019 Advanced_C
105/117
Inline Response Files
Response files can also be coded inline in your makefile. Here is thetlink shell line of the example, written to use an inline response file:project.exe : $(OBJS)
tlink @
-
8/7/2019 Advanced_C
106/117
Makefile Directives
Makefile directives control the makefile lines Make reads at read time. Here is
our example extended with conditional directives (%if, %elif, %else and%endif) to support both Borland and Microsoft compilers. Comments have beenadded for documentation:
# This makefile compiles the project listed in the PROJ macro
#
PROJ = project # the name of the project
OBJS = main.obj io.obj # list of object files
# Configuration:
#
MODEL = s # memory model
CC = bcc # name of compiler
# Compiler-dependent section#
%if $(CC) == bcc # if compiler is bcc
CFLAGS = m$(MODEL) # $(CFLAGS) is ms
LDSTART = c0$(MODEL) # the start-up object file
LDLIBS = c$(MODEL) # the library
LDFLAGS = /Lf:\bc\lib # f:\bc\lib is library directory
Make and Makefiles
-
8/7/2019 Advanced_C
107/117
%elif $(CC) == cl # else if compiler is cl
CFLAGS = A$(MODEL,UC) # $(CFLAGS) is AS
LDSTART = # no special start-upLDLIBS = # no special library
LDFLAGS = /Lf:\c6\lib; # f:\c6\lib is library directory
%else # else
% abort Unsupported CC==$(CC) # compiler is not supported
%endif # endif
# The project to be built
# $(PROJ).exe : $(OBJS)
tlink $(LDSTART) $(OBJS), $(.TARGET),, $(LDLIBS) $(LDFLAGS)
$(OBJS) : incl.h
The layout of this makefile is fairly traditional macros are defined first, the
primary target follows the macros and the extra dependency information islast.
This example also uses the %abort directive to abort Make if the makefile doesnot support a particular compiler. Directives can also be used at run time, tocontrol the shell lines Make executes
Makefiles
-
8/7/2019 Advanced_C
108/117
Makefiles
You can include other Makefiles by using the include directive.
You can create conditional syntax in Makefiles, using ifdef, ifeq,ifndef, ifneq.
You can create variables inside of Makefiles, like the $(objects)above.
Let's use a different example. The hypothetical source tree:
a.c__/ \__
/ \
b.c c.c
__/__ __\__
/ \ / \d.c e.h f.c g.c
__ /__ __/__
/ \ / \
h.c i.c j.h
Makefiles
-
8/7/2019 Advanced_C
109/117
Let's create a more complex, yet easier to maintain Makefile for this
project: # Source, Executable, Includes, Library Defines
INCL = e.h j.h
SRC = a.c b.c c.c d.c f.c g.c h.c i.c
OBJ = $(SRC:.c=.o)
LIBS = -lgenEXE = moolicious
# Compiler, Linker Defines
CC = /usr/bin/gcc
CFLAGS = -ansi -pedantic -Wall -O2
LIBPATH = -L.LDFLAGS = -o $(EXE) $(LIBPATH) $(LIBS)
CFDEBUG = -ansi -pedantic -Wall -g -DDEBUG $(LDFLAGS)
RM = /bin/rm -f
Makefiles
-
8/7/2019 Advanced_C
110/117
Makefiles# Compile and Assemble C Source Files into Object Files
%.o: %.c$(CC) -c $(CFLAGS) $*.c
# Link all Object Files with external Libraries into Binaries
$(EXE): $(OBJ)
$(CC) $(LDFLAGS) $(OBJ)
# Objects depend on these Libraries
$(OBJ): $(INCL)
# Create a gdb/dbx Capable Executable with DEBUG flags turned on
debug:
$(CC) $(CFDEBUG) $(SRC)# Clean Up Objects, Exectuables, Dumps out of source directory clean:
$(RM) $(OBJ) $(EXE) core a.out
Memory Corruption
-
8/7/2019 Advanced_C
111/117
SIGSEGV This! SIGBUS That!
We saw that some virtual memory sections aren't mapped to physicalmemory. While some of them were simply paged out, others were
never allocated by the process. When a process runs, its virtual
memory table is small. As it allocates more memory pages, the table
grows. However, if the process tries to access a virtual memory
address of a section it hasn't allocated yet, the operating system has no
where to bring this page from. The designers of the Unix system
decided that this situation indicates a program bug, and thus instead of
making an automatic allocation of a memory page in such a case, they
chose to send a signal to the process. This signal is a SEGV signal (or
SIGSEGV), and its default signal handler prints out a "Segmentation
violation - core dumped" message, and dumps the memory image ofthe process into a file named 'core' in the process's current directory.
Memory Corruption
-
8/7/2019 Advanced_C
112/117
Another way to cause a 'segmentation violation' is trying to access anillegal location of virtual memory. Because many invalid pointer
problems occur with very low pointer values, the operating system doesnot allow a process to allocate a memory page for the virtual memorysection beginning with the virtual address '0'. This is what causesprograms to receive a SEGV signal when trying to dereference a NULLpointer (NULL on _most_ machine architectures is defined as '0').
What about a BUS (or SIGBUS) signal? this signal is sent to a program
that tries to access a non-aligned pointer. For instance, on manymachine architectures, access to 'long' (4 byte) numbers must be doneusing a memory address that divides by 4. Trying to access such anentity using an address that does not abide by this rule will cause theCPU to emit a trap. The operating system's kernel catches this trap, andthen sends a BUS signal to the program. The default signal handler for
this signal emits a "Bus error - core dumped" message, and dumps thememory contents to a 'core' file, much like the handler for the SEGVsignal does.
Debugging
-
8/7/2019 Advanced_C
113/117
We will be using three debugging techniques:
1. Non-interactive
2. GNU gdb
3. dbx
You can debug your code by placing #ifdef DEBUG and
corresponding #endif statements around debug code. For example:
#ifdef DEBUG
PRINTF(("Variables Currently Contain: %d, %f, %s\n", *pi, *pf[1],
str));
#endif
You can specify a DEBUG define at compile time by issuing gcc withthe -DDEBUG command option.
Note: This can be even further simplified into a single command called
DPRINTF, so you don't even have to write the #ifdef #endif directives!
Debugging using gdb
-
8/7/2019 Advanced_C
114/117
gdb is a powerful program in tracking down Segmentation Faults and
Core Dumps. It can be used for a variety of debugging purposes
though. First thing you must do is compile with the -g option and without any
optimization (i.e. no -O2 flag).
Once you do that, you can run gdb . where is the name of
the executable.
gdb should load with the executable to run on. Now you can createbreakpoints where you want the the execution to stop. This can be
specified with the line number in the corresponding c source file. For
example: break 376 would instruct gdb to stop at line 376.
You can now run the program by issuing the run command. If your
program requires command-line options or parameters, you can specifythem with the run command. For example: run 4 -s Doc! where 4, -s,
Doc! are the parameters.
Debugging using gdb
-
8/7/2019 Advanced_C
115/117
The program should run until the breakpoint or exit on a failure. If it
fails before the breakpoint you need to re-examine where you should
specify the break. Repeat the breakpoint step and rerun. If your programstops and shows you the breakpoint line, then you can step into the
function. To step into the function use the step command. NOTE: Do
not step into system library calls (e.g. printf). You can use the command
next over these types of calls or over local function calls you don't wish
to step into. You can repeat the last command by simply pressing enter. You can use the continue command to tell gdb to continue executing
until the next breakpoint or it finishes the program.
If you want to peek at variables, you can issue the print command on the
variable. For example: print mystruct->data.
You can also set variables using the set command. For example: setmystruct->data = 42.
The ptype command can tell you what type a particular variable is.
Debugging using gdb
-
8/7/2019 Advanced_C
116/117
gg g g g
The commands instruction tells gdb to set a particular number ofcommands and to report them to you. For example, commands 1 will
allow you to enter in a variable number of other commands (one per
line, end it with "end"), and will report those commands to you once
breakpoint 1 is hit.
The clear command tells gdb to clear a specified breakpoint. The list command can tell you where you are at in the particular code
block.
You can specify breakpoints not only with lines but with function
names.
For more information on other commands, you can issue the helpcommand inside gdb.
Debugging using dbx
-
8/7/2019 Advanced_C
117/117
gg g g dbx is a multi-threaded program debugger. This program is
great for tracking down memory leaks. dbx is not found onlinux machines (it can be found on Solaris or other *NIXmachines).
Run dbx with the executable like gdb. Now you can setarguments with runargs.
After doing that, issue the check -memuse command. Thiswill check for memory use. If you want to also check foraccess violations, you can use the check -all command.
Run the program using the run command. If you get anyaccess violations or memory leaks, dbx will report them to
you.
Run the help command if you need to understand othercommands or similar gdb commands.