Подтвердить что ты не робот

Объявление функции стиля Старого стиля

Здесь простая функция, делятся и определяются с помощью синтаксиса старого стиля:

#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 * во время выполнения, и даже предоставленные аргументы меньше, чем объявлено?

4b9b3361

Ответ 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, все равно будет напечатан номер, адрес строки во время выполнения.