Когда getenv может изменять содержимое ранее возвращенного указателя при использовании glibc?
Стандарт С говорит, что:
Функция getenv возвращает указатель на строку, связанную с членом списка совпадений. Указанная строка не должна изменяться программой, но может быть перезаписана последующим вызовом функции getenv.
Как я понимаю, реализация getenv в glibc (я использовал версию 2.17) возвращает элемент из глобальной переменной char **environment. Каждый последующий вызов функции getenv по-прежнему возвращает один из элементов этого массива (или ноль, если такой переменной env не существует) без изменения какого-либо из ранее возвращенных значений.
Возможно ли какое-либо изменение содержимого ранее возвращенного указателя функцией getenv при последующем вызове функции getenv при использовании glibc? Если да, то когда?
3 ответа
Это действительно просто ошибка в стандартах. Даже C11 сохраняет язык, который позволяет перезаписывать буфер, но в то же время он не разрешает гонки данных с другими вызовами getenv
только с (определенными реализацией) функциями, которые модифицируют среду, поэтому разрешение перезаписи кажется противоречивым.
На всех реальных реализациях, включая glibc, getenv
возвращает указатель на копию строки во внутреннем представлении среды и никогда не будет аннулирован, за исключением случаев, когда вы вызываете функции, которые изменяют среду.
Если я правильно понял, getenv
теоретически может быть реализовано путем копирования строки во внутренний статический буфер и возврата указателя на нее вместо указания указателя на environ
член. Если это так, строка будет изменяться каждым getenv
вызов.
Возможно, демо поможет:
char *getenv(const char *var){
int i=0, len=strlen(var);
if (!environ||!*var||strchr(var,'='))
return NULL;
while ( environ[i] && (environ[i][len] != '=' || strncmp(var,environ[i],len)) )
i++;
return (environ[i])? environ[i]+len+1 : NULL;
}
Каждый вызов получит соответствующий указатель на глобальный внешний массив 2d. environ
, поэтому программа не должна изменять его, потому что он фактически изменяет переменную окружения; однако последующий вызов getenv может получить значение в другой позиции (если setenv или putenv изменили его)
Обратите внимание, что вы получаете указатель на середину строки. Подумай, что случится, если ты это изменишь. Затем в любой заданный момент среда ** может измениться, так что адрес указывает на какую-то другую переменную (или мусор)... если вам нужно использовать эту строку в долгосрочной перспективе, вы должны скопировать ее просто для безопасности (особенно в многопоточных средах))
Что это говорит о том, что вы можете сделать mycharptr=getenv("myvar");
так часто, как вы хотите, это безопасно, но не делайте mycharptr=getenv("myvar");mycharptr[0]='\0'; ... mycharptr=getenv("myvar");
... или вы можете получить неожиданные результаты.