Утверждение времени компиляции, чтобы определить, является ли указатель массивом - PullRequest
0 голосов
/ 18 декабря 2018

В настоящее время у меня есть следующий блок кода для безопасного копирования строк (это работает):

#define STRCPY(dst, src) do { assert(((void*)(dst)) == ((void*) & (dst))); \
                              strlcpy(dst, src, sizeof(dst)); } while (0)

Таким образом, он принимает конструкцию типа:

const char *src = "hello";
char dest[5];
STRCPY(dest, src); //hell

И отрицаетследующее:

void (char *dst) {
    STRCPY(dst, "heaven"); //unknown size of dst
}

Проблема в том, что блок кода создает утверждение.Есть ли способ выполнить эту проверку на время компиляции?

Поэтому я хочу иметь ошибку при компиляции (например, создание массива с отрицательным размером ) вместо сбоя кода, если это возможно.

Ответы [ 4 ]

0 голосов
/ 18 декабря 2018

Для подтверждения времени компиляции о том, является ли dst массивом, я бы использовал решение @Lundin (или _Static_assert), если доступен C11.

В противном случае я бы использовал следующее:

#define BUILD_BUG_ON_NON_ARRAY_TYPE(e) (sizeof(struct { int:-!!(((void*)(e)) != ((void*) & (e))); }))

Который в основном принимает ваше вычисленное во время компиляции выражение ((void*)(dst)) == ((void*) & (dst)), но вместо того, чтобы использовать его во время выполнения assert, просто используйте его в утверждении во время компиляции.

Итак, в целом я бы изменил ваш макрос на:

#define STRCPY(dst, src) do { BUILD_BUG_ON_NON_ARRAY_TYPE(dst); \
                              strlcpy(dst, src, sizeof(dst)); } while (0)
0 голосов
/ 18 декабря 2018

Я обнаружил, что мое сообщение какое-то время было закрыто как дублированное и следовало за упомянутой ссылкой .Решение только для GCC, но для меня это нормально, потому что у меня тоже есть ночные сборки на GCC.Таким образом, предварительный проект кода:

#if defined(__GNUC__)
#define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); }))
#define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
#define __must_be_array(a) BUILD_BUG_ON_ZERO(__same_type((a), &(a)[0]))
#define STRCPY(dst,src) do{__must_be_array(dst);strlcpy(dst, src, sizeof(dst));}while(0)
#else
#define STRCPY(dst,src) do{strlcpy(dst, src, sizeof(dst));}while(0)
#endif
0 голосов
/ 18 декабря 2018

Если доступен стандартный C, то вы можете сделать это:

#define STRCPY(dst, src)                                        \
  _Generic(&(dst),                                              \
           char(*)[sizeof(dst)]: strlcpy(dst,src,sizeof(dst)) )

Объяснение:

Вы не можете использовать выражение _Generic для типа массива, потому что онони один из особых случаев, которые не освобождаются от правила «распада массива» (C17 6.3.2.1 §3).Таким образом, просто используя _Generic((dst), ... в моем примере, dst в конечном итоге получит char* при вычислении выражения, и тогда мы потеряем информацию его первоначального типа.

Но если мы примемадрес массива, используя &, мы используем один из этих особых случаев, и спад массива не происходит.Вместо этого мы получаем указатель массива, что означает, что _Generic придется проверить указатель массива ожидаемого типа и размера: char(*)[sizeof(dst)].


В качестве примечания / безопасностиЯ никогда не использую макросы do-while(0) и не поощряю их, но это уже другая история.

0 голосов
/ 18 декабря 2018

Если вы хотите проверить это, я могу думать только об этом:

assert((sizeof(dst)) != (sizeof(void*)));

, но это работает, только если размер массива отличается от размера указателя в системе OPs

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...