Как использовать структуру в нескольких файлах

Обновлено: 06.07.2024

в процессе изменения некоторого кода я разлил некоторые функции в несколько файлов. У меня есть файлы controls.cpp и display.cpp и я хотел бы иметь доступ к одному и тому же набору переменных в обоих файлах. Я не возражаю, где они инициализируются или объявляются, если функции в обоих файлах могут их использовать.

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

определите переменную в одном файле, например:

и объявить его глобальным в другом файле, например:

использовать эти переменные как extern т. е.

в другом файле объявите то же, что и обычная глобальная переменная.

создайте два новых файла:

  1. что-то вроде Globals.h и объявить все переменные как: extern type name;
    • btw помните, включают охранников.
  2. что-то вроде Globals.cpp и объявить переменные, такие как: type name;
  • Globals.cpp
  • controls.cpp
  • display.cpp

затем вам может потребоваться инициализация некоторых функций их.

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

самый простой способ сделать это-использовать макрос в заголовочном файле, такие как:

а затем объявите свои переменные в том же заголовочном файле, например:

из любого Один файл в проекте, включает файл заголовка, например:

из всех остальных, просто включите его нормально, вроде такого:

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

все объявления вы хотите видеть в нескольких единицах компиляции (.cpp files), должен войти в файл заголовка, который вы включаете во всех местах, где необходимо использовать переменную, тип, класс и т. д.

Это намного лучше, чем extern, который по существу скрывает ваше намерение поделиться декларацией.

