LINUX.ORG.RU

[C] void test() {} - По стантарту при вызове аргументы не проверяются. Почему?

 


0

2

Здравствуйте.

Недавно я узнал, что если объявить функцию так:

void test() {}
то ее можно вызывать с любыми аргументами, компилятор это никак не контролирует, даже варнинга не выдаст. (пруф: http://ideone.com/SoIwr )

Чтобы компилятор контролировал аргументы при вызове, нужно объявлять так:

void test(void) {}
Вопрос: зачем это было сделано? Я не могу придумать ни одного примера, где эту особенность можно использовать; я вижу только возможность сделать ошибку. Ведь ясно же, что если программист передает аргументы в функцию, которая не принимает аргументов, то этот программист делает какую-то фигню.

Так зачем это было сделано, кто-нибудь знает? Подскажите =)

Можно предположить, что для совместимости с достандартным С (который K&R C), в котором прототипов функций просто не было. А с помощью препроцессора можно получить код, который бы компилировался всеми компиляторами.

Begemoth ★★★★★
()

Думаю, это по историческим причинам. Когда-то в Си вообще не проверялись типы аргументов, а прототип функции писался «тип имя();». Потом эта форма стала обозначать «функция, принимающая любой число любых параметров» для сохранения совместимости с существующим кодом.

tailgunner ★★★★★
()
Ответ на: комментарий от x0r

Я писал:

Ведь ясно же, что если программист передает аргументы в функцию, которая не принимает аргументов, то этот программист делает какую-то фигню.

Или нет, не ясно?

Ну вот простейший пример http://ideone.com/4cODb

dimonomid
() автор топика
Ответ на: комментарий от tailgunner

tailgunner, о как.

Ну ладно, с прототипом еще можно понять, но ведь когда объявляется сама функция-то, то при ее объявлении ведь нужно перечислить аргументы, чтобы к ним можно было обращаться, так?

dimonomid
() автор топика
Ответ на: комментарий от note173

Ясно.

Лана, всем спасибо.

В общем, ясно: это просто для обратной совместимости, использовать это нельзя.

dimonomid
() автор топика
Ответ на: комментарий от dimonomid

А, нет, было вообще вот так:

int func() /*no parameters inside parentheses*/
int a,b; /*instead, parameters were declared here*/
float f; /*another  parameter*/
{
/*…function body*/
}

note173 ★★★★★
()

меня еще забавляет как разные компиляторы пережевывают повторное определение переменных, типа вот такое

int main() {
    for (int i;;) {
        // do smth
    }
    for (int i;;) {
        // do smth else
    }
}

dismal_faun ★★
()
Ответ на: комментарий от Eddy_Em

И вообще-то функция не может принимать только неименованные параметры. Должен быть как минимум один именованный для этого.

dimonomid
() автор топика
Ответ на: комментарий от Deleted

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

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от dismal_faun

А в чем проблема? Две разных блока кода - разная область видимости.

ANSI C не позволяет так определять переменные, но в C99 - запросто.

ono
()
Ответ на: комментарий от ono

В MS Visual C++ 6.0 одно определение в for-е объявляло переменную с областью видимости на всю функцию, второе, соответственно, вызывало ошибку. Что надо было курить, чтобы сделать такое поведение, я не знаю.

Legioner ★★★★★
()
Ответ на: комментарий от Legioner
#include <stdio.h>
int main() {
    do { 
        int i = 0;          // start loop
        while (i < 4) {     // condition
                            // start body
            printf("%d\n", i);
                            // end body
            i++;            // next
        }
    } while (0);
    do {
        int i = 4;
        while (i > 0) {
            printf("%d\n", i);
            i--;
        }
    } while (0);
}

возможно создатели C99 думали про for таким образом

dismal_faun ★★
()

я вижу только возможность сделать ошибку.

Вы так говорите, будто только что узнали, что Си — это персональный набор граблей, некоторые из которых заботливо присыпаны листьями.

buddhist ★★★★★
()
Ответ на: комментарий от Legioner

Обычное поведение ранних компиляторов Си++, в которых и появилась возможность объявлять переменные в for.

tailgunner ★★★★★
()
Ответ на: комментарий от dimonomid

А некоторые так вообще указатели забывают удалять. Или проверять. Давайте указатели тоже выпилим, ведь человек ошибку может совершить!

anonymous
()
Ответ на: комментарий от anonymous

я таких модных конструкций не разумею, ибо дрых на лекциях по сишке :3

dismal_faun ★★
()

еще интересно чем упарывались создатели перла (в плане аргументов функций), наверно из-за этого интерес к сему языку неуклонно падает

dismal_faun ★★
()

потому что в си первая конструкция равносильна

void test(...) {}

а в плюсах

void test(void) {}

а теперь зачем. потому что в сях в старинных было нормалным вызывать функцию нигде не определенную. тогда компилятор счетает что все аргументы - это аргументы типа int (или long?) т.е. это практически любая функция. поэтому можно было скомпилить код не имея .h и он каг-бе работал. ну и то же самое про кол-во аргументов...получается, любую функцию, ну скажем, malloc() можно было объявить так: void* malloc(); или же вообще не объявлять.

(man gcc => -Wmissing-prototypes)

в плюсах решили что это зло. исправили.

mmarkk
()
Ответ на: комментарий от dismal_faun

>еще интересно чем упарывались создатели перла (в плане аргументов функций), наверно из-за этого интерес к сему языку неуклонно падает

Возможностью вызывать функции с разным количеством аргументов.

