1.36.2. Защита от повторного включения заголовочного файла
В программе может возникнуть ситуация, когда при компиляции файла реализации заголовочный файл другого модуля оказывается включенным несколько раз. Такое повторное включение может привести к сообщениям об ошибках времени компиляции. Это может иметь место при наличии во включенном заголовочном файле определений типов. Такая ситуация характерна для работы со структурами (смотри раздел 1.38). Для защиты от повторного включения обычно применяются две директивы препроцессора: #ifndef … endif и #define. Для примера организуем такую защиту в заголовочном файле модуля my_math, который рассматривался в 1.35.1.
// Файл my_math.h с защитой от повторного включения #ifndef _MY_MATH_H #define _MY_MATH_H double sqr(double x); // … #endif;
Управление включением заголовочного файла my_math.h осуществляется с помощью своего рода флажка, имя которого _MY_MATH_H. Во время первого включения значение флажка не определено. Директива ifndef включает заголовочный файл, только в том случае, когда значение флажка не определено. После первого включения заголовочного файла директива #define назначает флажку некоторое значение. После этого повторное включение оказывается невозможным. Директива #endif ограничивает область действия #ifndef.
1.37. Массивы и указатели
Между массивами и указателями существует сильная связь. Начнем изложение с обсуждения общих сведений, относящихся к массивам.
1.37.1. Массивы. Общие сведения
Массивом называется упорядоченная совокупность однотипных величин, объединенных одним именем. Упорядоченность хранящихся в массиве данных достигается путем использования системы индексов. Количество индексов определяет размерность массива. Для хранения элементов массива выделяется непрерывная область памяти.
Различают одномерные массивы и многомерные массивы. При работе с элементами одномерных массивов используется один индекс, а при работе с многомерными массивами – два и более индексов. Минимальное значение индекса в языке Си принято равным нулю.
Назначение массивов состоит в хранении некоторой совокупности данных для их последующего использования.
1.37.1.1. Классификация массивов
В языке Си имеются три разновидности массивов:
• Массивы с фиксированными размерами (МФР).
• Массивы с переменными размерами (МПР).
Память для массивов с фиксированными и переменными размерами выделяется с помощью их определения. Выделение и освобождение памяти для динамических массивов выполняется с использованием библиотечных функций.
1.37.1.2. Определение и инициализация массива
Определение массива позволяет компилятору создать массив. В языке Си определение массива можно написать только для МФР и МПР массивов. Динамические массивы создаются и уничтожаются с применением стандартных функций (malloc и др.). Память для массивов МФР можно выделить как в статической области памяти, так и в стеке. При этом память для всех массивов МФР, определения которых записываются вне функций, выделяется в статической области оперативной памяти компьютера. Память выделяется во время начала работы программы и освобождается во время завершения работы программы. Память для массивов МФР, определения которых записываются внутри функций, выделяется в стеке. Выделение памяти происходит в момент времени, когда управление достигает точки определения, а освобождается в момент времени, когда управление выходит из блока, в котором находится определение массива. Память для массивов МПР может быть выделена только в стеке. Рассмотрим этот вопрос применительно к одномерным и двумерным массивам.
Формат определения одномерного массива имеет следующий вид:
type name[expr];
Здесь type – тип элементов массива, name – имя массива, expr – выражение, определяющее количество элементов в массиве. Для МФР массивов expr должно быть константным выражением, а для МПР массивов должно быть неконстантным.
Формат определения двумерного массива имеет следующий вид:
type name[expr_row, expr_col];
Здесь type – тип элементов массива, name – имя массива, expr_row – выражение, определяющее количество строк, а expr_col – выражение, определяющее количество столбцов в массиве. Заметим, что для МФР массивов все выражения, стоящие в квадратных скобках, должны быть константными выражениями, для МПР массивов эти выражения должны быть неконстантными.
Приведем примеры определений массивов.
#define MSIZE 20 #define MROW 5 #define MCOL 4 int main(void) < int x[MSIZE]; double mat[MROW][MCOL]; int n; int z[n]; int mr, mc; double mt[mr][mc]; //. >
В приведенном выше программном коде объявлены два одномерных (x и z) и два двумерных массива (mat и mt). В одномерных массивах должны храниться элементы типа int, а в двумерных массивах – элементы типа double. Массивы x и mat являются массивами с фиксированными размерами. Размеры этих массивов задаются символическими константами. Размеры двух других массивов (z и mt) задаются с помощью переменных; n для массива z и двумя переменными mr и mc – для массива mt.
Отметим очень важное отличие между массивами МФР и МПР, которое состоит в том, что массивы МФР могут быть инициализированы во время определения, а массивы МПР не могут. Для выполнения инициализации необходимо в конце определения массива записать математический знак равенства ( = ), затем в фигурных скобках указать список инициализаторов. В простейшем случае количество инициализаторов должно совпадать с количеством элементов в массиве. Если инциализаторов оказалось меньше количества элементов массива, то компьютер дополняет этот список справа нулями. Ситуация, когда инициализаторов оказалось больше, чем это требуется, рассматривается как ошибка. Компилятор в этом случае выдает сообщение об ошибке. Приведем примеры инициализации массивов.
int x[4] = ;// x[0] == 1, x[1] == 3,x[2]== 5, // x[3] == 7 int y[4] = ; // y[0] == 2, y[1] == 4, y[2] == 0, y[3]== 0 int z[4] = ; // Все элементы массива – нулевые int w[4] = // Сообщение об ошибке int v[] = ;// v[0] == 1, v[1] == 2, v[2] == 3 int v[2][3] = < , // Первая строка // Вторая строка >;
Защита от повторного включения заголовка
В проекте на C, код которого я недавно стал смотреть, используется защита от повторного включения заголовка:
#ifndef H_HEADER #define H_HEADER // Header itself #endif
Для унаследованного кода (где все и так работает), это нормально. Но для новых исходников хотелось бы что-нибудь проще. Есть ли аналог/заменитель в C?
Отслеживать
задан 14 дек 2010 в 14:15
34.5k 26 26 золотых знаков 98 98 серебряных знаков 214 214 бронзовых знаков
1 ответ 1
Сортировка: Сброс на вариант по умолчанию
В C есть специальная директива
#pragma once
которая указывает компилятору, что заголовок должен быть включен только один раз.
Отслеживать
ответ дан 17 дек 2010 в 10:46
34.5k 26 26 золотых знаков 98 98 серебряных знаков 214 214 бронзовых знаков
Насколько мне известно, #pragma once — это специфика Microsoft VC++, следовательно в Unix мире не может быть использована.
17 дек 2010 в 10:55
#pragma once не входит в стандарт C и не все компиляторы ее поддерживают; полностью портируемый код должен избегать ее. Тем не менее, GCC поддерживает эту директиву, поэтому и на Unix она доступна.
Как защитить хедер от повторного включения
Объясните достоинства обычной внутренней защиты #include
#if !defined(HEADER123_HPP) #define HEADER123_HPP //некий код #endif
Чем она лучше такой:
#if defined(HEADER123_HPP) #error wrong dependence detected #endif #define HEADER123_HPP //некий код
Re: защита от повторного include
| От: | jazzer | Skype: enerjazzer |
| Дата: | 26.02.06 18:42 | |
| Оценка: |
Здравствуйте, Dmi_3, Вы писали:
D_>Объясните достоинства обычной внутренней защиты #include
D_>
D_>#if !defined(HEADER123_HPP) D_>#define HEADER123_HPP D_>//некий код D_>#endif D_>
D_>Чем она лучше такой:
D_>
D_>#if defined(HEADER123_HPP) D_>#error wrong dependence detected D_>#endif D_>#define HEADER123_HPP D_>//некий код D_>
Это защита не от того, о чем ты подумал.
Представь, что ты включил хедер a.h в b.h и в c.h, а потом включаешь b.h и c.h в d.h.
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09
You will always get what you always got If you always do what you always did
Re: защита от повторного include
| От: | zitz |
| Дата: | 26.02.06 20:03 |
| Оценка: |
Здравствуйте, Dmi_3, Вы писали:
D_>Объясните достоинства обычной внутренней защиты #include
D_>
D_>#if !defined(HEADER123_HPP) D_>#define HEADER123_HPP D_>//некий код D_>#endif D_>
Читай по порядку: если КодЗадефайнен (т.е. мы его уже вставляли), то его пропустить, иначе вставить код и задефайнить.
D_>Чем она лучше такой:
D_>
D_>#if defined(HEADER123_HPP) D_>#error wrong dependence detected D_>#endif D_>#define HEADER123_HPP D_>//некий код D_>
Читай по порядку: если КодЗадезайнен вывести еггог и дальше вставить некий код, иначе задефайнить и вставить код.
Помоему разница налицо! В первом случае повторная вставка кода будет игнорироваться, а во втором высыпать куча ошибок.
Хотя если нужно чтобы так и было то второй вариант лучше, т.е. как написано в топике «защита от повторного include», хотя с какой целью писать хеадер чтобы его нельзя было использовать в нескольких частях проги непонятно.
Re[2]: защита от повторного include
| От: | jazzer | Skype: enerjazzer |
| Дата: | 26.02.06 20:21 | |
| Оценка: |
Здравствуйте, zitz, Вы писали:
D_>>
D_>>#if defined(HEADER123_HPP) D_>>#error wrong dependence detected D_>>#endif D_>>#define HEADER123_HPP D_>>//некий код D_>>
Z>Читай по порядку: если КодЗадезайнен вывести еггог и дальше вставить некий код, иначе задефайнить и вставить код.
Z>Помоему разница налицо! В первом случае повторная вставка кода будет игнорироваться, а во втором высыпать куча ошибок.
Не так.
#error выдает ошибку и останавливает компиляцию.
Так что кучи ошибок не будет, будет всего одна (если у тебя, конечно, не параллельная сборка 🙂 ).
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09
You will always get what you always got If you always do what you always did
Re: защита от повторного include
| От: | Андрей Тарасевич |
| Дата: | 26.02.06 20:52 |
| Оценка: |
Здравствуйте, Dmi_3, Вы писали:
D_>Объясните достоинства обычной внутренней защиты #include
D_>
D_>#if !defined(HEADER123_HPP) D_>#define HEADER123_HPP D_>//некий код D_>#endif D_>
D_>Чем она лучше такой:
D_>
D_>#if defined(HEADER123_HPP) D_>#error wrong dependence detected D_>#endif D_>#define HEADER123_HPP D_>//некий код D_>
Повторное включение заголовочного фала в одну и ту же единицу трансляции не является чем-то ненормальным. Поэтому ругаться на это через ‘#error’ — это непонятная и странная идея.
Под «защитой от посторного включения» имеется я виду не запрещение физического повторного включения, а избежание повторной компиляции содержимого заголовочного фала в ситуации, когда он оказался включенным повторно в одну и ту же единицу трансляции.
Best regards,
Андрей Тарасевич
Re[2]: защита от повторного include
| От: | Andrej Kalinicenko |
| Дата: | 27.02.06 10:14 |
| Оценка: |
» Андрей Тарасевич » <2174@users.rsdn.ru>schrieb im Newsbeitrag news:1700156@news.rsdn.ru.
> D_>2174@users.rsdn.ru>
> D_>#if defined(HEADER123_HPP) > D_>#error wrong dependence detected > D_>#endif > D_>#define HEADER123_HPP > D_>//некий код > D_>
>
> Повторное включение заголовочного фала в одну и ту же единицу трансляции не является чем-то ненормальным. Поэтому ругаться на это через ‘#error’ — это непонятная и странная идея.
>
Видел чудака который в таком заголовчном файле глобальные переменные описывал
Re: Re: задолбали разработчики, ниче не пишут че надо. сам вот взялся
[root@mobile100 cex]# c++ main.c f.c -o silly f.c:2:15: warning: extra tokens at end of #ifndef directive f.c:3:15: warning: ISO C requires whitespace after the macro name In file included from /usr/include/c++/3.2.2/backward/iostream.h:31, from f.c:4: /usr/include/c++/3.2.2/backward/backward_warning.h:32:2: warning: #warning This file includes at least one deprecated or antiquated header. Please consider using one of the 32 headers found in section 17.4.1.2 of the C++ standard. Examples include substituting the header for the header for C++ includes, or instead of the deprecated header . To disable this warning use -Wno-deprecated. [root@mobile100 cex]#
Может быть оно дает варнинг на то, что если я определю какюннить функцию, а она вдруг гдето зарезервированая в другой системной библиотеке?
anonymous
( 17.11.03 20:18:43 MSK )

