Объявление функции стиля Старого стиля
Здесь простая функция, делятся и определяются с помощью синтаксиса старого стиля:
#include <stdio.h>
void
error(message,a1,a2,a3,a4,a5,a6,a7)
char *message;
char *a1,*a2,*a3,*a4,*a5,*a6,*a7;
{
fprintf(stderr,message,a1,a2,a3,a4,a5,a6,a7);
}
int main ()
{
error("[ERROR %d]: %s.\n",110,"Connection timed out");
return 0;
}
Он может быть скомпилирован и корректно выполнен для печати:
[ERROR 110]: время ожидания подключения.
Я читал, что этот стиль не имеет связанного прототипа, но как он может автоматически преобразовывать int в char * во время выполнения, и даже предоставленные аргументы меньше, чем объявлено?
Ответы
Ответ 1
В принципе, это работает, потому что это слишком глупо, чтобы лучше знать. Старомодный K & R C в основном ничего не проверяет. Вы избегаете этого, потому что
-
бывает, что sizeof(int) == sizeof(char *)
в конкретной комбинации архитектуры и компилятора вы используете. Это на самом деле ничего не преобразует, просто цифры 32 бит - 32 бита.
-
Когда вы помещаете все эти аргументы в стек, они просто вставляют их. Когда printf использует их, он просто использует те, которые нужны, и оставляет остальных в покое; они затем исчезают, когда вызов возвращается, и никто не мудрее. Однако, если вам когда-нибудь удастся напечатать семь значений, в которых вы прошли только шесть аргументов, они будут взорваться во время выполнения, когда-нибудь творчески и неожиданно.
Ответ 2
Передача слишком мало аргументов или неправильный тип (вы сделали оба), вызывает поведение undefined. Именно поэтому вы никогда не должны использовать синтаксис старого стиля в новом коде. Если вы использовали новый синтаксис, вы получите "свободный" прототип из определения функции. Другими словами:
void
error(char * message,
char * a1, char * a2, char * a3, char * a4, char * a5, char * a6, char * a7)
{
}
также является прототипом.
Используя старый синтаксис, вы должны предоставить свой собственный, которого у вас нет. Это означает, что компилятор не может проверить вызовы.
На практике (на вашем компьютере) error
считывает int
из стека в char *
. Затем он проходит от char *
до fprintf
. Но используется спецификатор %d
, поэтому fprintf
выводит его как int
. Это поведение undefined. Но это работает на вашей машине; char *
и int
, вероятно, имеют одинаковый размер.
error
также считывает 5 значений мусора char *
из стека. Затем он передает их на fprintf
, который игнорируется, поскольку существуют только два спецификатора преобразования.
Ответ 3
Собственно, у компилятора действительно есть прототип в области видимости, если он сталкивается с определением функции error()
перед тем, как столкнуться с его использованием (поэтому старые программисты C часто заказывают определения функций в файлах в соответствии с их порядком использования). Следовательно, 110
может быть преобразован в (char*)110
(не то, что это имело бы значение на машине, где sizeof(int) == sizeof(char*)
). Было бы интересно посмотреть, что произойдет на машине, где sizeof(int) != sizeof(char*)
.
Ответ 4
На самом деле происходит преобразование 110 в int, это преобразование выполняется с помощью fprintf, когда fprint читает "% d", он пытается преобразовать соответствующий параметр в int. Другим моментом является то, что указатели ожидания функции, то есть адрес памяти и указатели являются целыми числами. Если строка была передана вместо 110, все равно будет напечатан номер, адрес строки во время выполнения.