Функции внутри функций в C - PullRequest
28 голосов
/ 05 июня 2009

Я делаю код, похожий на этот:

#include <stdio.h>

double some_function( double x, double y)
{
  double inner_function(double x)
  { 
    // some code
    return x*x;
  }

  double z;
  z = inner_function(x);
  return z+y;

}

int main(void)
{
  printf("%f\n", some_function(2.0, 4.0));
  return 0;
}

Это прекрасно компилируется в GCC (без предупреждений), но не компилируется в ICC.

ICC дает:

main.c(16): error: expected a ";"
    { 
    ^

main.c(21): warning #12: parsing restarts here after previous syntax error
    double z;
            ^

main.c(22): error: identifier "z" is undefined
    z = inner_function(x);
    ^

compilation aborted for main.c (code 2)

Что я делаю не так?

Спасибо.

(edit) Извините за плохой пример. В моем исходном коде мне нужно сделать это. Я использую числовой интегратор GSL и у меня есть что-то вроде:

double stuff(double a, double b)
{
  struct parameters
  {
    double a, b;
  };

  double f(double x, void * params)
  {
    struct parameters p = (struct parameters *) params;
    double a = p->a, b = b->b;
    return some_expression_involving(a,b,x);
  }
  struct parameters par = {a,b};

  integrate(&f, &par);

}

И у меня много функций с такой структурой: они являются результатом интеграции функций с большим количеством внешних параметров. А функции, реализующие численное интегрирование, должны получить указатель на функцию типа:

double f(double x, void * par)

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

Ответы [ 10 ]

30 голосов
/ 05 июня 2009

Вложенные функции доступны как расширение языка в GCC , но они не являются частью стандартного языка, поэтому некоторые компиляторы их не допускают.

20 голосов
/ 06 июня 2009

Все остальные дали вам канонический ответ «Вложенные функции не разрешены в стандарте C» (поэтому любое их использование зависит от вашего компилятора).

Ваш исправленный пример:

double stuff(double a, double b)
{
  struct parameters
  {
    double a, b;
  };

  double f(double x, void * params)
  {
    struct parameters p = (struct parameters *) params;
    double a = p->a, b = b->b;
    return some_expression_involving(a,b,x);
  }
  struct parameters par = {a,b};

  return integrate(&f, &par);     // return added!
}

Поскольку вы говорите, что такие функции, как интегратор, необходимы

  double (*f)(double x, void * par);

Я не понимаю, зачем вам вообще нужны вложенные функции. Я ожидал бы написать:

struct parameters
{
    double a, b;
};

static double f(double x, void *params)
{
    struct parameters p = (struct parameters *) params;
    double a = p->a, b = b->b;
    return some_expression_involving(a,b,x);
}

double stuff(double a, double b)
{
    struct parameters par = { a, b };
    return integrate(f, &par);
}

Приведенный выше код должен работать в C89 (если только не возникает проблема с инициализацией 'par') или в C99; эта версия кода предназначена только для C99 с использованием составного литерала для параметра (раздел 6.5.2.5 Составные литералы):

double stuff(double a, double b)
{
    return integrate(f, &(struct parameters){a, b});
}

Скорее всего, у вас есть всего несколько вариаций типа "параметры структуры". Вам необходимо предоставить отдельные (достаточно) значимые имена для различных функций - у вас может быть только одна функция с именем 'f' на исходный файл.

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

8 голосов
/ 05 июня 2009

C не имеет вложенных функций. Вложенные функции GCC являются расширением языка.

Ваша ошибка времени выполнения в GCC является орфографической ошибкой inner_funcion должно быть inner_function.

6 голосов
/ 06 июня 2009

Как уже упоминалось во многих ответах выше, внутренние функции не поддерживаются в C, однако внутренние классы могут использоваться в C ++ для достижения аналогичной цели. К сожалению, их немного громоздко использовать, но это может подойти вам, если вы не против компилировать как C ++.

Не проверенный пример:

double some_function( double x, double y)
{
  struct InnerFuncs
  {
    double inner_function(double x)
    { 
      // some code
      return x*x;
    }
    // put more functions here if you wish...
  } inner;

  double z;
  z = inner.inner_function(x);
  return z+y; 
}

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

Лет спустя редактировать:

C ++ теперь позволяет нам использовать лямбда-выражения в качестве внутренних функций. В приведенном выше примере с моей игрушкой это будет выглядеть примерно так:

double some_function( double x, double y)
{
   auto inner_function = [&]() { return x * x; }

   double z;
   z = inner_function ();
   return z + y;
}

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

Подробнее здесь: Что такое лямбда-выражение в C ++ 11?

3 голосов
/ 05 июня 2009

Вы используете вложенные функции - C не поддерживает такие вещи.

1 голос
/ 06 июня 2009

Если вам интересно, как отключить расширения GCC и тому подобное.

Вы можете использовать флаг -ansi, который по существу устанавливает стандарт c89 и отключает функции GCC, несовместимые с ISO C90.

См. документы для получения дополнительной информации. Проверьте флаг -std тоже.

1 голос
/ 05 июня 2009

Вложенные функции не являются частью стандартов C. Поэтому нет гарантии, что он будет работать для всех компиляторов, и его определенно следует избегать.

1 голос
/ 05 июня 2009

определения локальных функций внутри функции недопустимы в C.

1 голос
/ 05 июня 2009

Это недопустимый код C, внутренние функции не поддерживаются в C

0 голосов
/ 05 июня 2009

Я не эксперт, но я готов поспорить, это либо явно не разрешено, либо не определено спецификацией C99, поэтому, вероятно, лучше держаться подальше от него.

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