3.  Условной компиляцией.

Это позволяет создавать мобильные, более удобочитаемые и более удобные для сопровождения программы.

# - первый символ в левой позиции, в любом месте исходного файла сообщает препроцессору, что далее следует директива. Она имеет действие до конца файла.

Подстановка имен

Директивы:

#define – создать макроопределение

#undef - удалить макроопределение

Директива #define служит для:

а) определения символических констант

#define TWO 2

#define MSG “Приднестровский Университет”

#define PX printf(“X равен %d\n”,x)

#define FMT “X равен %d”

void main (void) {

int x=TWO;

PX;

Printf(FMT, x);

Printf(“%s\n”,MSG);

Printf(“TWO:MSG\n”);

}

Макроопределение не должно содержать пробелы. Процесс прохождения от макроопределения до заключительной строки называется макрорасширением. Препроцессор делает подстановки.

б) Макроопределение с аргументом.

Очень похоже на функцию (макро-функция).

#define SQUARE(x) x*x

#define PR(x) printf(“X равен %d\n”,x)

void main(void) {

int x=4; int z;

z=SQUARE(x); //16

PR(z);

Z=SQUARE(2); //4

PR(z);

PR(SQUARE(X));

PR(100/SQUARE(2));//100

PR(SQUARE(X+2)); //14

PR(SQUARE(++X)); //30

}

Макрофункция ведет к увеличению программы. Функция к увеличению времени работы программы. Макроопределение создает строчный код, не нужно заботится о типах переменных.

Включение файлов

Директива #include позволяет включить в текст содержимое файла, имя которого является параметром директивы.

1. #include <stdio. h> - ищет в системном каталоге

НЕ нашли? Не то? Что вы ищете?

2. #include “stdio. h” – ищет в текущем рабочем каталоге.

3. Можно создавать свои файлы с функциями и подключать.

#include “my_file. cpp”

4. Можно подключать макроопределение. Макрорасширение макроса должно приводить к одной из первых двух форм директивы.

#define MY_FILE “d:\\bc\work\\my_file. h”

#include MY_FILE

Условная компиляция

Позволяет компилировать не все части программы. Директивы условной компиляции исползуются в больших программах.

#if константное_выражение

[фрагмент текста]

[#elif константное_выражение

фрагмент текста]

...

