2016-12-09 1 views
3

Существует ли какой-либо автоматический метод или инструмент для анализа кода C, чтобы определить, логически ли все вызовы функций инициализатора связаны с соответствующим вызовом функции finalizer в области блока ?Убедитесь, что инициализаторы и финализаторы гарантированно логически спарены в области объема

С помощью инициализатора и финализатора я имею в виду пару, такую ​​как fopen() и fclose().


Значительное редактирование к изначальному вопросу состоялось: Фраза «код путь» был удален и фразы «логически парные» и «в блоке сферы» были добавлены в попытке прояснить первоначальное намерение , См. Подробное обсуждение ответа Юджина Ш. Под «логически сопряженным» я противопоставляю текстовое сопряжение, например. неважно, есть ли в тексте исходного кода одинаковые номера вызовов инициализатора и финализатора. Вместо этого, когда выходит область блока (если когда-либо), любые вызовы функций инициализатора гарантированно имеют соответствующий вызов функции финализатора.

Примеры

Ok:

initialize(); 
mystery_function_that_may_never_return(); 
finalize(); 

Ok:

initialize(); 
if (cond) 
    finalize(); 
else 
    finalize(); 

Не хорошо:

initialize(); 
initialize(); 
finalize(); 

Не хорошо:

initialize(); 
finalize(); 
finalize(); 

Не хорошо:

initialize(); 
if (cond) 
    finalize(); 
else 
    return; 

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

+3

Для этого потребуется решить проблему с остановкой (https://en.wikipedia.org/wiki/Halting_problem). – Schwern

+1

Они могут быть не в строгих парах. Там может быть один 'fopen' и два альтернативных' fclose' того же файла, в зависимости от условий выполнения или наоборот. Значит ли это ваш гипотетический инструмент? Хотя они нужны парами, нет строгого синтаксического спаривания, как это требуется (скобки) и {скобки}, "кавычки" или/* комментарии * /. –

+0

Например, 'void read_filehandle (FILE * fh) {... if (done || feof (fh)) {fclose (fh)}}' Как вы это учитываете? Это длинный извращенный способ сказать «возможно, нет». – Schwern

ответ

3

Возможно, нет.

Все, что угодно: «определять во время компиляции, если X происходит во время выполнения» или «определять все кодовые пути» в C, запускается в halting problem. Возможно, вы можете уйти с ним на чисто функциональном языке, например Haskell.

Например ...

bool find_thing(FILE *fp, const char* thing) { 
    ...go searching through the fp for thing... 

    if(found_the_thing) { 
     fclose(fp); 
     return true; 
    } 
    else { 
     rewind(fp); 
     return false; 
    } 
} 

FILE *fp = fopen(filename, "r"); 
while(!find_thing(fp, that_thing)) { 
    ...mess with the file... 
} 

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

Вот еще один пример.

typedef struct { 
    FILE *fp; 
    const char *filename; 
} FileHandle; 

FileHandle fh = malloc(sizeof(FileHandle)); 
fh->filename = file; 
fh->fp = fopen(file, "r"); 

do_something(fh); 

fclose(fh); 
free(fh->filename); 
free(fh); 

Похоже, достаточно просто! Кроме того, как вы знаете, что fh->fp содержит тот же указатель файла, который был инициализирован? Что, если do_something изменил его? Что, если он уже закрыл его? Что, если он откроет новый?

Вот еще один пример.

FILE *fps[10]; 

...initialize fps... 

/* Returns a file pointer from fps */ 
FILE *fp = highest_priority(fps); 

...do something with fp... 

fclose(fp); 

Какой файл указатель закрыт?

Как уже упоминалось в комментариях, это, пожалуй, легче подумать с точки зрения выделения и освобождения памяти, и это имеет ту же проблему. Как мы узнаем fh->filename не был изменен? Что, если это NULL? Что, если он уже освобожден? Что делать, если fh уже освобожден?

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

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

int read_file(const char* filename, void *thunk, 
       void(*handle_line)(const char *, void *)); 

Best вы могли бы сделать, это предоставить время работу, что позволяет вам знать, если какие-либо ресурсы не были завершены к концу программы, или доработаны в два раза, или использоваться при неинициализированном , Вот как работает большинство проверок памяти в C, например Valgrind.

+0

Вопрос был пересмотрен. Является ли намерение понятным? – Praxeolitic

+0

@Praxeolitic Тот же ответ. Ограничения, которые вы предлагаете, требующие, чтобы все ресурсы были инициализированы и завершены в том же объеме, настолько ограничены, что все упражнение бесполезно. Как у вас есть функции, которые возвращают инициализированные ресурсы и прикрепляют их к более крупным структурам? – Schwern

+0

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

2

Как @Schwern упомянули, эта проблема сводима к Halting problem.

Рассмотрите функцию «инициализатор» Start() и функцию «финализатор» Stop().

Теперь, чтобы решить Проблему Остановки для программы P() было бы достаточно, чтобы запустить предлагаемый инструмент по следующей программе:

Start(); 
P(); 
Stop(); 

и проверить, является ли или нет Start() в «паре» с Stop().

Более формально:

Предположим, что существует функция Paired(A), возвращая true если есть путь кода между функцией Start() и Stop() (в случае, если присутствует в A) для любого данного входа в программу. Тогда для любой программы P() мы можем построить программу P'(), так что P'() определяется по формуле:

Start(); 
P(); 
Stop(); 

, для которых Paired(P') = Halting(P). Следовательно, имея Paired, мы можем решить Halting для любой программы P.

+1

Я не уверен, что куплю ваш пример. Я утверждаю, что 'P' либо останавливается, а затем парно или не останавливается, и тогда вопрос не имеет значения. Мы хотим знать, освобождены ли все ресурсы после прекращения - вопрос, который не имеет смысла для программ, которые никогда не заканчиваются. –

+0

Хороший пример, я вижу, что сейчас говорит Шверн, но в этом случае, независимо от того, что делает 'P()', есть один путь кода до 'Start()' и 'Stop()', и они спарен на этом пути кода. – Praxeolitic

+0

@Praxeolitic Они не «спарены», если 'P' не останавливается, поскольку между ними нет кодового пути. Если между ними существует кодовый путь - он остановится (путь означает, что он соблюдается). –

Смежные вопросы