【问题标题】:Pointer casting and "free" in CC中的指针转换和“免费”
【发布时间】:2012-12-18 11:33:58
【问题描述】:

给定以下代码:

RTIME *rtCreate(void)
{
    RTIME *rtime;

    rtime = malloc(sizeof(*rtime));

    if (rtime != NULL)
    {
        /* Initialization stuff */
    }

    return rtime;
}

void rtDestroy(RTIME **rtime)
{
    if (*rtime != NULL)
    {
        free(*rtime);
        *rtime = NULL;
    }
}

什么会导致 GCC 抱怨“传递 'free' 的参数 1 会丢弃来自指针目标类型的限定符”?我知道没有必要强制转换 malloc 的结果(如 here 所讨论的那样),因为指向 void 的指针会自动转换为正确的类型。那么为什么编译器似乎暗示我必须先转换 *rtime 才能释放它?

编辑:- RTIME 定义如下。

typedef struct RTIME
{
    uint8 Sec;
    uint8 Min;
    uint8 Hour;
    uint8 DayOfWeek;
    uint8 DayOfMonth;
    uint16 DayOfYear;
    uint8 Month;
    uint16 Year;
} volatile RTIME;

【问题讨论】:

  • 是错误还是警告?
  • RTIME 是如何定义的以及如何调用rtDestroy() 函数?
  • 听起来 RTIME 是恒定的
  • 为什么要为易失性分配内存区域?通常你会从某个函数或其他机制中获取值,使用它,然后继续。
  • 谢谢 - 我现在知道哪里出错了。我的 typedef 基于另一个与硬件相关的 typedef,它被声明为 volatile,而我的不需要。

标签: c pointers free


【解决方案1】:

获取您的代码并从中生成SSCCE,如下所示:

#include <stdlib.h>

struct RTIME { int a; int b; };
typedef const struct RTIME RTIME;

RTIME *rtCreate(void)
{
    RTIME *rtime;

    rtime = malloc(sizeof(*rtime));

    if (rtime != NULL)
    {
        /* Initialization stuff */
    }

    return rtime;
}

void rtDestroy(RTIME **rtime)
{
    if (*rtime != NULL)
    {
        free(*rtime);
        *rtime = NULL;
    }
}

使用 GCC 4.7.1 和命令行编译:

$ gcc -O3 -g -std=c99 -Wall -Wextra -Wmissing-prototypes -c mf.c
mf.c:6:8: warning: no previous prototype for ‘rtCreate’ [-Wmissing-prototypes]
mf.c:20:6: warning: no previous prototype for ‘rtDestroy’ [-Wmissing-prototypes]
mf.c: In function ‘rtDestroy’:
mf.c:24:9: warning: passing argument 1 of ‘free’ discards ‘const’ qualifier from pointer target type [enabled by default]
In file included from mf.c:1:0:
/usr/include/stdlib.h:160:7: note: expected ‘void *’ but argument is of type ‘const struct RTIME *’
$

省略const,您只会收到有关缺少原型的(有效)警告。

我猜你正在使用旧版本的 GCC(因为旧版本不包含 note: 行中的额外信息),并且不知何故你的 typedefRTIME 包括const.

作为一般规则,您不希望 const 出现在 typedef 中,但该规则肯定会有例外。


从编辑后的问题中可以看出,限定词是volatile,而不是const。当我的示例代码中的typedef 发生更改时,GCC 4.7.1 会说:

mf.c:6:8: warning: no previous prototype for ‘rtCreate’ [-Wmissing-prototypes]
mf.c:20:6: warning: no previous prototype for ‘rtDestroy’ [-Wmissing-prototypes]
mf.c: In function ‘rtDestroy’:
mf.c:24:9: warning: passing argument 1 of ‘free’ discards ‘volatile’ qualifier from pointer target type [enabled by default]
In file included from mf.c:1:0:
/usr/include/stdlib.h:160:7: note: expected ‘void *’ but argument is of type ‘volatile struct RTIME *’