Формат файла состоит из 3 структур и одной переменной. Подробное описание формата во вложении, просьба посмотреть его. У меня возникла проблема, я не знаю как его читать. Дело в то что начало 5 байт - Идентификатор Файла-карты для программ различных версий. Дальше идет структура - 32 байта, дальше переменная 4 байта, дальше опять идет структура, она может быть одна, а может и 100 штук - размер одной 47 байт, далее идет следующая структура, ее размер 614 байт, она также может быть одна или несколько или вообще не быть, как читать этот бинарный файл не пойму. Подробности структуры и т.д. в файле, который находится в приложении.Вот код и попытки. По не понятной причине не работает(


7,444 1 1 золотой знак 21 21 серебряный знак 45 45 бронзовых знаков "Не работает" бывает разным. Ради интереса посмотрите sizeof() ваших структур и сравните с тем, что должно быть. Еще очень помогает в отладчике смотреть на память, выделенную под стрктуру во время записи в ее поля значений. Размер моих структур соответствует заданному формату, но ранее я не разу этого делал, а на практике задали ЕЩЕ РАЗ: sizeof() ваших структур и сравнивайте его с тем, что должно быть. sizeof() моих структур соответствует формату.Даже вывод есть в коде:cout << "sizeof(zagkm)=" << sizeof(zagkm) << endl; cout << "sizeof(InfoFile)=" << sizeof(InfoFile) << endl;

Изучите выравнивание полей и паковку структур:

Это первое. Второе, для типов фиксированных размеров используйте uint8_t/uint16_t/uint32_t/uint64_t и то же самое для знаковых, но без u в начале.

Третье, при чтении структур zagkm в качестве размера указываешь sizeof(zagolov) , что в корне неверно. Причём, если бы ты глазами проанализировал бы сам файл, ты бы увидел, на в первой записи стоят FF FF FF FF для значения ID , что дало бы для LongInt ( int32_t ) значение -1, а у тебя 0 выводится. Не смутило? Потом уже ползут все данные. Аналогичная хрень у тебя при чтении InfoFile . Что бы от такого не сильно страдать, куда лучше было-бы что-то вроде: sizeof(chunk47[i]) , в случае рефакторинга тоже поможет.

Как только программы становятся больше, их следует разбивать на несколько файлов (в целях удобства и улучшения функциональности). Одним из преимуществ использования IDE является легкость в работе с n-ным количеством файлов. Мы уже знаем, как создавать и компилировать однофайловые проекты, добавление новых файлов не составит труда.

Многофайловые проекты в Visual Studio

В Visual Studio щелкните правой кнопкой мыши по имени вашего проекта в "Обозревателе решений" , затем "Добавить" > "Создать элемент. " :


Во всплывающем диалоговом окне выберите тип файла, укажите его имя, расположение, а затем нажмите "Добавить" :


Также вы можете добавлять файлы к вашему проекту через "Проект" > "Добавить новый элемент. " :



Многофайловые проекты в Code::Blocks

В Code::Blocks перейдите в "File" > "New" > "File. " :


Затем выберите "C/C++ source" и нажмите "Go" :


Затем "Next" (этого окна может и не быть):


Затем "C++" и опять "Next" :


Затем укажите имя нового файла (не забудьте расширение .cpp) и его расположение (нажмите на троеточие и выберите путь). Убедитесь, что поставлены все три галочки (они отвечают за конфигурации сборки). Затем нажмите "Finish" :


Готово! Файл добавлен.

Многофайловые проекты в GCC/G++

В командной строке вам нужно будет создать файл, указать его имя и подключить к компиляции, например:

g++ main.cpp add.cpp -o main

Пример многофайловой программы

Рассмотрим следующую программу, которая состоит из двух файлов.

std :: cout << "The sum of 3 and 4 is: " << add ( 3 , 4 ) << std :: endl ;

Попробуйте запустить эту программу. Она не скомпилируется, вы получите следующую ошибку:

add: идентификатор не найден

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

Тем не менее, в данном случае, мы хотим, чтобы main.cpp знал (и использовал) функцию аdd(), которая находится в add.cpp. Для предоставления доступа main.cpp к функциям add.cpp, нам нужно использовать предварительное объявление:

int add ( int x , int y ) ; // это нужно для того, чтобы main.cpp знал, что функция add() определена в другом месте std :: cout << "The sum of 3 and 4 is: " << add ( 3 , 4 ) << std :: endl ;

Теперь, когда компилятор будет компилировать main.cpp, он будет знать, что такое add(). Попробуйте запустить эту программу еще раз.

Что-то пошло не так!

Пункт №1: Если вы получили ошибку от компилятора, что функция add() не определена в main(), то, скорее всего, вы забыли записать предварительное объявление функции add() в main.cpp.

Пункт №2: Если вы получили следующую ошибку от линкера:

unresolved external symbol "int __cdecl add(int,int)" (?add@@YAHHH@Z) referenced in function _main

то возможных решений есть несколько:

a) Cкорее всего, add.cpp некорректно добавлен в ваш проект. Если вы используете Visual Studio или Code::Blocks, то вы должны увидеть add.cpp в "Обозревателе решений" в списке файлов вашего проекта или в панели проекта IDE. Если добавленного файла нет, то щелкните правой кнопкой мыши по вашему проекту и добавьте файл, как это показано выше, а затем повторите попытку компиляции вашего проекта.

б) Вполне возможно, что вы добавили add.cpp к другому проекту.

в) Вполне возможно, что добавленный файл не подключен к компиляции/линкингу. Щелкните правой кнопкой мыши по имени вашего добавленного файла и выберите "Свойства" :


Убедитесь, что пункт "Исключен из сборки" оставлен пустым или выбрано значение "Нет" :


Пункт №3: Не следует писать следующую строку в main.cpp:

Наличие этой строки приведет к тому, что компилятор вставит всё содержимое add.cpp непосредственно в main.cpp вместо того, чтобы рассматривать эти файлы как отдельные.

Разделите следующую программу на два файла (main.cpp и input.cpp): main.cpp должен содержать функцию main(), а input.cpp — функцию getInteger().

Помните, что для функции getInteger() вам понадобится предварительное объявление в main.cpp.

Ответ

int getInteger ( ) ; // предварительное объявление функции getInteger() (906 оценок, среднее: 4,87 из 5)

Урок №19. Прототип функции и Предварительное объявление

Комментариев: 50

Я так понимаю, что при включении в главный файл заголовочный файл библиотеки, которые были в заголовочном файле копируются, что только прибавляет веса главному файлу или все же копирует лишь функции?

Здравствуйте. А если у меня функция getInteger() будет определена в 2 и более файлах, то как файл с функцией main() поймет какой именно getInteger() мне требуется?

Я методом тыка уже определил что будет ошибка )

Юрий :

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

Юрий :

Смотрите решение в уроке №7.

Спасибо, Юрий! Замечательные уроки! Получаю удовольствие.. Возникают вопросы.. ищу на сайтах.. нахожу.. Будут посерьёзнее, намерен обращаться, если позволите!

Юрий :

Начал изучать С++ для создания игр на Unreal, но там все как то сложно.
Случайно наткнулся на ваши уроки, это же просто чудо какое то)
Спасибо за ваш труд.
Пожалуй накидаю плюсиков в карму автору)

Мне кажется стоило также указать что хорошим тоном является написать функцию в <name>.cpp, сделать её объявление в <name>.h, после чего подключить <name.h> в основном файле.

Вместо того чтобы писать объявления функций напрямую.

Первый файл с функцией int getInteger():

Может кто-нибудь знает как это все осуществить при помощи Xcode, или статью какую-нибудь по обучению использования этой среды.

А что сложного в Xcode? Всё по аналогии с VS, но со своими особенностями (если уж на то пошло). Можно нагуглить на крайний случай.

Юрий :

Здравствуйте я пишу в андроид с помощью Dcoder и хотел пройти тест в конце этого урока но у меня выдаёт ошибку.

Спасибо за уроки, хороший язык оказывается.

Срочно нужна помощь.
Начал программировать на андроид с приложения Dcoder(c++: GCC compiler 6.3 (знаю, что это неудобно и т. д., но лучше так, чем никак).
С этим уроком получилась зиминка из-за моего не состояния найти способ, как связать несколько файлов именно в этом приложении. Если кто-то что с этим делать, тогда пишите (буду очень благодарен). Автору спасибо за уроки. Всё очень доходчиво написано.

Это символ переноса строки, такая управляющая последовательность(и гуглите 🙂 )

Какая же чудесная находка для меня эти уроки) Автору огромное спасибо за проделанную работу за перевод и адаптацию

Юрий : Юрий :

Здравствуйте. Как добавлять файлы в Visual Studio 2017 если он на русском. Просто не очень понятно, переводчик не помогает.

Юрий :

Тогда я ошибся, и это чудесно =) Прошу прощения. Я не заметил ссылки, привык их искать где-то в аннотации.

Юрий :

Ничего, без проблем 🙂

Добрый день! При попытке добавить в функцию, которая находится в отдельном файле, "stdafx.h" компилятор выдаёт ошибку: не удаётся открыть файл включения stdafx.h: No such file or directory. Без stdafx.h всё нормально работает. Visual Studio 2015.

Юрий :

Сегодня читала статью с телефона. Очень удобный сайт)

Юрий :

Когда пишу файл add.cpp постоянно не идёт,это ж линкер мешает по моему. И что ему не так,когда твою программу скопировал,также всё:

Ошибка LNK2019 ссылка на неразрешенный внешний символ _main в функции "int __cdecl invoke_main(void)" (?invoke_main@@YAHXZ) add.cpp 1

Юрий :

Вы сделали всё как в уроке? Правильно добавили файл? К тому проекту, что нужно?

Почему в VS нужно обязательно подключать библиотеку stdafx.h? Раньше, когда я учился по видеоурокам, я писал программы и без ее подключения. Но конечно есть одно но, я тогда немного изменил параметры создания проекта: поставил галочку на "Empty project" и удалил галочку на "Security Development Lifecycle (SDL) checks ". Может быть это как-то повлияло?

Юрий :

Зачем писать названия в VS (например Source Files) на английском, если в VS есть русский язык?

