Функции различной арности, вызываемые через указатели на функции неопределенных аргументов - PullRequest
4 голосов
/ 20 января 2011

Я на 99% убежден, что это законно после рассмотрения стандарта C и нескольких публикаций comp.lang.c, но я надеюсь, что кто-то может предоставить точный язык в стандарте, который разрешает (или запрещает) этот случай:

#include <stdio.h>

double id (double x) { return x; }
double add2 (double x, double y) { return x + y; }
double add3 (double x, double y, double z) { return x + y + z; }

typedef double (*fp) ();
static fp funcs[] = { id, add2, add3 };

int main (void)
{
    printf("id(5.3) = %f\n", funcs[0](5.3));
    printf("add2(5.3, 6.1) = %f\n", funcs[1](5.3, 6.1));
    printf("add3(5.3, 6.1, 7.2) = %f\n", funcs[2](5.3, 6.1, 7.2));

    return 0;
}

Приведенный пример дал ожидаемые результаты для меня под MinGW gcc 4.4.0 с использованием -Wall -pedantic -ansi.

Вопросы:

  • Мне известно, что вызовы функций с неопределенными параметрами неявно продвигают интегральные аргументы в соответствии с правилами интегрального продвижения и float аргументами double. Изменится ли это поведение каким-либо образом при вызове через указатель на функцию с неопределенными параметрами? (Я не понимаю, почему это так.)
  • Я сталкивался с несколькими сообщениями, подразумевающими, что вызовы функций со спецификаторами ... (например, double uhoh (double x, ...)) не разрешены через указатели на функции с неопределенными параметрами. Это имеет смысл с точки зрения реализации, но я не смог определить пункт в стандарте, который запрещает это.

Ответы [ 3 ]

3 голосов
/ 20 января 2011

Сначала я могу предложить стандартный синтаксис вызова функции:

void foo(int);
foo(3);

определяется как foo, затухающий в указателе функции, затем вызываемый; поэтому я не понимаю, почему будет какая-то разница между вашей ситуацией и "стандартным" вызовом функции.

Во-вторых, n1256 6.5.2.2 детализирует семантику вызова функции, и она выглядит довольно четко определенной для указателя функции без прототипа, если число и тип аргументов соответствуют количеству и типу параметров.

Пустой список параметров несовместим со списком переменных параметров в соответствии с n1256 6.7.5.3p15:

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

Это означает, что является нарушением ограничения (6.5.16.1p1) назначать указатель переменной функции на указатель на функцию без прототипа, поскольку вы назначаете указатели несовместимым типам.

Я бы предположил, что приведение указателя переменной функции к непрототипированному указателю функции также является UB и, возможно, CV; но я не проверял.

2 голосов
/ 20 января 2011

Вы правы в том, что эта программа имеет совершенно определенное поведение, если аргументы при выполнении вызова имеют число и типы, совпадающие с теми, которые указаны в определении вызываемой функции. См. Пункт 6.5.2.2, пункт 6. Мой связанный вопрос также может представлять интерес:

Допустимо ли это сомнительное использование объявления функции, не являющегося прототипом?

1 голос
/ 20 января 2011

Код четко определен, если все типы параметров представляются самим себе, а все предоставленные аргументы имеют правильный тип.

Если вы хотите получить некоторую меру безопасности типов, вы можете использовать явныйброски или союзы, например

#include <stdio.h>

static double id(double x) { return x; }
static double add2(double x, double y) { return x + y; }
static double add3(double x, double y, double z) { return x + y + z; }

union func
{
    double (*as_unary)(double x);
    double (*as_binary)(double x, double y);
    double (*as_ternary)(double x, double y, double z);
};

static const union func funcs[] = {
    { .as_unary = id },
    { .as_binary = add2 },
    { .as_ternary = add3 }
};

int main(void)
{
    printf("id(5.3) = %f\n", funcs[0].as_unary(5.3));
    printf("add2(5.3, 6.1) = %f\n", funcs[1].as_binary(5.3, 6.1));
    printf("add3(5.3, 6.1, 7.2) = %f\n", funcs[2].as_ternary(5.3, 6.1, 7.2));

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