You get a bonus - 1 coin for daily activity. Now you have 1 coin

6. OTHER POSSIBILITIES OF C

Lecture



Dynamic memory allocation. Malloc () and free () functions

In C, the following memory allocation is assumed:

Table 4

STACK Top addresses
FREE MEMORY  
SECTION OF GLOBAL
VARIABLES AND CONSTANT
 
PROGRAM CODE Lower addresses

For global variables, there is a fixed place in memory for the entire duration of the program. Local variables are stored on the stack. Between them there is a memory area for dynamic distribution.

The malloc () and free () functions are used to dynamically allocate free memory. The malloc () function allocates memory, the free () function frees it. The prototypes of these functions are stored in the stdlib.h header file and have the form:

  void * malloc (size_t size); 
       void * free (void * p); 
 

The malloc () function returns a void pointer; for proper use, the value of the function must be converted to a pointer to the appropriate type. If successful, the function returns a pointer to the first byte of free size memory. If there is not enough memory, the value 0 is returned. To determine the number of bytes required for the variable, use the sizeof () operation.

An example of using these functions:

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

  void main (void) 
  { 
     int * p, i; 
     p = (int *) malloc (100 * sizeof (int));  / * Memory allocation for 100  
                                               integers * / 
     if (! p)  
     { 
         printf ("Not enough memory \ n"); 
         exit (1); 
     } 
     for (i = 0; i <100; ++ i) * (p + i) = i;  / * Memory usage * / 
     for (i = 0; i <100; ++ i) printf ("% d", * (p ++)); 
     free (p);  / * Memory release * / 
  } 
 

Before using the pointer returned by malloc (), you need to make sure that there is enough memory (the pointer is not null).

  6. OTHER POSSIBILITIES OF C

Preprocessor

C preprocessor is a program that processes input to the compiler. The preprocessor scans the source program and performs the following actions: connects the specified files to it, performs substitutions, and also controls the compilation conditions. For the preprocessor, there are intended program lines starting with the # character. Only one com *** y (preprocessor directive) is allowed per line.

Directive

  #define id substitution 
 

causes a replacement of the named identifier by the replacement text in the subsequent text of the program (note the absence of a semicolon at the end of this command). Essentially, this directive introduces a macro definition (macro), where "identifier" is the name of the macro definition, and "substitution" is a sequence of characters with which the preprocessor replaces the specified name when it finds it in the program text. The name of the macro is made in capital letters.

Consider examples:

  #define MAX 25 
       #define BEGIN {  
 

The first line causes the MAX identifier to be replaced with a constant 25. The second allows you to use the word BEGIN instead of the opening curly bracket () in the text.

Note that since the preprocessor does not check the compatibility between the symbolic names of macro definitions and the context in which they are used, it is recommended that identifiers of this kind be defined not by the #define directive, but using the const keyword with an explicit type indication (this is more to the C + +):

  const int max = 25; 
 

(type int can be not specified, since it is set by default).

If the directive #define is:

  #define id (id, ... id) lookup 
 

and there is no space between the first identifier and the opening parenthesis, then this is a macro-substitution definition with arguments. For example, after the appearance of a line like:

  #define READ (val) scanf ("% d", & val) 
 

operator READ (y); perceived as scanf ("% d", & y) ;. Here val is an argument and macro substitution is performed with an argument.

If there are long definitions in the substitution that continue in the next line, the \ character is put at the end of the next line with a continuation.

Objects separated by ## characters can be placed in the macro definition, for example:

  #define PR (x, y) x ## y  
 

After this, PR (a, 3) will cause a3 substitution. Or, for example, macros

  #define z (a, b, c, d) a (b ## c ## d) 
 
will replace z (sin, x, +, y) with sin (x + y).

The # character, placed before the macro argument, indicates that it is converted to a string. For example, after the directive

  #define PRIM (var) printf (#var "=% d", var)  
 

next program text

  year = 2006;  
       PRIM (year);  
 

transformed as follows:

  year = 2006; 
       printf ("year" "=% d", year);  
 

We describe other preprocessor directives. The #include directive has already been met. It can be used in two forms:

  #include "file name"  
       #include <file name>  
 

The action of both teams is reduced to the inclusion in the program files with the specified name. The first one loads the file from the current or prefixed directory. The second com *** and searches for a file in the standard places defined in the programming system. If the file whose name is written in double quotes is not found in the specified directory, the search will continue in the subdirectories specified for the #include <...> command. #Include directives can be nested inside one another.

