Когда 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");... или вы можете получить неожиданные результаты.

Другие вопросы по тегам