Допустимо ли это сомнительное использование объявления функции, не являющегося прототипом? - PullRequest
3 голосов
/ 14 ноября 2010

Это допустимый код C (C99)?

int f();
int g(int x)
{
    if (x<0) return f(x);
    else return f(x,x);
}

Очевидно, что программа имеет неопределенное поведение, если g когда-либо вызывается с отрицательным аргументом и f не является функцией, которая принимает одинint аргумент, или если g вызывается с неотрицательным аргументом и f не является функцией, которая принимает два int аргумента.Но в противном случае?

Рассмотрим в качестве примера этот отдельный исходный файл, который вызывает g из вышеприведенного и предоставляет f:

int g();
#ifdef FOO
int f(int a, int b) { return a+b; }
int main() { return g(1); }
#else
int f(int a) { return a; }
int main() { return g(-1); }
#endif

Ответы [ 5 ]

4 голосов
/ 14 ноября 2010

Давайте спросим наоборот: Почему это не будет действительным? . Я действительно не могу найти аргумент или правило, которое запрещает приведенный выше код. Вызов функции в соответствующей другой ветке никогда не выполняется (хотя обсуждение в комментариях показывает, что это не так просто!).

3 голосов
/ 14 ноября 2010

C99 (6.5.2.2. Вызовы функций, пункт 8) говорит, что количество и типы параметров и аргументов «не сравниваются», если у определения функции нет прототипа.) используется в дикой природе с указателями на функции.Массив void (*)() содержал указатели функций void (*)(struct Client *) и void (*)(struct Client *, int parc, char *parv[]).Основываясь на индексе массива, код передает дополнительные параметры или нет.

В этом случае у компилятора нет (разумного) способа заранее проверить количество параметров, даже если он имеет весь соответствующий код.

Я думаю, что это грязный код, и я исправил этот конкретный экземпляр.

2 голосов
/ 15 ноября 2010

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

Существует еще один, более простой, способ прийти к вашему выводу о компоновщике:Поскольку это разрешено:

int f();
int (*fp)() = f;

Компоновщик должен иметь возможность найти адрес f(), не зная его фактического определения.Поэтому его символ должен быть определен без знания фактического определения.

0 голосов
/ 14 ноября 2010

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

В принципе, если f принимает один или нет аргументов, я бы не ожидал проблем.
Если f принимает два или более аргументов, можно ожидать, что эти другие аргументы (кроме первого) будут иметь нежелательные (очевидно, случайные) значения.

Рассмотрим этот кусок кода:

int f(int x, int y);
int g(int x)
{
   int k; //No value
   if (x<0) return f(x, k);
   else return f(x, x);
}

Конечно, это плохая идея. Вы должны предпочесть явно объявить все аргументы.

Вы также можете использовать int f(void);, чтобы явно объявить, что f не принимает аргументов.

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

0 голосов
/ 14 ноября 2010

Что, если это f(int x, ...) и он смотрит на знак своего первого аргумента, чтобы узнать, сколько (0 или 1) вараггов он получил?

...