Переименование типов typedef

Язык Си позволяет дать новое название уже существующим типам данных. Для этого используется ключевое словоtypedefи при этом новый тип не создается.

typedef < имя ранее определенного типа >< имя нового типа1>

[,<имя нового типа2>…];

Новое имя становится синонимом имен ранее определенным.

Например:

typedef float real; Теперь вместе fboat можно использовать real.

typedef char symbol;

Часто используется для переопределения структур.

Typedef struct st

{

char name [30];

char group [4];

int god;

} STUDENT;

Теперь для определения переменной можно записать st AN;

или STUDENT AN;

Область действия зависит от расположения оператора typedef. Если определение находится внутри функции, то область действия локальна и ограничена этой функцией. Если вне, то – глобальна.

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

Пример1.

typedef char arr [40]; // FIO- массив символов

arr FIO, *adres; // adres- указатель на массив символов

Это эквивалентно char FIO[40], *adres;

Пример2.

typedef int* Pi; //объявлен новый тип Pi-указатель на целое.

typedef void (*pfn) ( ); //объявлен новый тип pfn-указатель на функцию, не-

возвращающую значения, с любым списком типов аргументов.

typedef void (*pfnI) (int); // объявление типа pfnI-указатель на функцию с

одним аргументом типа int, невозвращающую значения

typedef void (*pptn[10]) (); //объявление типа pptn- массив из 10 указателей на

функцию, не возвращающую значения, с любым списком аргументов.

Объявление typedef применяется

1) для создания удобных распознаваемых имен часто встречающихся;

2) для вложенных типов;

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

Файлы

Прежде чем читать информацию из файла или записывать в него, нужно его открыть. В библиотеке <stdio.h>для этого имеется специальная функция

FILE *fopen(char *fname, char *mode);

где *fname– имя файла, *mode– режим (табл.3).Функция возвращает указа-тель (ссылку) на файл, который должен быть предварительно описан.

Например, FILE *fu; *fu–указатель на файловый тип

Пример. Объявим указатели на переменные файлового типа

FILE *uin, *uout; // (указатели на переменные файлового типа)

uin = fopen(“name1”, “r”); //открыть файл “name1” для чтения и далее

идентифицировать как uin

uout = fopen(”name2”, “w”); //открывается для записи и связывается с

идентификатором uout.

Если производиться открытие несуществующего файла, то он создается.

Для открытия файла с именем test рекомендуется метод, который позволяет определить ошибку при открытии файла.

# include <stdio.h>// работа с файлами и константа NULL (FILEOPEN.C)

# include <stdlib.h>//для ф-ии exit()

Void main()

{

FILE *fp;

if ((fp=fopen("test","w"))==NULL)

{

puts("Не могу открыть файл\n"); // печать строки

exit(1);

}

puts("Файл открыт\n");

}

Таблица 3

Таблица режимов для открываемого файла

Режим Действие
“r” Открыть для чтения
“w” Создать для записи
“a” Открыть для добавленияв существующий файл
“rb” Открыть двоичный файл для чтения
“wb” Создать двоичный файл для записи
“ab” Открыть двоичный файл для добавления
“r+” Открыть для чтения и записи
“w+” Создатьдля чтения и записи
“a+” Открыть для добавления или создать для чтения и записи
“r+b” Открыть двоичный файл для чтения и записи
“w+b” Создать двоичный файл для чтения и записи
“a+b” Открыть двоичный файл для добавления или создать для чтения и записи
“rt” Открыть текстовыйфайл для чтения
“wt” Создать текстовый файл для записи
“at” Открыть текстовый файл для добавления
“r+t” Открыть открыть текстовый файл для чтения и записи
“w+t” Создать текстовый файл для чтения и записи
“a+t” Открыть текстовый файл для добавления или создать для чтения и записи

В библиотеке <stdio.h> определены также следующие функции работы с файлами ( fp- указатель на файл, возвращаемый функцией):

fopen( ) - открытие файла;

int fclose(FILE *fp) - закрытиефайла. Возвращает нуль, если операция выполнена успешно и иное значение в противном случае. Функция является рекомендуемой, поскольку файлы при нормальном завершении закрываются автоматически;

int puts(int ch, FILE *fp) -записать символа типа intв поток. Возвращается записанный символ, если операция была успешной. Если произошла ошибка возвращается EOF;

int gets(FILE *fp) - прочесть символ типа intиз потока. Возвращает EOF, если достигнут конец файла, или произошла ошибка при чтении файла;

int feof(FILE *fp)- возвращает значение «истинно», если достигнут конец файла и нуль в противном случае;

Пример. while (!feof(fp)) {ch=getc(fp);}

Выполняется ввод символа, пока не достигнут конец файла;

int ferror(FILE *fp) - возвращает значение нуль, если обнаружена ошибка. Рекомендуется для обнаружения ошибок чтения и записи после каждой операции с файлами;

int fprintf(FILE *fp, const, char *string, …)- форматированная запись в файл. Содержит указатель на файл и управляющую строку со списком;

int fscanf(ILE *fp, const,char *string) –форматированное чтение из файла;