[#else фрагмент текста]

#endif

Результатом вычисления константного_выражения является целое число. Если оно не 0, то выполняется фрагмент текста программы от директивы #if до одной из директив #else, #elif или #endif.

1. #ifdef CI

#include <stdio. h>

#define MAX 80

#else

#include <iostream. h>

#define MAX 132

#endif

2. #ifndef – если макроопределение не определено

#ifndef MY_FILE // файл будет компилироваться только один раз

#define MY_FILE //когда макрос не определен

#include “my_fyle”

#endif

3. #if SYS == ”IBM”

//похоже на оператор, за ним следует константное выражение

//которое считается истинным, если оно не равно 0

#endif

4. Можно исключить блок программы

#ifdef любое имя

*****

#endif

#if defined(__LARGE__)||defined(__HUGE__)

typedef INT long

#else

typedef INT int

#endif

13.  УКАЗАТЕЛИ

Память состоит из байтов, каждый из которых пронумерован, начиная с 0, 1, 2 ... Номер – это адрес. В Си есть переменные, которые могут содержать этот адрес – указатели и операция взятия адреса - &.

int var=1; - определение и инициализация переменной. var – её имя.

printf ("%d %d\n",var, &var); 1 12136

Машинный код команды можно выразить словами. "Выделить 2 байта памяти, присвоить им имя var. Поместить в ячейку с адресом 12136 число". Фактический адрес этой переменной 12136, а его символическое представление &var.

Значением переменной типа указатель служит адрес некоторой величины. Дадим имя этой переменной ptr; тогда можно написать ptr=&var;

В этом случае говорим "ptr указывает на var", где ptr-переменная, &var-константа.

ptr=&num; - теперь указывает на num.

Операция косвенной адресации *

Для доступа к переменной, адрес которой помещен в ptr, используется операция косвенной адресации.

val=*ptr; //val==num

*ptr = 10; //num==10

Описание указателей

Мы уже знаем как описываются переменные, массивы. Как же описать указатель! Сложность в том, что переменные разных типов содержат разное число ячеек, но операции с указателями требуют знания отведенной им памяти. Поэтому, при определении указателя, мы описываем, на какой тип переменной она будет указывать, и что это указатель символ *.

int* ptr;

float* pmas;

char* pc;

Использование указателей для связи функций

В функцию можно передавать не только значения переменных, но и их адреса. В этом случае в вызываемой функции можно изменять значение локальных переменных, определенных в вызывающей функции.

void swap(int*,int*);

void main(void){

int x=5, y=10;

printf ("Прервичные значения х=%d, y=%d\n",x, y);

swap (&x, &y);

printf ("Новые значения х=%d, y=%d\n", x, y);

}

void swap (int*v, int*z){

int u;

u=*v;

*v=*z; //x=y

*z=u;

}

Мы передали в функцию адреса переменных, поэтому при выходе х, у имееют новые значения. При передаче значений этого не происходило.

Вызов swap (x, y); swap (&x, &y);

Определение функции swap (int v, int z); swap (int*v, int*z);

Переменные х, y являются локальными в функции main, но мы, таким образом, можем на них воздействовать в другой функции.

Указатели на одномерные массивы

Указатели позволяют эффективно работать с массивами. Имя массива представляет собой скрытую форму указателя.

Mas -> &mas[0]; - определяется адрес 1-го элемента массива. Оба выражения являются константными выражениями и не меняются на протяжении работы программы. Их можно присваивать переменной типа указатель.

void main(void){

int dates [4], *pti, i;

float bills [4], *ptf;

pti=dates;

ptf=bills;

for (i=0; i<4; i++)

printf ("указатель +%d: %10u и %10u\n", i, pti+i; ptf+i);

}

Указатель +0: 56014 56026 - начало адреса массивов.

+1: 56016 56030

+2: 56018 56034

+3: 56020 56038

Прибавляя 1 к указателю, переходим к следующему элементу массива, а не к следующему байту, т. е. смещаемся на длину типа элемента массива.

рt pt+1 pt+2 pt+3

dates+2 <=> &dates[2]

*(dates+2) <=> dates[2]

*dates+2 <=> *(dates)+2

//Суммирование элементов массива с использованием указателя.

for(i=0;i<10; i++)

sum+=*(ptm+i);

или

for(i=0;i<10; i++){

sum+=*ptm;

ptm++;

}

или

for(i=0;i<10; i++)

sum+=*ptm++;

Указатели на многомерные массивы

Указателю можно присвоить адрес на массив

int mas[2][2], *pti;

pti=mas[0]; //pti=&mas[0][0]

pti+1<=>&mas[0][1]; //так как элементы массива расположены в

pti+2<=>&mas[1][0]; //памяти последовательно и сначала

//меняется второй индекс.

Для двумерного массива:

mas[0]<=> &mas[0][0];

mas[1] <=>&mas[1][0]; //Это важное свойство, потому что можно

//работать с 2-м массивом как с одномерным.

Операции над указателями

1. Присвоить ему значение адреса

int*px, x=2; px=&x;

2. Можно присвоить константу - адрес ячейки с описанием состояния аппаратных средств - абсолютный адрес.

3. После присвоения адреса можно применять операцию взятия косвенного адреса.

px=&x; y=*px; //y==x; Приоритет выше, чем y операции «присвоение»

*px=10; x=10;

4. *px+2 //к значению переменной, адрес которой в px, прибавить 2.

рх++; // рх+1 увеличение адреса на длину типа.

++рх;

4.  сравнение указателей ==, !=, >=, <=, >, <

//Пример программы копирования двух массивов

void main(void) {

char ar1[100], ar2[100];

char *pa1, *pa2;

pa1=&ar1;

pa2=&ar2;

while(pa2<(ar2+sizeof(ar2)))

*pa1++=*pa2++;

}

Внутренние арифметические операции с указателями зависят от действующей модели памяти и наличия переопределяющих модификаторов указателя. Разность между двумя значениями указателей имеет смысл только в том случае, если оба они указывают на один массив.

Арифметические операции с указателями ограничены сложением, вычитанием и сравнением. Арифметические операции с указателями объектов типа "указатель на тип type" автоматически учитывают размер этого типа, то есть число байт, необходимое для хранения в памяти объекта данного типа.

При выполнении арифметических операций с указателями предполагается, что указатель указывает на массив объектов. Таким образом, если указатель объявлен как указатель на type, то прибавление к нему целочисленного значения перемещает указатель на соответствующее количество объектов type. Если type имеет размер 10 байтов, то прибавление целого числа 5 к указателю этого типа перемещает указатель в памяти на 50 байт. Разность представляет собой число элементов массива, разделяющих два значения указателей. Например, если ptr1 указывает на третий элемент массива, а ptr2 на десятый, то результатом выполнения вычитания ptr2 - ptr1 будет 7.

Когда с "указателем на тип type" выполняется операция сложения или вычитания целого числа, то результат также будет "указателем на тип type". Если type не является массивом, то операнд указателя будет рассматриваться как указатель на первый элемент "массива типа type" длиной sizeof(type).

Конечно, такого элемента, как "указатель на следующий за последним элементом" не существут, однако указатель может принимать это значение. Если P указывает на последний элемент массива, то значение P+1 допустимо, но неопределено. Использование указателя на элемент вне массива ведет к непредсказуемым результатам работы программы. Контроль за допустимыми значениями указателей возлагается на программиста.

Передача массива в качестве параметра в функцию

Указатель можно передавать в функцию в качестве параметра:

void main(void){

int age[10];

............

sum (age);

..........

Из за большого объема этот материал размещен на нескольких страницах:
1 2 3 4 5 6 7 8 9 10 11 12