Как реализовать «частную / ограниченную» функцию в C? - PullRequest
28 голосов
/ 10 сентября 2009

Во время интервью на C мне был задан очень интересный вопрос: как реализовать функцию f () таким образом, чтобы ее можно было вызывать только из определенной функции g (). Если функция, отличная от g (), попытается вызвать f (), это приведет к ошибке компилятора.

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

У кого-нибудь есть идеи?

Ответы [ 6 ]

38 голосов
/ 10 сентября 2009

Вот один из способов:

int f_real_name(void)
{
    ...
}

#define f f_real_name
int g(void)
{
    // call f()
}
#undef f

// calling f() now won't work

Другой способ, если вы можете гарантировать, что f() и g() являются единственными функциями в файле, это объявить f() как static.

РЕДАКТИРОВАТЬ: еще один макрос трюк, чтобы вызвать ошибки компилятора:

static int f(void) // static works for other files
{
    ...
}

int g(void)
{
    // call f()
}
#define f call function

// f() certainly produces compiler errors here
27 голосов
/ 10 сентября 2009

Поместите g () и f () в один и тот же модуль и объявите f () как статический. Ключевое слово static делает f () доступным только для функций в том же модуле или в исходном файле.

Вы также можете упомянуть, что в модуле нельзя использовать другие методы с f () и g (), в противном случае они могут вызывать f ().

PS - Я действительно думаю, что ответ Криса Латца на самом деле лучший. В нем упоминается этот подход, но также и умное переименование макросов, которое работает с меньшим количеством условий среды (не требует файл модуля специально для этих двух функций).

Обратите внимание, что с макросом вы можете сделать следующее:

#define f() f_should_not_be_called_by_anything_except_g

Которое показало бы приятное сообщение об ошибке, а автозаполнители (такие как Visual Studio) показывали бы этот совет, когда пользователь вводит f ().

10 голосов
/ 10 сентября 2009

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

static void func(void)
{
    // ...
}

Тогда func() может быть вызван только другими функциями, определенными в том же файле (технически, тот же самый блок перевода : другие функции, определения которых включены в директиву #include, все еще могут получить к ней доступ ). Говорят, что func имеет внутреннюю связь . Говорят, что все другие функции (то есть без ключевого слова static) имеют внешнюю связь.

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

8 голосов
/ 10 сентября 2009

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

8 голосов
/ 10 сентября 2009

Поместите f() и g() в один и тот же исходный файл, объявив f() статическим.

2 голосов
/ 10 сентября 2009

Возможно только по совпадению.

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

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

#include <stdio.h>

extern void g(void);    /* in a header */

/* Other functions that may not call f() go here */

static void f(void)
{
    puts("X");
}

void g(void)
{
    f();
}

Очевидно, что этот метод нельзя надежно распространить на другую пару функций в том же файле - x () и y () - так что x () и только x () могут вызывать y (), тогда как g () и только g () может вызывать f () одновременно.

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

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