unsign fread(void *buf, int bytes, int c, FILE *fp) - читает блок данных из потока (буферный обмен); buf - указатель на область памяти, откуда проис-ходит обмен информации. с- количество единиц записи длиной bytes, которая будит считываться.

unsign fwrite(void *buf, int bytes, int c, FILE *fp) - пишется блок данных в поток;

int remove(char *filename)- уничтожается файл, возвращается нуль при успешной операции;

unsign rewind( FILE *fp)- устанавливается указатель на начало файла;

int fseck( FILE *fp, long numbyte, int orig) - установить указатель позиции файла в заданное место, numbyte - количество байт от точки отсчета (0,1,2), orig – макрос 0 - начало, 1- текущая позоция, 2 - конец;

void abort() - <stdlib.h> - немедленное прекращение программы без закрытия файлов и без освобождения буферов.

2.10 Операторы динамического распределения памяти в С++

В С++ введены 2 «интеллектуальных» оператора new и delete, освобождающие про-граммиста от необходимости явно использовать библиотечные функции malloс, calloс и free.

Оператор new выделяет блок памяти, необходимый для размещения переменной или массива (необходимо указывать тип и, в случае массива, размерность), и при этом можно присваивать вновь созданной переменной начальное значение.

New type_name [(инициатор)]; илиNew (type_name [(инициатор)]);

Возвращает указатель на объект а= new int[n] для неизвестного числа элементов. Оператор delete освобождает ранее выделенную память. Размер занятого блока для правильной работы delete, записывается в его начало и обычно занимает дополнительно 4 байта.

Примечание: Следует помнить, что реальный размер занятого блока не произволен, а кратен определенному числу байт (в С++3.0-16), поэтому, с точки зрения расхода памяти невыгодно резервировать много блоков под небольшие объекты.

В случае успешного выполнение new возвращает адрес начала занятого блока памяти, увеличенный на количество байт, занимаемых информацией о размере блока (т.е. возвращает адрес созданной переменной или адрес нулевого элемента созданного массива). Когда new не может выделить требуемую память, он возвращает значение (void *), в этом случае рекомендуется предусмотреть в программе реакцию. Например:

# include <stdio.h> //

# include <iostream.h> // для в\в в С++

intmain()

{

int *u_i;

double *u_d;

char *string;

int str_len=80;

u_i=new int; //Зарезервировать место под переменную типа int и

//присвоить u_i ее адрес (Значение *u_i не определено)

u_d=new double(3.1415);// -"- и *u_d инициализируется знач. 3.1415

string=new char[str_len];

if (!(u_i && u_d && string))

{

cout<<"Не хватает памяти для всех динамически"

"размещаемых переменных!";

return 1;

}

string[0]='Y';

string[1]='e';

string[2]='s';

string[3]='!'; string[4]='\0';

cout<<"\n u_i="<<u_i<<" случайное значение *u_i="<<*u_i ;

delete u_i; //Освободить блок памяти, на кот указывает u_i

cout<<"\n u_d="<<u_d<<" *u_d="<< *u_d ;

delete u_d; //Освободить блок памяти, на кот указывает u_D

printf("\n string=%p ", string);

cout<<" string contents="<<string<<"\n";

delete string; // Можно и delete [str_len] string;

return 0; // но выдается предупр: размер массива игнорируется

}

Если указатель, на который действует оператор delete, не содержит адреса блока, зарегистрированного ранее оператором new, или же не равен NULL, то последствия будут непредсказуемыми.

Помимо проверки возвращаемого значения, в С++ для обработки ситуации нехватки памяти, программист может определить специальную функцию обработки, которая будет вызываться при неудачном выполнение оператора new и «пытаться» изыскать необходимую память, либо выдать сообщение и завершить выполнение программы библиотечной функцией exit или abort.

Директивы препроцессора

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

Можно избирательно компилировать части файла в зависимости от значения некоторого константного выражения или идентификатора.

Для этого служат директивы #if, #elif, #else, #endif.

Синтаксис условной конструкции:

#if <выражение1>

последовательность операторов //компилировать, если <выражение1> истинно.

#elif <выражение2>

операторы//компилировать, если <выражение1> ложно, а <выражение2> истинно

#elif <выражение3>

последовательность операторов // компилировать, если <выражения1 и 2>

ложны, а <выражение3> истинно

#else

операторы // компилировать, если все выражения ложны.

#endif // конец для условной компиляции if.

Правило выполнения условных директив.

1. Для каждого #if должна быть соотвествующая #endif.

2. Директивы #elif и #else являются опциональными (исключают друг друга).

3. Число директив #elif между #if и #endif не ограничено. Директива #else должна быть одна и находиться перед #endif.

4. Аналогично оператору if ...else компилируется та секция, которая соответс-твует первому истинному выражению.

5. Если ни одно из выражений не истинно, то компилируется секция, следую-щая за частью #else.

6. Значение выражения должно быть целой константой; в выражении нельзя использовать операцию sizeof и преобразование типов.

Оператор defined или знак операции препроцессора

