В некоторых обстоятельствах использование функций setenv() и clearenv() может привести к утечкам памяти в программе. Как уже говорилось, функция setenv() выделяет буфер памяти, который затем составляет часть среды. Когда вызывается функция clearenv(), она не высвобождает данный буфер (это невозможно, поскольку ей ничего не известно о его существовании). Программа, неоднократно использующая эти две функции, будет постоянно допускать утечку памяти. С практической точки зрения это вряд ли станет проблемой, поскольку обычно программа вызывает clearenv() только один раз в начале своего выполнения, чтобы удалить из среды все записи, унаследованные от своего предка (то есть от программы, вызвавшей exec() для ее запуска).
Функция clearenv() предоставляется во многих реализациях UNIX, но в SUSv3 она не определена. В SUSv3 определено, что, если приложение напрямую изменяет среду, как это делается функцией clearenv(), то поведение функций setenv(), unsetenv() и getenv() становится неопределенным. (Обоснование следующее: если запретить соответствующему приложению непосредствено изменять среду, то реализация ядра сможет полностью контролировать структуры данных, которые применяются ею для создания переменных среды.) Единственный способ очистки среды, разрешенный в SUSv3 приложению, заключается в получении списка всех переменных среды (путем извлечения их имен из environ), с последующим использованием функции unsetenv() для поименного удаления каждой переменной.
Использование всех ранее рассмотренных в этом разделе функций продемонстрировано в листинге 6.4. После начальной очистки среды программа добавляет любые определения среды, предоставленные в виде аргументов командной строки. Затем она добавляет определение для переменной GREET, если таковой еще не имеется в среде, удаляет определение для переменной BYE и, наконец, выводит на экран текущий список переменных среды. Вот как выглядит вывод этой программы:
$ ./modify_env "GREET=Guten Tag" SHELL=/bin/bash BYE=Ciao
GREET=Guten Tag
SHELL=/bin/bash
$ ./modify_env SHELL=/bin/sh BYE=byebye
SHELL=/bin/sh
GREET=Hello world
Если присвоить переменной environ значение NULL (как это делается при вызове clearenv() в листинге 6.4), то мы вправе ожидать, что следующий цикл (в том виде, в котором он используется в программе) даст сбой, поскольку запись *environ будет некорректна:
for (ep = environ; *ep!= NULL; ep++)
puts(*ep);
Но если функции setenv() и putenv() определят, что environ имеет значение NULL, они создадут новый список переменных среды и установят для environ значение, указывающее на этот список. Это приведет к тому, что описанный выше цикл станет работать правильно.
Листинг 6.4. Изменение среды процесса
proc/modify_env.c
#define _GNU_SOURCE /* Для получения различных объявлений из
#include
#include "tlpi_hdr.h"
extern char **environ;
int
main(int argc, char *argv[])
{
int j;
char **ep;
clearenv(); /* Удаление всей среды */
for (j = 1; j < argc; j++)
if (putenv(argv[j])!= 0)
errExit("putenv: %s", argv[j]);
if (setenv("GREET", "Hello world", 0) == -1)
errExit("setenv");
unsetenv("BYE");
for (ep = environ; *ep!= NULL; ep++)
puts(*ep);
exit(EXIT_SUCCESS);
}
proc/modify_env.c
Библиотечные функции setjmp() и longjmp() используются для нелокального перехода. Термин «нелокальный» обозначает, что цель перехода находится где-то за пределами той функции, которая выполняется в данный момент.
Как и многие другие языки программирования, язык C включает в себя инструкцию goto. Злоупотребление ею затрудняет чтение программы и ее сопровождение. Однако временами это весьма полезная инструкция в плане упрощения программы, ускорения ее работы или достижения обоих результатов.
Одно из ограничений имеющейся в языке C инструкции goto заключается в том, что она не позволяет осуществить переход из текущей функции в другую функцию. Но такая функциональная возможность временами может оказаться весьма полезной.