Snprintf ошибка. аргумент sizeof совпадает с аргументом назначения

gcc 4.8 дайте мне ошибку при построении

#include <string.h>
#include <stdio.h>

static inline void toto(char str[3])
{
    snprintf(str, sizeof(str), "XX"); 
}

int main(){
    char str[3]; 
    toto(str);
    return 0;
}

Вот ошибка gcc

ошибка: аргумент для вызова 'sizeof in' snprintf - это то же выражение, что и пункт назначения; Вы имели в виду предоставить ясную длину?

Примечание. Я использую флаги -Wall -Werror, которые преобразуют предупреждение в ошибку.

Здесь что-то похожее В комментарии кто-то ответил на это

"Для буферов с фиксированной длиной я обычно использую strncpy (dest, src, sizeof (dest)); dest [sizeof (dest) -1] = '\ 0'; Это гарантирует завершение NULL и это всего лишь меньше хлопот, чем snprintf не говоря уже о том, что многие люди используют snprintf (dest, sizeof (dest), src) и вместо этого очень удивляются, когда их программы сбой произвольно".

Но это неправильно: gcc 4.8 сказать

"error: аргумент для 'sizeof in' вызов strncpy - это то же выражение, что и пункт назначения, вы имели в виду предоставить явную длину? [-Werror = sizeof-pointer-memaccess]" /p >

в документации gcc 4.8, они говорят об этой проблеме: они говорят:

Поведение -Wall изменилось и теперь включает новый флаг предупреждения -Wsizeof-pointer-memaccess. Это может привести к появлению новых предупреждений в коде, которые были скомпилированы с использованием предыдущих версий GCC.

Например,

include string.h

struct A { };

int main(void) 
{
    A obj;
    A* p1 = &obj;
    A p2[10];

    memset(p1, 0, sizeof(p1)); // error
    memset(p1, 0, sizeof(*p1)); // ok, dereferenced
    memset(p2, 0, sizeof(p2)); // ok, array
    return 0;
}

Дает следующую диагностику: warning: аргумент "sizeof in" void memset (void *, int, size_t) - это то же выражение, что и пункт назначения; Вы имели в виду разыгрывать это? [-Wsizeof-указатель memaccess] memset (p1, 0, sizeof (p1));//ошибка                      ^ Хотя эти предупреждения не приведут к сбою компиляции, часто -Wall используется в сочетании с -Werror, и в результате новые предупреждения превращаются в новые ошибки. Чтобы исправить, либо перепишите для использования memcpy, либо разыщите последний аргумент в нарушающем вызове memset. *

Ну, в их примере, очевидно, что код был неправильным, но в моем случае, с snprintf/strncpy, я не понимаю, почему, и я думаю, что это ложная ошибка positif gcc. Правильно?

спасибо за помощь

Ответ 1

Массив распадается на указатель на первый элемент при передаче функции. Итак, что вы имеете в

static inline void toto(char str[3]) {..}

не является массивом, а указателем.

Следовательно, gcc правильно предупреждает.

Если вы укажете размер в параметре функции или нет, это не имеет значения:

static inline void toto(char str[3])

и

static inline void toto(char str[])

и

static inline void toto(char *str)

эквивалентны.

Читайте здесь: что такое разложение массива?

Ответ 2

test.c:

#include <stdio.h>

void bar(char foo[1000])
{
    printf ("sizeof foo = %d\n", (int)(sizeof foo));
}

int main ()
{
    char foo[1000];
    bar(foo);
}

работает:

bash $ ./test
4
bash $

Вот почему.

Ответ 3

В определении функции объявление параметра:

static inline void toto(char str[3])

не объявляет str как массив (C не имеет параметров типа массива). Скорее, это точно эквивалентно:

static inline void toto(char *str)

3 игнорируется.

Итак, sizeof(str) не имеет никакого отношения к числу символов, которые может содержать строка, это просто размер указателя char*.

Это результат правила, в котором говорится, что параметр, объявленный с типом массива, "настроен" на тип указателя. Это отличается от правила, согласно которому выражения типа массива неявно преобразуются (или "распадаются" ) на указатели в большинстве контекстов. Два правила работают вместе, чтобы сделать код, который имеет дело с массивами, похоже, что он имеет дело с массивами напрямую, когда он действительно работает с указателями на элементы массивов.

Связь между C-массивами и указателями часто сбивает с толку. Я рекомендую прочитать раздел 6 comp.lang.c FAQ; он очень хорошо объясняет это.

Ответ 4

static inline void toto(char str[3])

определяет функцию, которая может принимать массив любого размера. 3 игнорируется, а str рассматривается как указатель (try printf("%d\n", sizeof(str)): на моей машине он печатает 8 для размера 64-разрядного указателя). В этом случае компилятор действительно прав.

Кланг дает полезное предупреждение здесь:

test.c:6:25: warning: sizeof on array function parameter will return size of
      'char *' instead of 'char [3]' [-Wsizeof-array-argument]
    snprintf(str, sizeof(str), "XX");