Он может применяться в директивах #if и #elif и позволяет проверить, был ли определен идентификатор или макрос.

void ShowMessage(char *msg)

{

# if defined(DOS_target)

puts(msg);

# else MessageBox(NULL,msg,''MSG'',MB_OK);

# endif

}

Если макрос DOS_target определен, то функция ShowMessage использует для вывода сообщения функцию puts, в противном случае компилируется функция MessageBox для специального вывода.

Можно применять логическую операцию ! для проверки того, что иденти-фикатор не определен. Функцию void ShowMessage(DOS_target) можно перепи-сать таким образом

{ # if !defined(DOS_target)

MessageBox(NULL,msg,''MSG'',MB_OK);

# else

puts(msg);

# endif

}

Директивы #ifdef и #ifndef

Эти директивы эквивалентны соответственно #if defined и #if !defined и позволяют проверить, определен идентификатор в данный момент или нет. Однако применение оператора defined является более предпочтительным, т.к. он позволяет проверить сразу несколько макросов в сложных логических выражениях.

# ifdef DOS_target можно упростить до # if defined (DOS_target)

# ifndef NDEBUG &&!defined(NDEBUG)

puts(msg); puts(msg);

# endif # endif

# endif

Данные директивы требуют наличия соответствующей директивы #endif, между ними может (но не обязательно) размещаться директива #else или #elif.

Директива #error

Позволяет выдавать во время компиляции сообщение об ошибке.

Ее структура: #error <сообщение об ошибке>

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

Директива #line

Позволяет изменить внутренний счетчик строк компилятора и имеет

Структуру: #line номер строки["имя файла"]

Номер строки должен быть целой константой. В строке может присутство-вать опциональное имя файла. Эта директива изменяет предопределенный макрос _ _LINE_ _. Если присутствует имя файла, то меняется макрос

_ _FILE_ _. Директива используется, чтобы в процессе трансляции заменить наименование текущего файла исходного текста программы и (или) номер строки этого текста.

Предопределенные макросы

Компилятор автоматически определяет некоторые макросы ANSI.(ANSI – официальное название стандарта языка Си, препроцессора и библиотеки поддержки выполнения программы, принятое Американским Национальным институтом Стандартов)

__DATE__ - строка, представляющая дату, когда данный файл обрабатывался препроцессором (форма даты mm dd yyyy).

__FILE__- строка, представляющая имя текущего файла в двойных кавычках.

__LINE__- целое, представляющее текущий номер строки.

__STDC__- значение является константой, равной 1, если установлено со-ответствие со стандартом ANSI, в противном случае макрос не определен.

__TIME__- строка, представляющая время в форме hh:mm:ss, когда данный файл обрабатывался препроцессором.

Директива #pragma

Позволяет управлять специфическими возможностями компилятора.

Ее синтаксис: #pragma <директива>

Если реализация системы программирования, обнаружив pragma, ее не узнает, то система ее игнорирует. (Стандартных прагм не существует.)

Директивы #pragma, поддерживаемые компилятором C++:

аrgsusd - подавляет предупреждающее сообщение о том, что параметр xхх не ис- пользован для функции, следующей за директивой;

exit - позволяет указать функцию, которая должна быть вызвана перед завер-шением программы;

еxtref - заставляет компилятор включить ссылку на неиспользованную внеш-нюю переменную или функцию;

hdrfile специфицирует имя заранее откомпилированного файла-заголовка;

hdrignore - т.к. макросы и типы, определяемые в заголовочном файле, могут изменяться, когда определяется другой макрос, компилятор не использует информацию из перекомпилированного заголовка, если встречает директиву условной компиляции. Данная директива указывает, что заголовок должен использоваться, если встречается в директиве условной компиляции;

hdrstop - предписывает компилятору не включать дальнейшую информацию, перекомпилированную в заголовочный файл;

inline - указывает, что компиляция текущего модуля должна производиться через ассемблер;

intrinsic - эта директива может быть использована, чтобы разрешить или запре-тить генерацию inline-кода для встроенной функции; (встроенная функция - это библиотечная процедура, для которой компилятор генерирует inline-код вместо вызова библиотеки);

obsolete - приводит к тому, что компилятор генерирует сообщение-предупреж-дение о том, что имя файла является устаревшей функцией. Ее можно исполь-зовать, чтобы сообщить другим программистам, что улучен код и предусмотрена для данной задачи новая функция;

option – позволяет включить в код опции командной строки компилятора;

startup – является дополнительной к #pragma exit, позволяет указать функцию, которая должна выполняться до функции main;

warn – позволяет выборочно разрешать или подавлять предупреждающее сообще-ние. Если предупреждению предшествует знак «+», то выдача сообщения разре- шается, если знак «-» , то запрещается.

Некоторые компиляторы, в частности ANSI, узнают директиву pragma, которая указывает на то, как плотно упакованы смежные члены в структуру, например: #pragma pack (n) , где n может быть 1,2 или 4, указывая, что имеет место выравнивание на границу байта, слова или двойного слова.

В языке С/С++ имеются также директивы подключения библиотек # include и макроподстановок #define [7, 8].


Наши рекомендации