Каких функций из стандартной библиотеки следует (следует) избегать? - PullRequest
87 голосов
/ 02 апреля 2010

Я прочитал о переполнении стека, что некоторые функции C «устарели» или «их следует избегать». Можете ли вы дать мне несколько примеров такого рода функций и причины?

Какие альтернативы этим функциям существуют?

Можем ли мы использовать их безопасно - какие-либо хорошие практики?

Ответы [ 13 ]

1 голос
/ 02 апреля 2010

Очень трудно безопасно использовать scanf. Хорошее использование scanf может избежать переполнения буфера, но вы все еще подвержены неопределенному поведению при чтении чисел, которые не соответствуют запрошенному типу. В большинстве случаев лучше использовать fgets с последующим самоанализом (используя sscanf, strchr и т. Д.).

Но я бы не сказал "избегать scanf все время". scanf имеет свое применение. В качестве примера предположим, что вы хотите прочитать пользовательский ввод в массиве char длиной 10 байт. Вы хотите удалить завершающий перевод строки, если он есть. Если пользователь вводит более 9 символов перед новой строкой, вы хотите сохранить первые 9 символов в буфере и отбросить все до следующей новой строки. Вы можете сделать:

char buf[10];
scanf("%9[^\n]%*[^\n]", buf));
getchar();

Как только вы привыкнете к этой идиоме, она короче и в некоторых отношениях чище, чем:

char buf[10];
if (fgets(buf, sizeof buf, stdin) != NULL) {
    char *nl;
    if ((nl = strrchr(buf, '\n')) == NULL) {
        int c;
        while ((c = getchar()) != EOF && c != '\n') {
            ;
        }
    } else {
        *nl = 0;
    }
}
0 голосов
/ 11 июня 2014

Во всех сценариях копирования / перемещения строк - strcat (), strncat (), strcpy (), strncpy () и т. Д.) Дела идут намного лучше ( безопаснее ), если пара простых эвристик применяются:

1. Всегда добавляйте NUL-буфер в буфер (ы) перед добавлением данных.
2. Объявите символьные буферы как [SIZE + 1] с макрос-константой.

Например, дано:

#define   BUFSIZE   10
char      Buffer[BUFSIZE+1] = { 0x00 };  /* The compiler NUL-fills the rest */

мы можем использовать код как:

memset(Buffer,0x00,sizeof(Buffer));
strncpy(Buffer,BUFSIZE,"12345678901234567890");

относительно безопасно. Memset () должен появляться перед strncpy (), даже если мы инициализировали Buffer во время компиляции, потому что мы не знаем, какой мусор помещен в него другим кодом до вызова нашей функции. Функция strncpy () урезает скопированные данные до «1234567890» и не NUL-завершит их. Однако, так как мы уже заполнили NUL весь буфер - sizeof (Buffer), а не BUFSIZE - гарантированно будет окончательный NUL «вне области», заканчивающий NUL, так как мы ограничиваем наши записи с использованием BUFSIZE константа вместо sizeof (Buffer).

Буфер и BUFSIZE также отлично работают для snprintf ():

memset(Buffer,0x00,sizeof(Buffer));
if(snprintf(Buffer,BUFIZE,"Data: %s","Too much data") > BUFSIZE) {
    /* Do some error-handling */
}   /* If using MFC, you need if(... < 0), instead */

Несмотря на то, что snprintf () специально записывает только символы BUFIZE-1, за которыми следует NUL, это работает безопасно. Таким образом, мы «теряем» лишний байт NUL в конце буфера ... мы предотвращаем переполнение буфера и неопределенные строковые условия при довольно небольшой стоимости памяти.

Мой вызов strcat () и strncat () более жесткий: не используйте их. Трудно безопасно использовать strcat (), а API для strncat () настолько нелогичен, что усилия, необходимые для его правильного использования, сводят на нет любую выгоду. Я предлагаю следующее раскрытие:

#define strncat(target,source,bufsize) snprintf(target,source,"%s%s",target,source)

Соблазнительно создать раскрывающийся список strcat (), но не очень хорошая идея:

#define strcat(target,source) snprintf(target,sizeof(target),"%s%s",target,source)

потому что target может быть указателем (таким образом, sizeof () не возвращает нужную нам информацию). У меня нет хорошего "универсального" решения для экземпляров strcat () в вашем коде.

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

0 голосов
/ 02 апреля 2010

atoi не является потокобезопасным. Вместо этого я использую strtol по рекомендации со страницы руководства.

...