int a;
int a=3; //error as cpp compiled with clang++-7 compiler but not as C compiled with clang-7;
int main() {
}
Для C компилятор, кажется, объединяет эти символы в один глобальный символ, но для C ++ это ошибка.
Демо
file1:
int a = 2;
file2:
#include<stdio.h>
int a;
int main() {
printf("%d", a); //2
}
Поскольку C файлы, скомпилированные с помощью clang-7, компоновщик не выдает ошибку, и я предполагаю, что он преобразует неинициализированный глобальный символ 'a' к внешнему символу (рассматривая его, как если бы он был скомпилирован как внешнее объявление). Так как файлы C ++ скомпилированы с помощью clang ++ - 7, компоновщик выдает ошибку множественного определения.
Обновление: связанный вопрос действительно отвечает на первый пример в моем вопросе, а именно: «В C, если фактический внешнее определение находится раньше или позже в той же самой единице перевода, тогда предварительное определение просто действует как декларация. ' и «C ++ не имеет« предварительных определений ».
Что касается второго сценария, если я печатаю a, то он печатает 2, так что, очевидно, компоновщик связал его правильно (но я ранее предполагал, что предварительное определение будет инициализировано компилятором в 0) как глобальное определение и может вызвать ошибку связи.
Оказывается, что int i[];
предварительное определение в обоих файлах также связано с одним определением. int i[5];
также является предварительным определением в .common, только с другим размером, выраженным ассемблеру. Первое известно как предварительное определение с неполным типом, тогда как второе является предварительным определением с полным типом.
Что происходит с компилятором C, так это то, что int a
становится сильным слабый глобальный в .common и оставленный неинициализированным (где .common подразумевает слабый глобальный) в таблице символов (тогда как extern int a
будет внешним символом), и компоновщик принимает необходимое решение , то есть он игнорирует все глобалы со слабой связью, определенные с помощью #pragma weak
, если в единице перевода существует глобал со строгой связью с тем же идентификатором, где 2 сильные границы будут ошибкой множественного определения (но если он не найдет сильную -bounds и 1 слабой границей, выходной файл представляет собой одну слабую границу, и если он не находит сильных границ, но две слабых границы, он выбирает определение в первом файле в командной строке и выводит одну слабую границу Хотя две слабые границы - это два определения для компоновщика (потому что они инициализируются компилятором в 0) , это не множественная ошибка определения, потому что они оба являются слабосвязанными), а затем разрешает все символы .common, чтобы указывать на сильный / слабый связанный сильный глобал. https://godbolt.org/z/Xu_8tY https://docs.oracle.com/cd/E19120-01/open.solaris/819-0690/chapter2-93321/index.html Поскольку baz
объявлено с #pragma слабым, оно является слабой границей и обнуляется компилятором и вставляется. bss (даже если это слабый глобал, он не go в .common, потому что он слабосвязан; все слабые переменные go в .bss, если неинициализированы и инициализируются компилятором, или. данные, если они инициализированы). Если он не был объявлен с #pragma weak
, baz
будет go общим, и компоновщик обнулит его, если не найден сильный глобальный символ со слабой / сильной связью.
Компилятор C ++ делает int a
сильный глобал в .bss и инициализирует его 0 : https://godbolt.org/z/aGT2-o, поэтому компоновщик обрабатывает его как множественное определение.