По мере увеличения размера программ весь код уже не помещается в нескольких файлах, записывать каждый раз предварительные объявления для функций, которые мы хотим использовать, но которые находятся в других файлах, становится всё утомительнее и утомительнее. Хорошо было бы, если бы все предварительные объявления находились в одном месте, не так ли?

Заголовочные файлы из Стандартной библиотеки C++

Рассмотрим следующую программу:

Результат выполнения программы:

Как правило, в заголовочных файлах записываются только объявления, без определений. Следовательно, если cout только объявлен в заголовочном файле iostream, то где же он определяется? Ответ: в Стандартной библиотеке С++, которая автоматически подключается к вашему проекту на этапе линкинга.


Пишем свои собственные заголовочные файлы

Теперь давайте вернемся к примеру, который мы обсуждали на предыдущем уроке. У нас было два файла: add.cpp и main.cpp.

int add ( int x , int y ) ; // предварительное объявление с использованием прототипа функции std :: cout << "The sum of 3 and 4 is " << add ( 3 , 4 ) << std :: endl ;

Примечание: Если вы создаете все файлы заново, то не забудьте добавить add.cpp в свой проект, чтобы он был подключен к компиляции.

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

Написать свой собственный заголовочный файл не так уж и сложно. Заголовочные файлы состоят из двух частей:

Директивы препроцессора — в частности, header guards, которые предотвращают вызов заголовочного файла больше одного раза из одного и того же файла (об этом детально на следующем уроке).

Содержимое заголовочного файла — набор объявлений.

Все ваши заголовочные файлы (которые вы написали самостоятельно) должны иметь расширение .h .

// Начнем с директив препроцессора. ADD_H – это произвольное уникальное имя (обычно используется имя заголовочного файла) int add ( int x , int y ) ; // прототип функции add() (не забывайте точку с запятой в конце!)

Чтобы использовать этот файл в main.cpp, вам сначала нужно будет подключить его к проекту.

main.cpp, в котором мы подключаем add.h:

std :: cout << "The sum of 3 and 4 is " << add ( 3 , 4 ) << std :: endl ;

add.cpp остается без изменений:

Если вы получили ошибку от линкера, что функция аdd() не определена, то убедитесь, что вы корректно подключили add.cpp к вашему проекту (и к компиляции тоже)!

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

Почему iostream пишется без окончания .h?

Когда C++ только создавался, все файлы библиотеки Runtime имели окончание .h. Оригинальные версии cout и cin объявлены в iostream.h. При стандартизации языка С++ комитетом ANSI, решили перенести все функции из библиотеки Runtime в пространствo имен std, чтобы предотвратить возможность возникновения конфликтов имен с пользовательскими идентификаторами (что, между прочим, является хорошей идеей). Тем не менее, возникла проблема: если все функции переместить в пространство имен std, то старые программы переставали работать!

Когда вы подключаете заголовочный файл из Стандартной библиотеки C++, убедитесь, что вы используете версию без .h (если она существует). В противном случае, вы будете использовать устаревшую версию заголовочного файла, который уже больше не поддерживается.

Кроме того, многие библиотеки, унаследованные от языка Cи, которые до сих пор используются в C++, также были продублированы с добавлением префикса c (например, stdlib.h стал cstdlib). Функционал этих библиотек также перенесли в пространство имен std, чтобы избежать возможность возникновения конфликтов имен с пользовательскими идентификаторами.

Можно ли записывать определения в заголовочных файлах?

Язык C++ не будет жаловаться, если вы это сделаете, но так делать не принято.

Иногда делаются исключения для простых функций, которые вряд ли изменятся (например, где определение состоит всего лишь из одной строки).

Советы

Вот несколько советов по написанию собственных заголовочных файлов:

Всегда используйте директивы препроцессора.

Не определяйте переменные в заголовочных файлах, если это не константы. Заголовочные файлы следует использовать только для объявлений.

Читайте также: