15

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 错误

错误:“snprintf”调用中“sizeof”的参数与目标的表达式相同;您的意思是提供明确的长度吗?

注意:我使用 -Wall -Werror 标志将警告转换为错误。

这里有类似的东西 在评论中,有人回答了这个

“对于固定长度的缓冲区,我通常使用 strncpy(dest, src, sizeof(dest)); dest[sizeof(dest)-1] = '\0'; 这样可以保证 NULL 终止,而且比 snprintf 更省事,更不用说很多人使用 snprintf(dest, sizeof(dest), src); 代替,当他们的程序任意崩溃时感到非常惊讶。”

但这是错误的:gcc 4.8 说

“错误:‘strncpy’调用中‘sizeof’的参数与目标的表达式相同;您的意思是提供明确的长度吗?[-Werror=sizeof-pointer-memaccess]”

在 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;
}

给出以下诊断: 警告:“void memset(void*, int, size_t)”调用中“sizeof”的参数与目标的表达式相同;你的意思是取消引用它吗?[-Wsizeof-pointer-memaccess] memset(p1, 0, sizeof(p1)); // 错误 ^ 虽然这些警告不会导致编译失败,但通常 -Wall 与 -Werror 结合使用,因此新的警告会变成新的错误。要修复,要么重写以使用 memcpy,要么取消引用有问题的 memset 调用中的最后一个参数。*

好吧,在他们的例子中,很明显代码是错误的,但在我的例子中,使用 snprintf/strncpy,我不明白为什么,我认为这是 gcc 的错误 positif 错误。正确的 ?

感谢您的帮助

4

4 回答 4

12

当您传递给函数时,数组会衰减为指向第一个元素的指针。所以你有什么

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)

都是等价的。

在这里阅读:什么是数组衰减?

于 2013-10-05T21:10:07.773 回答
6

测试.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 $

这就是为什么。

于 2013-10-05T21:09:01.447 回答
3

在您的函数定义中,参数声明:

static inline void toto(char str[3])

不声明str为数组(C 没有数组类型的参数)。相反,它完全等同于:

static inline void toto(char *str)

3悄悄地忽略了。

所以sizeof(str)与字符串可以容纳的字符数无关,它只是char*指针的大小。

这源于一条规则,即声明为数组类型的参数被“调整”为指针类型。这与说数组类型的表达式在大多数上下文中隐式转换(或“衰减”)为指针的规则不同。这两个规则一起工作,使处理数组的代码看起来像是直接处理数组,而实际上它是在处理指向数组元素的指针。

C 数组和指针之间的关系经常令人困惑。我推荐阅读comp.lang.c FAQ的第 6 部分;它很好地解释了它。

于 2013-10-06T01:51:26.437 回答
2
static inline void toto(char str[3])

定义了一个可以采用任意大小数组的函数。被3忽略,并被str视为指针(尝试printf("%d\n", sizeof(str)): 在我的机器上,它打印 8 表示 64 位指针的大小)。在这种情况下,编译器实际上是正确的。

Clang 在这里给出了一个有用的警告:

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"); 
于 2013-10-05T21:09:11.887 回答