Имеет ли значение порядок объектов C? - PullRequest
3 голосов
/ 12 июня 2011

Имеет ли значение порядок, в котором объекты C появляются в файле?

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

Это относится к какому-либо конкретному компилятору?Как работает компилятор в этом случае?Сначала он сканирует весь файл на предмет всех объявлений / определений, а затем пытается разыменовать функции / символы?

Ответы [ 4 ]

3 голосов
/ 12 июня 2011

Во-первых, если "если я создам две функции и одна из них ссылается на другую, будет ли она работать?"Вы имеете в виду что-то вроде этого:

int foo()
{
    return bar();
}

int bar()
{
    return 0;
}

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

В C всякий раз, когда вы используете идентификатор (и неважно, какойидентификатора: это может быть функция, переменная, тип и т. д.), это должно быть объявлено заранее.Различные модификаторы, которые вы можете добавить к любому идентификатору (как вы сказали, static, inline и все остальные) не влияют на это.

Не путайте объявление и определение .Объявление просто говорит компилятору, что имя существует;определение фактически сообщает компилятору, что это такое.

Например, это определение:

int bar() { return 4; }

Обратите внимание на то, что у него есть тело с (простым) кодом внутри.

Это соответствующее объявление:

int bar();

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

Итак, мой первый пример должен выглядеть такthis:

int foo();
int bar();

int foo()
{
    return bar();
}

int bar()
{
    return 0;
}

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

2 голосов
/ 12 июня 2011

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

Для функций просто предоставьте прототип выше, где он вызывается, и все будет хорошо.

int trol(int a, int b);

// use trol(int, int)

int trol(int a, int b) { }

Если у вас есть две функции, a и b, и они вызывают друг друга и определены в порядке: a, b, тогда вы должны предоставить прототип b выше определения a.Прототип a не требуется, потому что он определен выше, где он используется внутри b.Тогда у компилятора не будет проблем.

Еще один особый случай для функций - это то, что вы можете использовать функцию, не объявляя ее, и компилятор попытается вывести сигнатуру из вызова.Этот ответ объясняет это довольно хорошо, я думаю: Должны ли вы объявлять прототип функции в C?

Для struct s, вы можете использовать указатели до того, как они будут фактическиопределяется (но вы не можете получить доступ ни к одному из полей), предоставляя предварительное объявление:

struct s;

// use s*'s

struct s { };

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

1 голос
/ 12 июня 2011

По моему опыту, все в C должно быть написано с указанным "объектом", прежде чем ссылка будет сделана.Я не думаю, что это специфично для любого компилятора, но, возможно, есть такие, которых я не нашел.В принципе, все всегда должно быть:

Object Declaration
...
Object Reference
1 голос
/ 12 июня 2011

Это важно, потому что, если компилятор не знает, что это за функция - он попытается «угадать» (создать прототип int foo() по умолчанию с соответствующими параметрами), а если ваш вызов неверен - у вас будет несоответствия (ошибки сборки, неявное приведение типов и т. д.).

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

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

int foo(int a)
{
    b(a);
    b("hello", "kitty");
}

void b(int a, ...)
{

    printf("%d", a);
}

Но это - воля:

#include <stdio.h>
int foo(int a)
{
    return b(a);
}

int b(int a)
{

    return printf("%d", a);
}

(с предупреждением о неявном форвардном объявлении)

Итак, чтобы не иметь дело с порядком объектов в файле - используйте прототипирование (предварительные объявления), чтобы компилятор знал, что следует.

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