переменные разных типов с одинаковым идентификатором, связанные вместе - PullRequest
0 голосов
/ 25 февраля 2019

ах

void addr(void);

переменного тока

#include <stdio.h>

int x;

void addr(void) {
    printf("a:x=%p\n", &x);
}

вс

#include <stdio.h>
#include "a.h"

char x;

int main(void) {
    addr();                 /* a:x=0x601044 */
    printf("b:x=%p\n", &x); /* b:x=0x601044 */

    return 0;
}

Почему компилятор или компоновщик не жалуются на два внешних объявления с различным типом и одинаковым идентификатором ( x ), и они молча связаны друг с другом?

Среда:

$ gcc --version
gcc (Ubuntu 4.8.4-2ubuntu1~14.04.3) 4.8.4
$ gcc -o test -Wall -std=c11 a.c b.c

Ответы [ 2 ]

0 голосов
/ 25 февраля 2019

Введение

Стандарт C не определяет поведение определения идентификатора с внешней связью дважды.Некоторое поведение обычно определяется как расширение C, особенно в системах Unix.Однако это расширение опирается на определения, имеющие совместимые типы;результат определения int x; и char x; обычно не определяется.

Обсуждение

Определение идентификатора с внешней связью дважды нарушает ограничение в стандарте C, в C 2018 6,9 5 (добавлен жирный шрифт):

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

В вашей программе x используется в выражении &x, поэтому применимо указанное выше ограничение: должно быть ровно одно внешнее определение для x.Когда ограничение нарушается, результирующее поведение не определяется стандартом C согласно C 2018 4 2.

Почему тогда int x; и char x; ведут себя иначе, чем int x = 0; и char x = 0;?Можно подумать, что они должны быть одинаковыми, потому что первые являются предварительными определениями (поскольку у них нет спецификатора или инициализатора класса хранения), а C 2018 6.9.2 2 говорит:

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

Есть две причины.Первое - это правило о нарушении ограничения, которое приводит к тому, что поведение, не определенное стандартом C, является основным правилом;оно имеет приоритет над правилом о предварительных определениях.

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

Например, Двоичный интерфейс приложений Systems V описывает, как можно согласовать несколько определений в случаях, когда естьсмешанные сильные и слабые определения или смешанные общие и не общие определения.Компилятор сотрудничает с этим расширением C, создавая объектный файл, который помечает идентификаторы по-разному в зависимости от того, имеют ли они регулярные определения или только предварительные определения.Например, компиляция файла, содержащего char x; с Apple LLVM 10.0.0 и clang-1000.11.45.5 для x86_64, производит символ x, помеченный для общего раздела, но компиляция файла, содержащего int x = 0;, производит символ xотмечен для общего раздела.(Когда команда nm применяется к объектному файлу, созданному компилятором, для этих разделов отображаются C и S соответственно.)

Сводка

В результате получается:

  • Определение x дважды не определяется стандартом C.
  • Компилятор и компоновщик расширяют стандарт C, чтобы разрешить несколько предварительных определений x наряду с максимальноодно регулярное определение.
  • Несмотря на расширение, поведение определения x с int в одном месте и char в другом месте является неправильным, но не диагностируется компоновщиком.
0 голосов
/ 25 февраля 2019

Объявление int x; в a.c и char x; в b.c являются только предварительными определениями идентификатора x.

В проекте стандарта C11 N1570 говорится:

6.9.2 Определения внешних объектов
...
2 Объявление идентификатора дляобъект, имеющий область действия файла без инициализатора и без спецификатора класса хранения или со статическим спецификатором класса хранения, составляет предварительное определение .

Если вместо этого вы инициализируете x в обоих файлах (что-то вроде int x = 2; в a.c и char x = '1'; в b.c, они становятся «полными» определениями, и тогда у вас будет многократная ошибка определения из компоновщика.

что-то вроде:

Error   LNK1169 one or more multiply defined symbols found  
Error   LNK2005 x already defined in a.obj  
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...