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

Почему этот указатель на работу работает без предупреждений или ошибок?

Зная, что этот вызов:

pow(4);

создаст это сообщение об ошибке:

 error: too few arguments to function ‘pow’

Я изучаю указатели на функции, и я удивился, увидев этот код ниже. Но почему?

#include<stdio.h>
#include<math.h>

void aux(double (*function)(), double n, double x);

int main(void)
{
    aux(pow, 4, 2); 

    aux(sqrt, 4, 0);

    return 0;
}

void aux(double (*function)(double), double n, double x)
{   
    if(x == 0)
        printf("\nsqrt(%.2f, %.2f): %f\n", n, x, (*function)(n));  
    else
        printf("\npow(%.2f, %.2f): %f\n", n, x, (*function)(n));  
}

Я скомпилировал, используя:

gcc -Wall -Wextra -pedantic -Wconversion -o test test.c -lm

Результат:

pow(4.00, 2.00): 16.000000

sqrt(4.00, 0.00): 2.000000

Если я изменил третий параметр первого вызова aux на 3, результат изменится на:

pow(4.00, 3.00): 64.000000

sqrt(4.00, 0.00): 2.000000

И еще один вопрос. Каков правильный способ объявления и использования указателей на функции в этом случае?

4b9b3361

Ответ 1

Это:

void aux(double (*function)(), double n, double x);

использует декларацию не-прототипа старого стиля для function. Пустые круглые скобки () означают, что функция принимает фиксированное, но неуказанное число и тип аргументов.

C по-прежнему разрешает такую ​​декларацию для обратной совместимости. Прототипы (объявления функций, указывающие типы параметров) были введены ANSI C в 1989 году. До этого было невозможно указать типы параметров в объявлении функции, и компиляторы не смогли проверить, прошел ли вызов правильное число и тип аргументов.

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

Правила совместимости типов функций немного сложны, если у вас есть прототип, а другой нет. Эти типы:

double(double)         /* function with one double parameter
                          returning double */
double(double, double) /* function with two double parameters
                          returning double */

несовместимы друг с другом, но оба они совместимы с этим типом:

double()   /* function with a fixed but unspecified number of parameters
              returning double */

что позволяет иметь неправильные вызовы без диагностики из компилятора.

Чтобы избежать этой проблемы, всегда используйте прототипы:

void aux(double (*function)(double, double), double n, double x);

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

Ответ 2

Ваш прототип функции aux()...

void aux(double (*function)(), double n, double x);

... указывает первый аргумент как указатель на функцию, возвращающую double и принимающую неуказанные аргументы. Это предотвращает передачу GCC предупреждений о несоответствующих типах для вызовов этой функции в main().

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

Ответ 3

Потому что вы указали прототип aux(), прежде чем main и function не имеют каких-либо типов аргументов. Узнайте разницу:

void f();   /* Accepts any number of arguments thanks to K&R C */
void g(void); /* No arguments accepted */
void h(int); /* Only one integer argument accepted */

Если вы объявите прототип aux() следующим образом:

void aux(double (*function)(double), double n, double x);

GCC начинает жаловаться.

Ответ 4

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