Объясните пожалуйста как работает прога - PullRequest
7 голосов
/ 05 июля 2010
#include<stdio.h>
int f();

int main()
{

    f(1);
    f(1,2);
    f(1,2,3);
}

f(int i,int j,int k)
{

    printf("%d %d %d",i,j,k);

}

он работает нормально (без ошибок) ... Можете ли вы объяснить, как он работает?как f (1) и f (1,2) связаны с f (int, int, int)?

Ответы [ 6 ]

14 голосов
/ 05 июля 2010

У вас должно быть другое определение слова "ошибка" :-) Что печатается в первые два раза, когда вы вызываете функцию f? Я получаю

1 -1216175936 134513787
1  2          134513787
1  2          3

для моих трех вызовов функций.

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

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

Это прекрасно компилируется, хотя и очень неразумно, C. И я имею в виду, что в очень реальном, «неопределенном поведении» смысл слова (в частности, относится к C99: «Если выражение, обозначающее вызываемую функцию, имеет тип который не включает прототип, ... если количество аргументов не равно количеству параметров, поведение не определено ").

Вы действительно должны предоставить полностью сформированные прототипы функций, такие как:

void f(int,int,int);

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


Кроме того, под покровом обычно происходит то, что вызывающая функция начинается со стека, подобного:

12345678
11111111

и помещает (например) два значения в стек так, чтобы оно заканчивалось следующим образом:

12345678
11111111
2
1

Когда вызываемая функция использует первые три значения в стеке (поскольку это то, что она хочет), она обнаруживает, что она имеет 1, 2 и 11111111.

Он делает то, что должен делать, затем возвращает, и вызывающая функция удаляет эти два значения из стека (это называется стратегией «вызывающий делает добро»). Горе всем, кто пытается сделать это с помощью стратегии «вызывающий-делает-хорошо» :-), хотя в C это довольно необычно, поскольку делает функции с переменными аргументами, такие как printf, немного сложными для выполнения.

4 голосов
/ 05 июля 2010

Это объявление:

int f();

... сообщает компилятору "f - это функция, которая принимает некоторое фиксированное количество аргументов и возвращает int". Затем вы пытаетесь вызвать его с одним, двумя и тремя аргументами - компиляторы C концептуально являются однопроходными (после предварительной обработки), поэтому на данный момент у компилятора нет информации, чтобы с вами поспорить.

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

3 голосов
/ 05 июля 2010
int f();

В C это объявляет функцию, которая принимает переменное число аргументов, т.е. это эквивалентно следующему в C ++

int f(...);

Для проверки используйте следующее вместо int f();

int f(void);

Это заставит компилятор жаловаться.

Обратите внимание: здесь также задействована причуда компоновщика C ... компоновщик C не проверяет аргументы, передаваемые функции в момент вызова, и просто ссылается на первый открытый символ с тем же именем. Таким образом, использование функции f () в main разрешено из-за объявления int f(). Но компоновщик связывает функцию f (int, int, int) в течение времени ссылки на сайтах вызовов. Надеюсь, что это имеет смысл (пожалуйста, дайте мне знать, если это не так)

1 голос
/ 05 июля 2010

Работает нормально, поскольку int f() означает то, что уже сказал другой ответ: это означает неопределенное количество аргументов.Это означает, что вы можете вызывать его с нужным вам числом аргументов (также более 3), при этом компилятор ничего не говорит об этом.помещается в стек, а затем вызывается «из» стека в функции f.Если вы передаете 0 аргументов, i, j, k функции «соответствует» значениям в стеке, которые из функции PoV являются мусором.Тем не менее, вы можете получить доступ к их значениям.Если вы передаете 1 аргумент, один из трех i j k получает доступ к значению, остальные получают мусор.И т. Д.

Обратите внимание, что те же рассуждения работают, если аргументы передаются каким-либо другим способом, но в любом случае это соглашение используется.Другим важным аспектом этих соглашений является то, что вызываемый объект не отвечает за настройку стека;это зависит от вызывающей стороны, которая знает, сколько аргументов выдвигается по-настоящему.Если бы это было не так, определение f могло бы предложить, что он должен «настроить» стек, чтобы «выпустить» три целых числа, и это вызовет какой-то сбой.

Что вы 'Написано хорошо для текущего стандарта (на компиляции gcc без предупреждений даже с -std=c99 -pedantic; есть предупреждение, но речь идет о пропущенном int перед определением f), хотя многим это кажется отвратительными назовите это «устаревшей чертой».Несомненно, ваше использование в примере кода не показывает никакой полезности, и, вероятно, это может помочь устранению ошибок более обязательному использованию прототипов!(Но все же я предпочитаю C аде)

add

Более «полезное» использование «функции», которая не вызывает проблему «неопределенного поведения», может быть

#include<stdio.h>
int f();

int main()
{

    f(1);
    f(2,2);
    f(3,2,3);
}

int f(int i,int j,int k)
{
  if ( i == 1 ) printf("%d\n", i);
  if ( i == 2 ) printf("%d %d\n", i, j);
  if ( i == 3 ) printf("%d %d %d\n", i, j, k);
}
0 голосов
/ 05 июля 2010

В C объявление должно объявлять как минимум тип возвращаемого значения.Поэтому

int f();

объявляет функцию, которая возвращает тип int.Это объявление не содержит никакой информации о параметрах, которые принимает функция.Определение функции:

f(int i,int j,int k)
{

    printf("%d %d %d",i,j,k);
}

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

Чтобы предотвратить эти ошибки, вы должны использовать соответствующие прототипы функций, такие как

f(int,int,int);           //in your case
f(void);                  //if you have no parameters
0 голосов
/ 05 июля 2010

Когда вы компилируете ту же программу с помощью компилятора g ++, вы видите следующие ошибки -

g++ program.c
program.c: In function `int main()':
program.c:2: error: too many arguments to function `int f()'
program.c:6: error: at this point in file
program.c:2: error: too many arguments to function `int f()'
program.c:7: error: at this point in file
program.c:2: error: too many arguments to function `int f()'
program.c:8: error: at this point in file
program.c: At global scope:
program.c:12: error: ISO C++ forbids declaration of `f' with no type

Использование gcc с опцией -std = c99 просто выдает предупреждение

Компилируйте ту же программутот же стандарт, который g ++ имеет по умолчанию, выдает следующее сообщение:

gcc program.c -std=c++98
cc1: warning: command line option "-std=c++98" is valid for C++/ObjC++ but not for C

Тогда я отвечу, что компиляторы c соответствуют другому стандарту, который не столь ограничителен, как тот, которому соответствует c ++.

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