AptGet ★★★
()
Ответ на: комментарий от Deleted

> Разве обязательно?

Да. Пустой список параметров не гарантирует, что ты можешь передать любые параметры, а просто выключает проверку аргументов. Хочешь передать неизвестное кол-во - используй «…».

LamerOk ★★★★★
()
Ответ на: комментарий от mmarkk

> тогда компилятор счетает что все аргументы - это аргументы типа int (или long?)

Тоже 4.2 Перепутал с типом возвращаемого значения.

LamerOk ★★★★★
()
Ответ на: комментарий от tailgunner

> А чему она на самом деле эквивалентна?

Смотря где. В объявлении функции —

просто выключает проверку аргументов.

[C] void test() {} - По стантарту при вызове аргументы не проверяются. Почему? (комментарий)

В определении означает, что у функции аргументов нет (К.О.).

LamerOk ★★★★★
()
Ответ на: комментарий от LamerOk

>> А чему она на самом деле эквивалентна?

Смотря где. В объявлении функции —— просто выключает проверку аргументов.

Так же, как и эллипсис. Я не понял, к чему именно ты придрался.

tailgunner ★★★★★
()
Ответ на: комментарий от LamerOk

посмотри ассемблерный листинг от программы (не соберется, но скомпилится):

int myprintf();

int main() {
     myprintf("qwe %c %d %p\n", 'a', 123, main);
}

movl	$main, 12(%esp)
movl	$123, 8(%esp)
movl	$97, 4(%esp)
movl	$.LC0, (%esp)
call	myprintf
и сверни свой 4.2 в трубочку, засунь сам знаешь куда и проверни до щелчка.

mmarkk
()
Ответ на: комментарий от tailgunner

Я немного разобрался. еллипсис отличается от пустых скобок тем что типа компилятор при наличии ... может использовать специальный способ передачи аргументов. а пустые скобки - означает произвольное число аргументов передаваемых обычным путем. Так что это немного не эквивалент. Как показал тест GCC использует одинаковый метод вызова....

А ещё, при ... (ISO C requires a named argument before ‘...’), а при пустых скобках — не требуется..

Но по большому счёту, это риальне одно и то же. поэтому надо ввести бан за разбрасывание обвинений в 4.2

mmarkk
()
Ответ на: комментарий от LamerOk

Для особо одарённых:

4.2:

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

Так вот именно ТЫ разводишь нездоровую дискуссию. так что это твоё сообщение про 4.2 является 4.2. Иименно ТЫ безаппеляционно сообщаешь вызывающе неверные факты о неэквивалентности и др.

mmarkk
()
Ответ на: комментарий от mmarkk

Сходи на птичий рынок и купи себе мозгов. Хотя бы куриных. Потом попытайся понять, где именно ты протупил по второму разу. Пока будешь гулять, можешь помедитировать над следующими строчками из собственного листинга:

movl	$main, 12(%esp)
...
movl	$.LC0, (%esp)
LamerOk ★★★★★
()
Ответ на: комментарий от tailgunner

> Так же, как и эллипсис.

Ну, вообще да. С точки зрения компилятора, а не программиста, разумеется.

Я не понял, к чему именно ты придрался.

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

LamerOk ★★★★★
()
Ответ на: комментарий от LamerOk

> в определении функции эти выражения не будут эквивалентны. (К.О., дубль 2)

Капитан, объясните, в чем неэквивалентность «void f() {}» и «void f(...) {}».

tailgunner ★★★★★
()
Ответ на: комментарий от LamerOk

Ну что же, ЛамерОк, расскажи-ка что же ты там увидал, а то все ходишь тут излучаешь лучи 4.2. а то пока кроме высера и копипаста я от тебя ничего не увидел. Ни ссылки на доки, ни объяснения твоей точки зрения.

mmarkk
()
Ответ на: комментарий от tailgunner

> объясните

О_о

в чем неэквивалентность «void f() {}» и «void f(...) {}».

Можно, я просто процитирую стандарт?

6.9.1

8 If a function that accepts a variable number of arguments is defined without a parameter type list that ends with the ellipsis notation, the behavior is undefined.

Я чувствую под ногами мостик, а клавиши превращаются в ручки штурвала...

LamerOk ★★★★★
()
Ответ на: комментарий от mmarkk

> Ни ссылки на доки, ни объяснения твоей точки зрения.

Малыш, запоминай урок 1-ый: бремя доказательства лежит на утверждающем. Видишь, tailgunner задаёт мне вопросы (о, боги!) по моему утверждению, и я на них отвечаю.

Ты выдвинул утвреждение:

тогда компилятор счетает что все аргументы - это аргументы типа int (или long?)

Ну так докажи его. Когда докажешь, тогда и поговорим.

LamerOk ★★★★★
()
Ответ на: комментарий от Legioner

> В MS Visual C++ 6.0 одно определение в for-е объявляло переменную с областью видимости на всю функцию, второе, соответственно, вызывало ошибку. Что надо было курить, чтобы сделать такое поведение, я не знаю.

Вообще-то такое поведение предложил господин Страуструп, но позже под давлением общественности оно было пересмотрено в пользу нынешнего.

anonymous
()

с помощью такой конструкции работает open - как-то так: header

int open();

open.c:

int open(char *name, int mode, int perms)
{
    /* Blah, blah, blah... */
    if (mode & O_RW) {
        if (chmod(name, perms)) {
            /* Cleanup */
            return -1;
        }
    }
    return 0;
}

Естественно это не реальный код, но смысл ясен :).

rymis ★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.