C / C ++ нужны для прототипов локальных функций?

Есть ли какое-либо преимущество в явном создании прототипов локальных функций в C / C ++ вместо определения функций перед использованием? Под локальными я подразумеваю функции, используемые только в их исходном файле. Пример таков:

#include "header.h"
static float times2(float x){
return 2*x;
}

static float times6(float x){
return times2(3*x);
}

int main(void){

// Other stuff ...

float y = times6(1);

// Other stuff ...
}

По сравнению с этим:

#include "header.h"
// Local function prototypes
static float times2(float);
static float times6(float);

// Main
int main(void){

// Other stuff ...

float y = times6(1);

// Other stuff ...
}

// Local functions definition
static float times2(float x){
return 2*x;
}

static float times6(float x){
return times2(3*x);
}

Лично я предпочитаю использовать первый вариант, так как кода для написания меньше, и (для меня) файл легче читать, но теперь я задаюсь вопросом, есть ли какие-либо технические причины для предпочтения второго варианта.

РЕДАКТИРОВАТЬ: я добавил статический для times2 () и times6 (), см. ответ @Gangadhar и последующие комментарии.

3

Решение

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

Рассмотрим эти функции, которые не делают ничего особенно полезного:

int foo(int x)
{
if(x < 1) return x;
else return x + bar(x-1);
}

int bar(int x)
{
if(x < 3) return x;
return x * foo(x-1);
}

Если вы попытаетесь скомпилировать это, компилятор рассердится на вас:

ошибка: ‘bar’ не был объявлен в этой области

Вам нужно поместить отсутствующие прототипы перед функцией, используя ее:

int bar(int);
// as above unchanged

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

int foo(int);
int bar(int);
int foo(int);
int bar(int);

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

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

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

2

Другие решения

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

Если файл имеет десятки функций (и переменных), предшествующее локальное определение помогает организации. Можно пожелать сохранить такую ​​большую коллекцию в порядке (A-Z). Этот порядок может / не может хорошо работать с прямыми ссылками. Перечисляя все функции / переменные (глобальные в (* .h` файле) и локальные здесь (* .c), можно поддерживать организацию, даже если форвардные потребности могут измениться в течение жизни кода.

// function/variable prototypes
static float pi;
static float pi2;
static float times2(float);
static float times3(float);
static float times4(float);
static float times5(float);
static float times6(float);
static float times7(float);
static float times8(float);
static float times9(float);
3

Если вы можете избежать предварительного заявления, тогда ваш вопрос — основа мнения, и у меня нет предложений.

Но иногда вы не можете избежать предварительного объявления, например:

void f();

void g()  { /* ... */  f();  }

void g()  { /* ... */  g();  }
0

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

0

Когда две функции вызывают друг друга, то есть FuncA () вызывает FuncB () и наоборот, тогда любая из функций должна быть объявлена. Это условие, которое описал @M M (ответ выше).

Но в любых нормальных ситуациях объявление функции — это просто соглашение, которое отличается от разработчика к разработчику.

0
По вопросам рекламы [email protected]