The next group of directives allows you to selectively compile parts of the program. This process is called conditional compilation. This group includes directives #if, #else, #elif, #endif, #ifdef, #ifndef. The main form of the #if directive is:

  #if constant_expression sequence_operators  
       #endif 
 

Here the value of the constant expression is checked. If it is true, then the specified sequence of statements is executed, and if it is false, then this sequence of statements is skipped.

The action of the #else directive is similar to the action of the else command in the C language, for example:

  #if constant_expression  
                  statement sequence_1 
       #else    
                  statement sequence_2 
       #endif 
 

Here, if the constant expression is true, then operator_sequence_1 is executed, and if false, an operator_sequence_2.

The #elif directive means an action like "else if". The main form of its use is:

  #if constant_expression 
                 statement sequence 
       #elif constant_expression_1 
                 statement sequence_1 
       #elif constant_expression_n 
                 statement sequence_n 
       #endif 
 

This form is similar to a type C construct: if ... else if ... else if ...

Directive

  #ifdef id 
 

determines whether the specified identifier is currently defined, i.e. Whether it was included in directives like #define. View string

  #ifndef id 
 

checks whether the specified identifier is currently undefined. Any of these directives can be followed by an arbitrary number of lines of text, possibly containing the #else instruction (#elif cannot be used) and ending with the string #endif. If the condition being checked is true, then all lines between #else and #endif are ignored, and if false, then the lines between checking and #else (if the word #else is not, then #endif). The #if and #ifndef directives can be nested inside one another.

Directive of the species

  #undef id 
 

leads to the fact that the specified identifier begins to be considered undefined, i.e. not replaceable.

Consider examples. The following three directives:

  #ifdef WRITE 
       #undef WRITE 
       #endif 
 

check whether the identifier WRITE is defined (i.e. whether there was a com *** in the form #define WRITE ...), and if so, then the name WRITE begins to be considered undefined, i.e. not replaceable.

Directives

  #ifndef WRITE 
       #define WRITE fprintf 
       #endif 
 

check whether the WRITE identifier is undefined, and if so, the WRITE identifier instead of the fprintf name is determined.

The #error directive is written in the following form:

  #error error_message 
 

If it is found in the text of the program, the compilation stops and an error message is displayed on the screen. This com *** is mainly used at the debugging stage. Note that the error message should not be enclosed in double quotes.

The #line directive is intended to change the values ​​of the _LINE_ and _FILE_ variables defined in the C programming system. The variable _LINE_ contains the line number of the program currently being executed. The _FILE_ identifier is a pointer to a string with the name of the program being compiled. The #line directive is written as follows:

  #line number "filename" 
 

Here, the number is any positive integer that will be assigned to the _LINE_ variable, the file_name is an optional parameter that overrides the value of _FILE_.

The #pragma directive allows you to pass some instructions to the compiler. For example, the string

  #pragma inline 
 

says that the program in the C language has strings in the assembly language. For example:

  asm mov ax, 5 
       asm { 
             inc dx 
             sub bl, al 
       } 
 
etc.

Consider some global identifiers or macro names (macro names). Five such names are defined: _LINE_, _FILE_, _DATE_, _TIME_, _STDC_. Two of them (_LINE_ and _FILE_) have already been described above. The _DATE_ identifier defines a string in which the date of translation of the source file into the object code is stored. The _TIME_ identifier specifies a string that preserves the time of translation of the source file into the object code. The _STDC_ macro is set to 1 if the standard defined macro names are used. Otherwise, this variable will not be defined.

  6. OTHER POSSIBILITIES OF C

Use of software-accessible registers for the Intel 8086 microprocessor

In the C language for IBM-compatible personal computers, access to the registers of the Intel 8086 microprocessor is performed using special objects called pseudo-variables. The full list of pseudo-variables includes 21 elements: _AX, _BX, _CX, _DX, _CS, _DS, _SS, _ES, _SP, _BP, _DI, _SI, _AL, _AH, _BL, _BH, _CL, _CH, _DL, _DH, _FLAGS. Their names are formed from the names of registers with the prefix _ (for example, the variable _AX is associated with the register AX). The first twelve pseudo-variables and the last are of type unsigned int, and the remaining eight are unsigned char. Assigning a value to a variable, for example _AX, causes the entry of this value in the register AX. Getting the value of a variable, for example _BX, is equivalent to getting the value from the register BX.


Comments


To leave a comment
If you have any suggestion, idea, thanks or comment, feel free to write. We really value feedback and are glad to hear your opinion.
To reply

Algorithmization and programming. Structural programming. C language

Terms: Algorithmization and programming. Structural programming. C language