Почему в Си может быть несколько объявлений, но только одно определение? - PullRequest
3 голосов
/ 01 июля 2011

В Си, почему у функции может быть несколько объявлений, но только одно определение? Может кто-нибудь уточнить это, пожалуйста!

Ответы [ 4 ]

4 голосов
/ 01 июля 2011

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

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

const char *foo() {
  return "world";
}

Достаточно просто, верно?Теперь мы скомпилируем два файла.a.c содержит только foo.b.c также содержит это:

const char *bar() {
  return "Hello world";
}

Компилятор может сделать так, чтобы foo() 'world' указывал на середину bar() 'Hello world'.Линкер должен каким-то образом определить, что два foo() идентичны, даже если они указывают на неидентичные константные данные.

При рассмотрении правил наложения имен возникает большая проблема.Рассмотрим:

void frob(int *p);

int foo() {
  int x = 1;
  x++;
  frob(&x);
}

Скомпилировано само по себе, это может привести к тому, что ассемблерный код будет похож на:

foo:
    sub %esp, 4               ; allocate stack space for x
    mov dword [%esp], 2       ; set x to 2 (x++ combined with initialization)
    push %esp                 ; push %x to the stack as frob's argument
    call frob
    mov %eax, dword [%esp+4]  ; load the value of x into eax for the return value
    add %esp, 8               ; clear the stack of frob's argument and x
    ret                       ; return

Теперь давайте скомпилируем его с определением frob в области действия:

void frob(int *p) { /* no-op */ }

Теперь у нас есть:

frob:
    ret                       ; return

foo:
    mov %eax, 2               ; return value = 2
    ret                       ; return

Как компоновщик может сказать, что два foo определения идентичны?

Учитывая трудности с доказательством идентичности тел функций, C выбираетпросто запретить вам определять одну и ту же функцию дважды.C ++ использует другой подход для встроенных и шаблонных функций;он просто не проверяет и предполагает, что они идентичны.

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

3 голосов
/ 01 июля 2011

Краткий ответ - «исторические причины».

Объявление указывает компилятору, как генерировать код для вызова функции.

Определение приводит к коду для реализации функции. Как правило, компилятор генерирует ассемблерный код, который передается ассемблеру, который генерирует объектные файлы, которые передаются компоновщику. Компилятор, ассемблер и компоновщик традиционно писались разными людьми ... Они не были частью единого "пакета".

При наличии нескольких определений компилятор выдаст несколько копий функции. Тогда компоновщик увидит функцию, определенную несколько раз, и не будет знать, какую функцию вы пытаетесь вызвать. Затем компоновщик обычно генерирует ошибку, такую ​​как «многократно определенный символ ххх». Люди, которые определили C, не хотели накладывать дополнительное бремя на разработчиков компоновщика (например, проверять, были ли множественные определения идентичными), поэтому они просто запретили это в языке.

При наличии нескольких объявлений единственным бременем является компилятор C.

По крайней мере, это мое подозрение в обосновании.

1 голос
/ 01 июля 2011

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

1 голос
/ 01 июля 2011

Может иметь несколько объявлений, но все они должны быть идентичными, но не имеет смысла иметь более одного определения. Как компилятор решит, какой он должен использовать?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...