Глобальная область видимости не содержит visual studio
Использую Visual Studio 2019
Я хочу запустить этот код
Но я получаю следующие ошибки:
Код серьезности Описание Ошибка состояния подавления строки файла проекта (активно) E1696 не может открыть исходный файл "stdio.h" fox C: \ Program Files (x86) \ Microsoft Visual Studio \ 2019 \ Community \ VC \ Tools \ MSVC \ 14.27.29110 \ включить \ cstdio 12
Код серьезности Описание Ошибка состояния подавления строки файла проекта (активно) E0282 в глобальной области нет "floorf" fox C: \ Program Files (x86) \ Microsoft Visual Studio \ 2019 \ Community \ VC \ Tools \ MSVC \ 14.27.29110 \ include \ cmath 717
Код серьезности Описание Ошибка состояния подавления строки файла проекта C1083 Не удается открыть включаемый файл: 'crtdbg.h': нет такого файла или каталога fox C: \ Program Files (x86) \ Microsoft Visual Studio \ 2019 \ Community \ VC \ Tools \ MSVC \ 14.27.29110 \ include \ yval.h 12
Код серьезности Описание Ошибка состояния подавления строки файла проекта (активно) E0065 ожидается ';' лиса C: \ Program Files (x86) \ Microsoft Visual Studio \ 2019 \ Community \ VC \ Tools \ MSVC \ 14.27.29110 \ include \ xlocale 34
Код серьезности Описание Ошибка состояния подавления строки файла проекта (активно) E0077 это объявление не имеет класса хранения или спецификатора типа fox C: \ Program Files (x86) \ Microsoft Visual Studio \ 2019 \ Community \ VC \ Tools \ MSVC \ 14.27.29110 \ включить \ xlocinfo.h 119
Код серьезности Описание Ошибка состояния подавления строки файла проекта (активный) Шаблон класса E0135 "std :: _ Locbase " не имеет члена "numeric" fox C: \ Program Files (x86) \ Microsoft Visual Studio \ 2019 \ Community \ VC \ Инструменты \ MSVC \ 14.27.29110 \ include \ xlocale 47
Код серьезности Описание Ошибка состояния подавления строки файла проекта (активно) E0282 в глобальной области нет "expf" fox C: \ Program Files (x86) \ Microsoft Visual Studio \ 2019 \ Community \ VC \ Tools \ MSVC \ 14.27.29110 \ include \ cmath 715
Код серьезности Описание Ошибка состояния подавления строки файла проекта (активный) E0757 переменная «_ACRTIMP» не является именем типа fox C: \ Program Files (x86) \ Microsoft Visual Studio \ 2019 \ Community \ VC \ Tools \ MSVC \ 14.27.29110 \ включить \ xlocinfo.h 134
Код серьезности Описание Ошибка состояния подавления строки файла проекта (активно) E1696 не может открыть исходный файл "share.h" fox C: \ Program Files (x86) \ Microsoft Visual Studio \ 2019 \ Community \ VC \ Tools \ MSVC \ 14.27.29110 \ включить \ xiosbase 11
Вот пример как следует объявлять переменные и функции.
Но я хочу в StdAfx.h поместить все свои и сторонние библиотеки.
Однако варианты с extern приводят к огромным трудностям, понижению компактности и децентрализации управления.
Неужели необходимо для каждой переменной указывать extern , а в дальнейшем создавать с инициализацией еще и после StdAfx ?
Вы хотите использовать прекомпилированный заголовок не по назначению. Да, он предназначен для ускорения времени компиляции, однако в нем должны содержаться исключительно редко изменяемые определения, например, сторонних библиотеки или модулей других компонентов, а также всевозможные константы. Иначе выгоды от него особой не получите. Код вашего модуля, в том числе глобальные функции и переменные (которых вообще следует избегать), не стоит туда вносить, а лучше выделить в отдельный компактный заголовочный файл, который не будет замедлять сборку.
3 ответа 3
@manking, откровенно скажу, будет ли это работать в StdAfx.h, не знаю.
Например заголовочный файл
Но, как такой прием будет работать в предварительно откомпилированных заголовках - не знаю.
UPD по теме static, global и т.п.
Возьмем 2 файла t.c и tt.c
Откомпилируем в .o и посмотрим на таблицы имен
Сделаем загрузочный модуль и запустим его
Убедимся, что константы в модулях еще до запуска
Надеюсь это прояснит ситуацию с static, extern, что где в памяти лежит и подскажет как надо объявлять переменные.
Нет это не подходит. Собственно смысл всего вопроса - можно ли без геморроя поместить любой файл который нормально компилируется в глобальной области видимости, в прекомпилированный заголовок. Вот если вы подключаете какую то часть boost. Вам она очень нужна. Кладете это в StdAfx.h И тут лезут ошибки "обнаружен многократно определенный символ" - потому что у boost есть глобальные переменные. Изменять boost вы никак не можете. Вот в данном случаи как то можно всё таки скомпилировать?
В общем суть я понял. Но по вашему комментарию, а не этому ответу. Очень уж он запутан. Глобальная переменная и статическая глобальная переменная по содержимому равны. Они видны везде, и отличаются лишь временем инициализации. Для простых типов достаточно знать, что инициализация произойдет раньше её использования. Таким образом самый простой путь использования глобальных переменных внутри прекомпилированного заголовка это, сделать их статическими. Но в таком случаи придётся изменять содержимое модулей помещенных в stdafx.
@manking, если глобальные (extern) заменить на static, то они будут видны только из этого модуля. Это раз. А самое главное - два (не понял, Вы это понимаете или нет) все extern с одним именем - это одна и та же переменная для всех модулей (один экземпляр в памяти), а вот все static будут разными, каждая в своем модуле. Если Вы также считаете, просто я не понимаю этого, то хорошо. Только я не представляю, насколько просто переделать все Ваши модули под новую архитектуру. Зачем Вам вообще эта предкомпиляция? Напишите нормальный Makefile и все будет ОК.
Предкомпилируемый заголовочный файл StdAfx.h работает следующим образом. После любого изменения в нём (или в тех файлах, которые он в себя включает) компилятор обрабатывает его содержимое и в конце тупо сохраняет состояние своих символьных таблиц во внешний файл в удобном ему бинарном формате. В последующие разы (если StdAfx.h не изменился) он просто быстро загружает из этого файла символьные таблицы, и они оказываются в состоянии, как будто бы компилятор только что честно прошёл по всему содержимому. Именно поэтому StdAfx.h всегда должен включаться в любой файл самой первой строкой.
Нужно понимать, что инициализация - это действие, совершаемое над переменной. Т.е. обработка этой языковой конструкции подразумевает генерацию кода. Пусть и весьма тривиального. Было бы странно, если бы компилятор хранил в своих символьных таблицах не только символы, но и ещё какой-то выполняемый код. Это бы серьёзно всё усложнило, поскольку появилась бы явная взаимосвязь между совершенно разными фазами компиляции - между фазой разбора и фазой генерации кода. Притом что компиляторы - это и так очень сложная вещь. Короче говоря, я не думаю, что кому-нибудь понравился бы такой компилятор, который генерирует код уже в процессе разбора символов. Это бы полностью обессмыслило идею разделения файлов на модульные и заголовочные. Идея, кстати, уже не выглядит удачной в наше время. Но раз уж она лежит в основе языка, то нужно это учитывать.
@manking Потому что статические переменные инициализируются не там, где объявляются, а за мгновение до первого использования. И значит в процессе прохождения по заголовку компилятор не генерирует никакого кода вне зависимости от того, есть инициализация или нет.
@Shamov, я не специалист по архитектуре компиляторов, но все же не могу согласиться с вашим ответом. Например, что есть символ функции, как не собственно ее тело, т.е. код. Символ - это именно содержимое, тело кода. Далее, предкомпилированный файл может содержать любой допустимый для других заголовочных файлов код, хоть встраиваемые шаблонные функции. Да и потом, разделение транслируемых файлов на заголовочные и файлы реализации - распространенный способ организации исходных текстов в проекте, и как он может выглядеть неудачной, я не понимаю. Прямо к языку это никак не относится!
@manking, статическая переменная видна только в том модуле, где она описана. В другом модуле переменная с таким же именем - это уже другая. Глобальная (нестатической она естественно быть не может) одна для всех модулей. Она должна определяться только в одном модуле, а остальные на нее ссылаются. Присвоивание константных значений всем статическим переменным происходит при компиляции. -- Для иллюстрации я сделаю UPD к своему ответу, посмотрите, что непонятно - спрашивайте.
В файле StdAfx.h объявляете переменную. В тех файлах, которым нужно ее использовать - подключаете StdAfx.h. В одном месте, например в main(), инициализируете. Все.
Не вижу никаких проблем. Должно работать. Пусть себе глобальные переменные сидят в include файлах. К директиве include нужно относиться просто - она говорит "вставь содержимое файла" Не больше, не меньше. Если в вставляемом файле есть свои include, процедура рекурсивно повторяется. Поэтому, простенький main.cpp может развернуться препроцессором в тысячи строк. Суть "прекомпилированных заголовков" в том, что препроцессор собирает эти все хедеры один раз в один файл и потом просто быстро вставляет. И ещё компилятор делает "черновой проход" и потом не парсит эти файлы много раз.
Но тогда вываливаются ошибки: error LNK2005: "int r" (?r@@3HA) уже определен в stdafx.obj fatal error LNK1169: обнаружен многократно определенный символ - один или более
Естественно, а как линкеру понять к какой из этих (определенных в разных .o модулях) глобальных переменных обращаться? @manking, а мое предложение с условной трансляцией не проверили?
глобальная переменная определена в множестве файлов? В main.cpp ее уже не нужно определять. Она уже есть. Переменная должна быть определена строго в одном месте.
Часть 1. Вступление
Часть 2. Заголовочные файлы
Часть 3. Область видимости
Часть 4. Классы
…
Эта статья является переводом части руководства Google по стилю в C++ на русский язык.
Исходная статья (fork на github), обновляемый перевод.
Область видимости
Пространство имён
Размещайте свой код в пространстве имён (за некоторыми исключениями). Пространство имён должно иметь уникальное имя, формируемое на основе названия проекта, и, возможно, пути. Не используйте директиву using (например, using namespace foo). Не используйте встроенные (inline) пространства имён. Для безымянных пространств имён смотрите Безымянные пространства имён и статические переменные.
Определение
Пространства имён делят глобальную область видимости на отдельные именованные области позволяя избежать совпадения (коллизий) имён.
За
Пространства имён позволяют избежать конфликта имён в больших программах, при этом сами имена остаются достаточно короткими.
Например, если два разных проекта содержат класс Foo в глобальной области видимости, имена могут конфликтовать. Если каждый проект размещает код в своё пространство имён, то project1::Foo и project2::Foo будут разными именами, конфликтов не будет, в то же время код каждого проекта будет использовать Foo без префикса.
Пространства имён inline автоматически делают видимыми свои имена для включающего пространства имён. Рассмотрим пример кода:
Здесь выражения outer::inner::foo() и outer::foo() взаимозаменяемы. Inline пространства имён в основном используются для ABI-совместимости разных версий.
Против
Пространства имён могут запутать программиста, усложнить понимание, что к чему относится.
Пространства имён inline, в частности, могут сбивать с толку т.к. область видимости не ограничена местом определения. Поэтому такой вид пространств имён может быть полезен только при обновлении интерфейсов с сохранением совместимости.
В ряде случаев требуется использование полных имён и это может сделать код сильно перегруженным.
- Следуйте правилам Именования Пространств Имён.
- В конце объявления пространства имён добавляйте комментарий, аналогично показанным в примерах.
В .cc файлах могут быть дополнительные объявления, такие как флаги или using-декларации.
Не используйте using-директиву чтобы сделать доступными все имена из пространства имён.
Не используйте псевдонимы пространств имён в блоке namespace в заголовочном файле, за исключением явно обозначенных «внутренних» пространств имён. Связано это с тем, что любая декларация в заголовочном файле становится частью публичного API, экспортируемого этим файлом.
Безымянные пространства имён и статические переменные
Когда определения внутри .cc файла не используются в других исходных файлах, размещайте такие определения в безымянном пространстве имён или объявляйте их как static. Не используйте такой тип определения в заголовочных файлах (.h).
Определение
Размещённые в безымянном пространстве имён объявления могут быть слинкованы как internal (только для внутреннего использования). Функции и переменные также могут быть с internal линковкой, если они заявлены как static. Такие типы объявления подразумевают, что они будут недоступны из другого файла. Если другой файл объявляет сущность с таким же именем, то оба объявления будут полностью независимы.
Вердикт
Использование internal линковки в .cc файлах предпочтительно для любого кода, к которому не обращаются снаружи (из других файлов). Не используйте подходы internal линковки в .h файлах.
Формат описания безымянного пространства имён полностью аналогичен именованному варианту. Не забывайте к закрывающей скобке написать комментарий, в котором имя оставьте пустым:
Функции: глобальные, статические внутри класса, вне класса
Предпочтительно размещать функции либо внутри класса, либо в некотором пространстве имён. Использование глобальных функций должно быть минимальным. Также не используйте класс для группировки различных функций, объявляя их статическими в одном классе: статические функции (по-правильному) должны использоваться для работы с экземплярами класса или его статическими данными.
За
Вообще функции (как статические, так и вне класса) довольно полезная штука, Кэп. И размещение функций либо в классе, либо в пространстве имён позволяет всё остальное содержать в чистоте (я про глобальное пространство имён).
Против
Иногда разумнее статические функции класса и функции вне класса сгруппировать в одном месте, в новом классе. Например, когда у них сложные зависимости от всего или им нужен доступ к внешним ресурсам.
Вердикт
Иногда полезно объявить функцию, не привязанную к экземпляру класса. И можно сделать либо статическую функцию в классе, либо внешнюю (вне класса) функцию. Желательно, чтобы функция-вне-класса не использовала внешних переменных и находилась в пространстве имён. Не создавайте классы только для группировки статических функций: это всё равно, что дать функциям некий префикс и группировка становится лишней.
Если требуется определить функцию: в .cc-файле; вне класса; используемой только в локальном файле — используйте internal линковку для ограничения области видимости.
Локальные переменные
Объявляйте переменные внутри функции в наиболее узкойобласти видимости, инициализируйте такие переменные при объявлении.
Язык C++ позволяет объявлять переменные в любом месте функции. Однако рекомендуется делать это в наиболее узкой (наиболее вложенной) области видимости, и по возможности ближе к первому использованию. Это облегчает поиск объявлений, проще узнать тип переменной и её начальное значение. Также рекомендуется использовать инициализацию, а не объявление с присваиванием. Примеры:
Переменные, необходимые только внутри кода if, while и for лучше объявлять внутри условий, тогда область их видимости будет ограничена только соответствующим блоком кода:
Однако учитывайте одну тонкость: если переменная есть экземпляр объекта, то при каждом входе в область видимости будет вызываться конструктор, и, соответственно, при выходе будет вызываться деструктор.
Возможно было бы более эффективно такую переменную (которая используется внутри цикла) объявить вне цикла:
Переменные: статические и глобальные
Объекты в статической области видимости/действия запрещены, кроме тривиально удаляемых. Фактически это означает, что деструктор должен ничего не делать (включая вложенные или базовые типы). Формально это можно описать, что тип не содержит пользовательского или виртуального деструктора и что все базовые типы и не-статические члены ведут себя аналогично (т.е. являются тривиально удаляемыми). Статические переменные в функциях могут быть динамически инициализированными. Использование же динамической инициализации для статических членов класса или переменных в области пространства имён (namespace) в целом не рекомендуется, однако допустимо в ряде случаев (см. ниже).
Эмпирическое правило: если глобальную переменную (рассматривая её изолированно) можно объявить как constexpr, значить она соответствует вышеуказанным требованиям.
Определение
Каждый объект имеет тот или иной тип времени жизни / storage duration, и, очевидно, это влияет на время жизни объекта. Объекты статического типа доступны с момента их инициализации до момента завершения программы. Такие объекты могут быть переменными в пространстве имён («глобальные переменные»), статическими членами классов, локальными переменными внутри функций со спецификатором static. Статические переменные в функциях инициализируются, когда поток выполнения кода проходит в первый раз через объявление; все остальные объекты статического типа инициализируются в фазе старта (start-up) приложения. Все объекты статического типа удаляются в фазе завершения программы (до обработки незавершённых(unjoined) потоков).
Инициализация может быть динамическая, т.е. во время инициализации делается что-то нетривиальное: например, конструктор выделяет память, или переменная инициализируется идентификатором процесса. Также инициализации может быть статической. Сначала выполняется статическая инициализация: для всех объектов статического типа (объект инициализируется либо заданной константой, либо заполняется нулями). Далее, если необходимо, выполняется динамическая инициализация.
За
Глобальные и статические переменные бывают очень полезными: константные имена, дополнительные структуры данных, флаги командной строки, логирование, регистрирование, инфраструктура и др.
Против
Глобальные и статические переменные с динамической инициализацией или нетривиальным деструктором могут сильно усложнить код и привести к трудно обнаруживаемым багам. Порядок динамической инициализации (и разрушения) объектов может быть различным когда есть несколько единиц трансляции. И, например, когда одна из инициализаций ссылается на некую переменную статического типа, то возможна ситуация доступа к объекту до корректного начала его жизненного цикла (до полного конструирования), или уже после окончания жизненного цикла. Далее, если программа создаёт несколько потоков, которые не завершаются к моменту выхода из программы, то эти потоки могут пытаться получить доступ к объектам, которые уже разрушены.
Вердикт
Когда деструктор тривиальный, тогда порядок разрушения в принципе не важен. В противном случае есть риск обратиться к объекту после его разрушения. Поэтому, настоятельно рекомендуется использовать только переменные со статическим типом размещения (конечно, если они имеют тривиальный деструктор). Фундаментальные типы (указатели или int), как и массивы из них, являются тривиально разрушаемыми. Переменные с типом constexp также тривиально разрушаемые.
Отметим, что ссылка не есть сам объект, и, следовательно, к ним не применяются ограничения по разрушению объекта. Хотя ограничения на динамическую инициализацию остаются в силе. В частности, внутри функции допустим следующий код static T& t = *new T;.
Тонкости инициализации
Инициализация может быть запутанной: мало того, что конструктору нужно (желательно правильно) отработать, так есть ещё и предварительные вычисления:
На выполнение всех выражений, кроме первого, может повлиять порядок инициализации, который может быть разным/неопределённым (или зависимым от . ).
Рассмотрим константную инициализацию. Это означает, что инициализационное выражение — константное, и если при создании объекта вызывается конструктор, то он (конструктор) тоже должен быть заявлен как constexpr:
Константная инициализация является рекомендуемой для большинства случаев. Константную инициализацию переменных со статическим размещением рекомендуется помечать как constexpr или атрибутом ABSL_CONST_INIT
. Любую переменную вне функции, со статическим размещением и без указанной выше маркировки следует считать динамически инициализируемой (и тщательно проверять на ревью кода).
Например, следующие инициализации могут привести к проблемам:
Динамическая инициализация переменных вне функций не рекомендуется. В общем случае это запрещено, однако, это можно делать если никакой код программы не зависит от порядка инициализации этой переменной среди других: в этом случае изменение порядка инициализации не может что-то поломать. Например:
Динамическая же инициализация статических переменных в функциях (локальных) допустима и является широко распространённой практикой.
Стандартные практики
- Глобальные строки: если требуется глобальная или статическая строковая константа, то рекомендуется использовать простой символьный массив или указатель на первый символ строкового литерала. Строковые литералы обычно находятся в статическом размещении (их время жизни) и этого в большинстве случаев достаточно.
- Динамические контейнеры (map, set и т.д.): если требуется статическая коллекция с фиксированными данными (например, таблицы значений для поиска), то не используйте динамические контейнеры из стандартной библиотеки как тип для статической переменной, т.к. у этих контейнеров нетривиальный деструктор. Вместо этого попробуйте использовать массивы простых (тривиальных) типов, например массив из массивов целых чисел (вместо std::map ) или, например, массив структур с полями int и const char*. Учтите, что для небольших коллекций линейный поиск обычно вполне приемлем (и может быть очень эффективным благодаря компактному размещению в памяти). Также можете воспользоваться алгоритмами absl/algorithm/container.h для стандартных операций. Также возможно создавать коллекцию данных уже отсортированной и использовать алгоритм бинарного поиска. Если без динамического контейнера не обойтись, то попробуйте использовать статическую переменную-указатель, объявленную в функции (см. ниже).
- Умные указатели (unique_ptr, shared_ptr): умные указатели освобождают ресурсы в деструкторе и поэтому использовать их нельзя. Попробуйте применить другие практики/способы, описанные в разделе. Например, одно из простых решений это использовать обычный указатель на динамически выделенный объект и далее никогда не удалять его (см. последний вариант списка).
- Статические переменные пользовательского типа: если требуется статический и константный пользовательский тип, заполненный данными, то можете объявить у этого типа тривиальный деструктор и constexpr конструктор.
- Если все другие способы не подходят, то можно создать динамический объект и никогда не удалять его. Объект можно создать с использованием статического указателя или ссылки, объявленной в функции, например: static const auto& impl = *new T(args. );.
Потоковые переменные
Потоковые переменные (thread_local), объявленные вне функций должны быть инициализированы константой, вычисляемой во время компиляции. И это должно быть сделано с помощью атрибута ABSL_CONST_INIT
. В целом, для определения данных, специфичных для каждого потока, использование thread_local является наиболее предпочтительным.
Определение
Начиная с C++11 переменные можно объявлять со спецификатором thread_local:
Каждая такая переменная представляется собой коллекцию объектов. Разные потоки работают с разными экземплярами переменной (каждый со своим экземпляром). По поведению переменные thread_local во многом похожи на Переменные со статическим типом размещения. Например, они могут быть объявлены в пространстве имён, внутри функций, как статические члены класса (как обычные члены класса — нельзя).
Инициализация потоковых переменных очень напоминает статические переменные, за исключением что это делается для каждого потока. В том числе это означает, что безопасно объявлять thread_local переменные внутри функции. Однако в целом thread_local переменные подвержены тем же проблемам, что и статические переменные (различный порядок инициализации и т.д.).
Переменная thread_local разрушается вместе с завершением потока, так что здесь нет проблем с порядком разрушения, как у статических переменных.
- Потоковые переменные в принципе защищены от эффекта гонок (т.к. каждый поток обычно имеет доступ только к своему экземпляру), что очень полезно для программирования потоков.
- Переменные thread_local являются единственным стандартизованным средством для создания локальных потоковых данных.
- Операции с thread_local переменными могут привести к выполнению кода, который не предполагался и его размер также может быть различным.
- Переменные thread_local являются по сути глобальными переменными со всеми их недостатками (за исключением потокобезопасности).
- Выделяемая для thread_local память зависит от количества потоков и её размер может быть значительным.
- Обыкновенный член класса не может быть thread_local.
- В ряде случаев thread_local переменные могут проигрывать по эффективности хорошо оптимизированному компилятором коду (например, интринсикам/intrinsics).
Вердикт
Переменные thread_local, заявленные внутри функций, можно использовать без ограничений, т.к. у них с безопасностью всё отлично. Отметим, что возможно использовать объявленную внутри функции переменную thread_local и вне функции. Для этого нужна функция доступа к переменной:
Переменные thread_local в классе или пространстве имён необходимо инициализировать константой времени компиляции (т.е. динамическая инициализация недопустима). Для этого необходимо аннотировать эти thread_local переменные с помощью ABSL_CONST_INIT
(или constexpr, но это лучше использовать пореже):
Переменные thread_local должны быть предпочтительным способом определения потоковых данных.
В онлайн редакторе код запускается в visual studio community выдаёт
Необработанное исключение по адресу 0x00D83299 0xC00000FD: Stack overflow.
И при запуске без отладки завершил работу с кодом -1073741571.
Причём проблема именно в коде потому,что другой код запускается.
Буду благодарен за помощь.
Может поможет
dalbio, а не "маловат" объём матрицы для объекта на стеке? 1001 * 1001 * 8 = 8,016,008 = приблизительно 7.8 мегабайт. ВЕСЕМЬ МЕГАБАЙТ НА СТЕКЕ ПО-УМОЛЧАНИЮ. Я бы на месте стека тоже переполнился :-)
long long matr[1001][1001] - это будет 8016008 байт, 7.645МБ.
Стандартный размер стека в MS Visual Studio задан в 1МБ. Естественно, при объявлении настолько большого массива ты сразу получишь Stack Overflow.
Выходов из данной ситуации несколько.
Выход первый - подойти к вопросу рассудительно. Тебе точно нужен именно статический массив в 8МБ именно на стеке? Я думаю что нет. Я думаю что тебе нужен std::vector , в котором ты сможешь легко разместить все 1002001 элементов. На самом деле и двумерность массива тебе тоже не очень нужна, т.к. на самом деле она тебя сейчас только запутывает. Через простую функцию от двух аргументов можно легко перейти от двух индексов к индексу в линейном массиве.
Выход второй - вынести свой статический массив за пределы контекста функции. Это можно сделать, объявив этот массив как static или объявив его в глобальной области видимости.
Выход третий, которым я советую не пользоваться без однозначного понимания своих действий.
Можно изменить размер стека через настройки линкера.
В свойствах проекта: Configuration Properties -> Linker -> System:
Stack Reserve Size - значение в байтах, это максимальный размер стека для потока. Его можно изменить хоть до 32МБ и больше.
Подвох с этим значением в том, что потоков у твоего приложения не один даже если само твое приложение является однопоточным. Вместе с твоим главным потоком работает еще несколько служебных. Их стек тоже будет расширен. Это все приводит к увеличению потребления памяти.
Обычно размер стека по умолчанию не трогают или сжимают до 64КБ, т.к. большинству потоков этого более чем достаточно. А вот для требовательных потоков, обычно, отдельно расширяют стек до требуемых размеров в момент создания потока.
Таким образом достигается контроль памяти. Даже сегодня бывают случаи, когда ее бывает мало.
Скомпилированный код функции создает ссылку или вызов символа, но компоновщик не может найти определение символа в любой из библиотек или объектных файлов для связи.
Возможные причины
Существует множество способов получения этой ошибки. Все они используют ссылку на функцию или переменную, которую компоновщику не удалось Разрешить, или найти определение для. Компилятор может определить, когда символ не объявлен, но не может определить, не определенли символ. Это связано с тем, что определение может находиться в другом исходном файле или библиотеке. Если символ упоминается, но не определен, компоновщик создает неразрешенную extern ошибку символа Al.
Ниже приведены некоторые распространенные проблемы, вызывающие ошибку LNK2019.
Исходный файл, содержащий определение символа, не скомпилирован
в Visual Studio убедитесь, что исходный файл, определяющий символ, компилируется как часть проекта. Проверьте промежуточный каталог выходных данных сборки на наличие соответствующего OBJ-файла. если исходный файл не компилируется, щелкните его правой кнопкой мыши в Обозреватель решений и выберите пункт свойства , чтобы проверить свойства файла. На странице Свойства> конфигурацииОбщие должен отображаться тип элементакомпилятора C/C++. В командной строке убедитесь, что исходный файл, содержащий определение, скомпилирован.
Объектный файл или библиотека, содержащие определение символа, не связаны
в Visual Studio убедитесь, что объектный файл или библиотека, содержащие определение символа, связаны как часть проекта. В командной строке убедитесь, что список файлов для связывания содержит объектный файл или библиотеку.
Объявление символа написано не так, как определение символа
Проверьте правильность написания и регистра символов в объявлении и определении, а также в любом месте, где используется или вызывается символ.
Используется функция, но тип или число параметров не соответствуют определению функции
Объявление функции должно соответствовать определению. Убедитесь, что вызов функции соответствует объявлению и что объявление соответствует определению. Код, который вызывает функции шаблона, также должен иметь совпадающие объявления функции шаблона, включающие те же параметры шаблона, что и определение. Пример несоответствия объявления шаблона см. в разделе Sample LNK2019e. cpp статьи "примеры".
Функция или переменная объявлена, но не определена
Ошибка LNK2019 может возникать, если объявление существует в файле заголовка, но соответствующее определение не реализовано. Для функций элементов или static элементов данных реализация должна включать селектор области класса. Пример см. в разделе Missing Function Body or Variable.
Соглашение о вызовах отличается между объявлением функции и определением функции.
Соглашения о вызовах ( __cdecl , __stdcall , __fastcall или __vectorcall ) кодируются как часть декорированного имени. Убедитесь, что соглашение о вызовах одинаково.
Символ определен в файле C, но объявлен без использования extern "C" в файле C++
Символы, определенные в файле, который компилируется как C, имеют разные декорированные имена, чем символы, объявленные в файле C++, если не используется extern Модификатор "C" . Убедитесь, что объявление соответствует компоновке компиляции для каждого символа. Аналогично, если символ определяется в файле C++, который будет использоваться программой C, в определении следует использовать extern "C" .
Символ определяется как static , а затем на него указывает ссылка за пределами файла
В C++, в отличие от C, Глобальная const Ants имеет static компоновку. Чтобы обойти это ограничение, можно включить const инициализацию в файл заголовка и включить этот заголовок в cpp-файлы. Кроме того, можно сделать переменную const Ant и использовать const ссылку Ant для доступа к ней.
staticЧлен класса не определен
staticЧлен класса должен иметь уникальное определение или будет нарушать правило одного определения. staticЧлен класса, который не может быть определен как встроенный, должен быть определен в одном исходном файле с помощью его полного имени. Если он не определен вообще, компоновщик создает ошибку LNK2019.
Зависимость сборки определяется только в качестве зависимости проекта в решении
в более ранних версиях Visual Studio этот уровень зависимости был достаточным. однако начиная с Visual Studio 2010 Visual Studio требуется ссылка междупроектами. Если в проекте нет ссылки проекта на проект, может возникнуть Эта ошибка компоновщика. Чтобы устранить ошибку, добавьте ссылку одного проекта на другой.
Точка входа не определена
код приложения должен определять соответствующую точку входа: main или wmain для консольных приложений, а WinMain также wWinMain для Windows приложений. Дополнительные сведения см. в разделе main функция и аргументы командной строки или WinMain функция. Чтобы использовать настраиваемую точку входа, укажите параметр компоновщика /Entry (символ точки входа) .
построение консольного приложения с помощью параметров приложения Windows
Вы пытаетесь связать 64-разрядные библиотеки с 32-битным кодом или 32-bit Library в 64-разрядный код.
Библиотеки и объектные файлы, связанные с вашим кодом, должны быть скомпилированы для той же архитектуры, что и код. Убедитесь, что библиотеки, на которые ссылается проект, компилируются для той же архитектуры, что и проект. Убедитесь, что свойство /libpath или Дополнительные каталоги библиотек указывает на библиотеки, созданные для правильной архитектуры.
Для встраивания функций в разные исходные файлы используются различные параметры компилятора
Использование встроенных функций, определенных в CPP-файлах, и смешение в различных исходных файлах параметров компилятора для встраивания функций может привести к возникновению ошибки LNK2019. Для получения дополнительной информации см. Function Inlining Problems.
Автоматические переменные используются вне области действия
Автоматические переменные (области видимости функции) могут использоваться только в области видимости данной функции. Эти переменные не могут объявляться extern и использоваться в других исходных файлах. Пример см. в разделе Automatic (Function Scope) Variables.
Вы вызываете встроенные функции или передайте типы аргументов в встроенные функции, которые не поддерживаются в целевой архитектуре.
Например, если вы используете AVX2 встроенную функцию, но не указываете параметр компилятора / ARCH : AVX2 , компилятор предполагает, что встроенная функция является extern функцией Al. Вместо создания встроенной инструкции компилятор создает вызов extern символа Al с тем же именем, что и встроенная инструкция. Когда компоновщик пытается найти определение этой отсутствующей функции, он создает ошибку LNK2019. Убедитесь, что используются только встроенные функции и типы, поддерживаемые целевой архитектурой.
Вы занимаетесь смешением кода, который использует собственный wchar_t код с кодом, который не
действия по согласованности языка C++, выполненные в Visual Studio 2005, сделали wchar_t собственный тип по умолчанию. Если не все файлы были скомпилированы с использованием одних и тех же параметров /Zc: wchar_t , ссылки типа могут не разрешаться в совместимые типы. Убедитесь, что wchar_t типы во всех файлах библиотек и объектов совместимы. Либо обновите из wchar_t определения типа, либо используйте соответствующие параметры /Zc: wchar_t при компиляции.
Проблемы с библиотекой сторонних производителей и vcpkg
если вы видите эту ошибку при попытке настроить библиотеку стороннего производителя в рамках сборки, рассмотрите возможность использования vcpkg, диспетчера пакетов C++ для установки и сборки библиотеки. vcpkg поддерживает большой и растущей список библиотек сторонних производителей. Он задает все свойства конфигурации и зависимости, необходимые для успешной сборки в рамках проекта.
Средства диагностики
Иногда трудно определить, почему компоновщику не удается найти определенное определение символа. Часто проблема заключается в том, что в сборку не включен код, содержащий определение. Или же параметры сборки создали разные декорированные имена для extern символов Al. Существует несколько средств и параметров, которые могут помочь в диагностике ошибок LNK2019.
Параметр / VERBOSE Компоновщик может помочь определить, на какие файлы ссылается компоновщик. С помощью этого параметра можно проверить, включен ли в сборку файл, содержащий определение символа.
Параметры DUMPBIN/ EXPORTS и / SYMBOLS программы позволяют определить, какие символы определены в .dll и файлах объектов или библиотек. Убедитесь, что экспортированные декорированные имена соответствуют декорированным именам, которые ищет компоновщик.
UNDNAMEПрограмма может отобразить эквивалентный недекорированный extern символ Al для декорированного имени.
Примеры
Далее приводится несколько примеров кода, вызывающего ошибку LNK2019, а также сведения о том, как устранить ошибку.
Символ объявлен, но не определен
В этом примере переменная Al объявлена, extern но не определена:
Ниже приведен еще один пример, где переменная и функция объявляются как, extern но определение не предоставляется:
staticЭлемент данных объявлен, но не определен
Ошибка LNK2019 также может возникать, если static элемент данных объявлен, но не определен. В следующем примере показано возникновение ошибки LNK2019 и приводятся сведения по ее устранению.
Параметры объявления не соответствуют определению
Код, который вызывает функции шаблона, должен иметь совпадающие объявления функции шаблона. Объявления должны содержать те же параметры шаблона, что и определение. В следующем примере показано возникновение ошибки LNK2019 для определяемого пользователем оператора и приводятся сведения по ее устранению.
Непротиворечивые wchar_t определения типов
В этом примере создается библиотека DLL с экспортом WCHAR , который разрешается в wchar_t .
В следующем примере используется библиотека DLL из предыдущего примера и создается LNK2019, так как типы unsigned short* и WCHAR* не совпадают.
Чтобы устранить эту ошибку, измените unsigned short значение на wchar_t или WCHAR или скомпилируйте LNK2019g. cpp с помощью /Zc: wchar_t -.
Дополнительные ресурсы
Читайте также: