Объявление функции старого стиля C - PullRequest
15 голосов
/ 03 января 2011

Вот простая функция, удаленная и определенная с использованием синтаксиса старого стиля:

#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;
}

Она может быть скомпилирована и правильно запущена для печати:

[ОШИБКА 110]: время соединенияout.

Я читал, что у этого стиля нет ассоциированного прототипа, но как он может автоматически конвертировать int в char * во время выполнения, и даже если предоставленных аргументов меньше, чем объявлено?

Ответы [ 4 ]

17 голосов
/ 03 января 2011

По сути, это работает, потому что слишком глуп, чтобы знать лучше. Старомодный K & R C практически ничего не проверяет. Вам это сойдет с рук, потому что,

  1. бывает, что sizeof(int) == sizeof(char *) на конкретной комбинации архитектуры и компилятора, которую вы используете. Это на самом деле ничего не конвертирует, просто показывает, что 32 бита - это 32 бита.

  2. Когда вы помещаете все эти аргументы в стек, он просто помещает их внутрь. Когда printf использует их, он просто использует их, если нужно, и оставляет остальные в покое; затем они исчезают, когда звонок возвращается, и никто не становится мудрее. Однако, если вам когда-нибудь удастся попытаться напечатать семь значений, в которых вы передали только шесть аргументов, во время выполнения это взорвется, иногда творческим и неожиданным образом.

9 голосов
/ 03 января 2011

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

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. Это больше неопределенное поведение. Но это работает на вашей машине; char * и int, вероятно, имеют одинаковый размер.

error также считывает 5 значений мусора char * из стека. Затем он передает их в fprintf, который игнорирует, поскольку существует только два спецификатора преобразования.

0 голосов
/ 25 мая 2014

На самом деле происходит преобразование 110 в int, это преобразование выполняется fprintf, когда fprint читает "% d", он пытается преобразовать соответствующий параметр в int. Еще один момент заключается в том, что функция ожидает указатели, то есть адрес памяти и указатели являются целыми числами. Если вместо 110 была передана строка, все равно будет напечатан номер, адрес строки во время выполнения.

0 голосов
/ 03 января 2011

Фактически, компилятор фактически имеет прототип в области видимости, если он сталкивается с определением функции error() до того, как столкнется с ее использованием (вот почему старые программисты на С часто упорядочивают определения функций в файлах в соответствии с их порядком использования).Следовательно, 110 может быть преобразовано в (char*)110 (не то, чтобы это имело значение на машине с sizeof(int) == sizeof(char*)).Было бы интересно посмотреть, что произойдет на машине, где sizeof(int) != sizeof(char*).

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