当我使用系统 GCC 编译时,我得到一个更简单、不太精确的错误消息:

mf.c:7: warning: no previous prototype for ‘rtCreate’
mf.c:21: warning: no previous prototype for ‘rtDestroy’
mf.c: In function ‘rtDestroy’:
mf.c:24: warning: passing argument 1 of ‘free’ discards qualifiers from pointer target type

这是来自 Apple 的 GCC:

i686-apple-darwin11-llvm-gcc-4.2 (GCC) 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2336.11.00)

所以,限定符是 volatile 而不是 const


尝试升级到 GCC 4.7.x 的一个很好的理由是错误消息比早期版本有了很大改进。改进的消息也在 4.6.0 中; 4.5.2 中的消息是较旧的样式,信息量较少。

【讨论】:

  • 感谢您的全面回答。然而不幸的是,我正在一个特定于供应商的嵌入式开发平台上工作,因此升级 GCC 暂时是不可能的。然而,正如您所知,学习 C 语言的数量仍然很大。
【解决方案2】:

我建议你重写你的rtDestroy() 函数如下。似乎 GCC 看到了指向指针的指针的取消引用,并将其视为可能的错误来源。

您似乎想要释放分配的内存,然后将指针设置为 NULL,以便轻松检测指针不再有效。但是,通过执行您正在执行的操作,free() 函数将通过编译器无法检查的重定向获得要释放的内存地址。

因此,如果将 *rtime 指向的地址分配给局部变量,然后将其传递给 free() 函数,编译器检查应该通过。对于某些类型的源构造,这似乎是一个相当常见的警告。当变量需要 const 修饰符并且未指定时,这似乎是最常见的。请参阅此示例Initialization discards qualifiers from pointer target type

void rtDestroy(RTIME **rtime)
{
    RTIME *pTemp = *rtime;

    if (*rtime != NULL)
    {
        free(pTemp);
        *rtime = NULL;
    }
}

编辑: 似乎 RTIME 是一种变量类型,包含以纳秒为单位的硬件时间滴答或类似的东西。不知道为什么它会是 const,除非所使用的函数提供了一个指向只读操作系统内存区域的指针。

我建议没有必要进行内存分配,而是使用堆栈上的变量并使用适当的函数来检索无论如何都会改变的当前值。

但是,您可以尝试使用void *pTemp = (void *) (*rtime);,而不是使用RTIME *pTemp = *rtime;,但是如果RTIME 涉及一个常量,那么我希望这只会将警告放在该行上。 Jonathan Leffler(请参阅下面的评论)报告说,他发现与我上面建议的更改相同的错误。

看到这个article Real Time Linux II

【讨论】:

  • 当您提出的代码插入我的测试工具时,会生成与以前相同的警告(当然是给出或获取行号)。
  • RTIME的定义/声明是什么?
  • 那(RTIME 的定义/声明是什么)是个大问题——但从我看到的编译警告来看,RTIME 似乎包含一个const,这使得它们不是非常好用。
  • 我不想说改变 RTIME *pTemp = *rtime;作废 *pTemp = *rtime; RTIME 似乎是一种变量类型,包含以纳秒为单位的硬件时间滴答或类似的东西。除非使用的函数提供指向只读操作系统内存区域的指针,否则不确定为什么它会是 const。 linuxfocus.org/English/July1998/article56.html
【解决方案3】:

问题在于 volatile 属性 - free 获取 void *,它不是易失性的,因此您不能将 RTIME(易失性)传递给它。

您应该问问自己为什么要使用volatile。这个限定词经常被误解。
在我看到的许多情况下,volatile 用于解决不存在的问题,或者即使您使用它仍然存在的问题(最常见的是竞争条件)。

【讨论】:

    猜你喜欢
    • 2011-04-09
    • 1970-01-01
    • 2021-01-10
    • 1970-01-01
    • 1970-01-01
    • 2021-07-11
    • 1970-01-01
    • 2012-12-17
    相关资源
    最近更新 更多