C Объем прототипа - PullRequest
3 голосов
/ 15 ноября 2009

Я узнал, что

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

См. Программу на C, указанную ниже.

void fn (struct st {int a;} a, struct st b) ;

struct st obj ;

Компиляторы немедленно выдают ошибку, поскольку размер 'obj' неизвестен (или) struct st не является 'типом'. Вот так! объявление структуры struct st заканчивается на объявлении прототипа.

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

void fn (int a) ;
int a ;

Итак, чтобы разрешить вышеуказанные объявления, объем прототипа ограничен. (Поправьте меня если я ошибаюсь)

Но для объявления прототипа имя переменной параметра бесполезно. Итак, почему он «узко ограничен»? Каково значение наличия имени переменной параметра? Что думают разработчики языка (или) спецификации по этому поводу?

Ответы [ 4 ]

4 голосов
/ 15 ноября 2009

Имена параметров могут помочь документировать использование параметров.

Рассмотрим функцию настройки памяти:

void mem_set(void *, int, int);

void mem_set(void *buffer, int value, int nbytes);

Что легче понять?


Объявление типа структуры, как написано, является локальным для прототипа функции. Как вы, вероятно, знаете (сейчас, если не раньше), вам нужно определить тип вне области действия прототипа, чтобы успешно использовать его. То есть вы должны написать:

struct st {int a;};
void fn(struct st a, struct st b);

Вы говорите:

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

void fn(int a);
int a;

Итак, чтобы разрешить вышеуказанные объявления, объем прототипа ограничен. (Поправьте меня, если я ошибаюсь)

Есть вероятность, что GCC с '-Wshadow' предупредит о параметре 'a', скрывающем глобальную переменную 'a' - это, безусловно, будет сделано в определении функции и может произойти в объявлении функции. Но это не обязательное предупреждение; код, как написано, является законным C - хотя и немного сомнительным из-за затенения.


В комментариях идет длительная дискуссия о том, «почему C ограничивает (мешает) вам объявить тип в списке параметров« с подтекстом », потому что C ++ позволяет это делать»:

Комментарии

Поскольку разрешено использование / ** /, программист должен быть обязан (в соответствии с практикой кодирования) добавлять надлежащие комментарии об использовании языка там. Я считаю, что должно быть «что-то» кроме оказания помощи комментариям. - Ганеш Гопаласубраманян

ОК - верь прочь. Совместимость с тем, что делал C ++, была остальной причиной, и имена аргументов были добавлены туда для улучшения читабельности. См. Страуструп «Дизайн и эволюция C ++». Обратите внимание, что имена параметров в прототипе не являются частью интерфейса - см. Обсуждение предоставления аргументов по имени вместо позиции. - Джонатан Леффлер

Я полагаю, что вопрос, который задает ОП: «какой смысл вообще иметь область действия прототипа функции?». Вы отвечаете, к сожалению, не проливает свет на это. Честно говоря, я тоже понятия не имею. Если бы они просто хотели ограничить область объявления именованных параметров (в объявлении без определения) в качестве догадок OP, они могли бы сделать это, не вводя область действия (как, например, в C ++). - AndreyT

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

@ Джонатан Леффлер: Похоже, вы объясняете, почему были разрешены имена параметров («совместимость с C ++» - ОК). То, что я хотел бы знать, - это обоснование для введения «области действия прототипа функции». Почему они увидели необходимость ввести такую ​​область? С ++ так не делает. В C ++ отсутствует область действия прототипа функции. - AndreyT

@ AndreyT Да! Мы оба тонем в одной лодке :) - Ганеш Гопаласубраманян

Встречный пример

Это показывает, что C ++ «делает это так».

#include <cstdio>
using namespace std;

extern void x(struct c {int y;} b);

void x(struct c b)
{
    printf("b.y = %d\n", b.y);
}

int main()
{
    struct c a;
    a.y = 0;
    x(a);
    return(0);
}

Этот код не компилируется с G ++ (4.0.1 в MacOS X 10.5.8). Жалуется:

$ g++ -o xx xx.cpp
xx.cpp:4: error: types may not be defined in parameter types
$

Ошибка возникает на уровнях предупреждения / ошибки по умолчанию, а также на педантичных уровнях.

Так оно и естькажется справедливым и точным сказать «С ведет себя так же, как С ++» в этом контексте. Можете ли вы продемонстрировать на примере встречного счетчика, как можно определить тип в прототипе функции в C ++, указав, какой компилятор и платформа C ++ это позволяет?

2 голосов
/ 15 ноября 2009

Я бы повторил то, что Кришаррис и Джонатан Леффлер сказал. Наличие идентификаторов, ограниченных прототипом, полезно. Полезнее, чем не иметь их. Кроме того, он позволяет прототипам следовать той же грамматике, что и объявления функций, что, вероятно, хорошо с точки зрения разработки языка, а также с точки зрения разработчика.

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

Тот факт, что вы можете объявить что-то в прототипе, что бесполезно вне прототипа, является недостатком (я полагаю), но это довольно безопасно. Исправление «не делай этого». Мне не известно о каких-либо скрытых проблемах, когда вы позволяете вам объявить что-то, что вы не можете использовать вне прототипа.

Если мы собираемся решить эту проблему, мы должны также исправить тот факт, что C позволяет вам объявить что-то вроде:

struct {
    int obj;
};

Который объявляет менее чем полезный тип структуры.

1 голос
/ 22 сентября 2010

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

int f(void *);

тогда передача любого типа указателя действительна. Но если вы прототипируете функцию как:

int f(struct dummy *);

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

Будет ли это полезно в реальном мире, и будет ли это красиво или ужасно безобразно, я оставлю темы на следующий день.

0 голосов
/ 15 ноября 2009

Два - давайте сделаем это три - очевидные причины наличия имен параметров в прототипах функций:

  • Имена указывают назначение каждого параметра.
  • Имена позволяют более легко ссылаться на параметры в документации API (например, в Doxygen).
  • Вы можете легче создавать прототипы (вырезать и вставлять, либо автоматизировать) из определений.
...