Re: Re: задолбали разработчики, ниче не пишут че надо. сам вот взялся
// f.c #ifndef header.h /*HEADER - это название твоего хидера*/ #define header.h #include #include "header.h" #endif void f () < cout [root@mobile100 cex]# c++ main.c f.c -o silly f.c:2:15: warning: extra tokens at end of #ifndef directive f.c:3:15: warning: ISO C requires whitespace after the macro name In file included from /usr/include/c++/3.2.2/backward/iostream.h:31, from f.c:4: /usr/include/c++/3.2.2/backward/backward_warning.h:32:2: warning: #warning This file includes at least one deprecated or antiquated header. Please consider using one of the 32 headers found in section 17.4.1.2 of the C++ standard. Examples include substituting the header for the header for C++ includes, or instead of the deprecated header . To disable this warning use -Wno-deprecated. [root@mobile100 cex]# вот так вот!! :( Может быть оно дает варнинг на то, что если я определю какюннить функцию, а она вдруг гдето зарезервированая в другой системной библиотеке? чето ниче не запостить. !!
vilfred ☆☆
( 17.11.03 20:19:51 MSK )

Re: Re: задолбали разработчики, ниче не пишут че надо. сам вот взялся
по этому поводу: // f.c #include #include "header.h" #endif void f () < cout дает ошибку [root@mobile100 cex]# c++ main.c f.c -o silly f.c:2:18: stream: No such file or directory f.c: In function `void f()': f.c:5: `cout' undeclared (first use this function) f.c:5: (Each undeclared identifier is reported only once for each function it appears in.) [root@mobile100 cex]# на это // f.c #include #include "header.h" void f () < cout дает ошибку File f.c not changed so no update needed. [root@mobile100 cex]# c++ main.c f.c -o silly f.c: In function `void f()': f.c:5: `cout' undeclared (first use this function) f.c:5: (Each undeclared identifier is reported only once for each function it appears in.) [root@mobile100 cex]# Че делать? Короче, в принципе оно конешно работает, но как то после #!/usr/bin/perl -w или use strict; use warnings непонятно, откуда это все, че ей не нравится. Может команда cout устарела просто со времен страустрапа? Причем, если я кимпилю gcc, выскакивает ошибка что нет либы. Если я компилю cc выскакивает что нет либы stream или iostream не помогало даже принудительно прописывание #include д
vilfred ☆☆
( 17.11.03 20:25:44 MSK )
Re: Re: Re: задолбали разработчики, ниче не пишут че надо. сам вот взялся
#include . std::cout
abbr ★
( 17.11.03 20:37:08 MSK )
Re: Re: Re: Re: задолбали разработчики, ниче не пишут че надо. сам вот взялся
vilfred☆☆
( 17.11.03 20:51:44 MSK )Не того Страустроупа читал, ищи 3-ю редакцию
Читай не просто С++, а стандарт ISO С++. Там все стандартные заголовочные файлы заменены на такие же, но без раширений. И все помещено в namespace std.
anonymous
( 24.11.03 15:19:42 MSK )
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.Похожие темы
- Форум ошибка при компеляции (2004)
- Форум warning: #warning This file includes at least. (2005)
- Форум Языками не владею (2004)
- Форум Проблеммы компиляции в C++ (2004)
- Форум Заголовочный файл (2006)
- Форум [ Perl ] Проблема с использованием inline CPP (2008)
- Форум проблема с с++ (2004)
- Форум контейнеры (2005)
- Форум не компилируются 5 строчек кода =( (2005)
- Форум начинаюшему с++ помогите (2006)