Является ли буфер области действия допустимым вне блока в c - PullRequest
1 голос
/ 05 июня 2019

Мне просто интересно, можно ли считать безопасным следующее:

void some_func(void) {
    const char * foo;

    if (SOME_MACRO()) {
       char * foo_buf[20];
       snprintf(foo_buf,sizeof(foo_buf),"blah_%d",some_global);
       foo = foo_buf;
    } else {
       foo = "some constant string";
    }
    // do a bunch of other stuff here...
    printf("%s\n", foo);
}

Это делает предположение, что память в foo_buf все еще действительна (и неизменна) за пределами области видимости блока.Меня беспокоит, есть ли ситуации, когда компилятор сбрасывает или перезаписывает конкретную память блока после его выхода.Причина, по которой я хотел бы объявить в блоке, а не в глобальной области видимости, заключается в том, что на некоторых платформах SOME_MACRO() будет преобразовываться в константу, в других - в выражение, поэтому код внутри блока if можно оптимизировать внекоторые случаи.

Ответы [ 2 ]

3 голосов
/ 05 июня 2019

Это UB.

Подъем char * foo_buf[20]; не должен давать вам худший код. Все локальные функции функции, вероятно, в любом случае будут размещены наверху, и компиляторы вполне способны устранить локальные элементы, которые никогда не используются.

Попробуйте скомпилировать:

#define UP 0
#define CONST 1

#include <stdio.h>

#if CONST
   #define SOME_MACRO() 0
#else
   int SOME_MACRO(void);
#endif

int some_global;


void some_func(void) {
    const char * foo;

#if UP
    char foo_buf[20]; //fixed from char *foo_buf[20];
#endif


    if (SOME_MACRO()) {
#if !UP
       char foo_buf[20]; //fixed from char *foo_buf[20];
#endif
       snprintf(foo_buf,sizeof(foo_buf),"blah_%d",some_global);
       foo = foo_buf;
    } else {
       foo = "some constant string";
    }
    // do a bunch of other stuff here...
    printf("%s\n", foo);
}

с CONST, установленным в 0 или 1, а затем с изменением ВВЕРХ на значение от 0 до 1.

Когда gcc, clang или icc перемещают объявление (изменяя UP), не имеет значения даже при -O0: https://gcc.godbolt.org/z/z9jnQD.

3 голосов
/ 05 июня 2019

Это делает предположение, что память в foo_buf все еще действительна (и неизменна) вне области блока

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

Лучше либо определить этот буфер в более высоком объеме, где он все еще будет действителен, либо динамически выделять память, чтобы она оставалась действительной.

Кроме того, тип является неправильным:

char * foo_buf[20];

Это определяет массив указателей, а не массив символов.Должно быть:

char foo_buf[20];
...