列表,在C中命名指针和语义的别名

时间:2012-11-20 12:31:15

标签: c semantics

我一直在使用C编程语言作为项目的一部分。仅供参考,进入和列表的结构是非常基本的数据结构,并定义为

typedef struct entry entry_t;
typedef struct list list_t;

struct entry
{
    void * data;
    entry_t * next;
};

struct list
{
    size_t size;
    entry_t * head;
    entry_t * tail;
};

headtail分别指向第一个和最后一个条目。列表中没有这样的开销条目,但列表中的所有数据条目都没有,因此如果列表中只有一个条目,headtail应指向相同的条目。此外,还有一个片段可以删除列表中的所有条目

list_t list;
entry_t * current, * next;
for(
    current=list->head,
    next=current->next,
    free(current);
    current!=list->tail;
    current=next,
    next=current->next,
    free(current)
);

问题是在释放current指针后评估内存地址list->tailcurrent的值之间的比较。假设currentlist->tail现在指向同一块内存,然后current被释放,评估的结果是什么,更重要的是结果(无论是什么)您体验中所有不同编译器的确定性?在我的例子中,程序在MSVC中编译并正确运行,表示在current被释放后,内存地址list->tailcurrent的值相等。

4 个答案:

答案 0 :(得分:2)

在这一点上并不重要,但是如果你清除了清单,那么请原谅明显的,但是谁在乎尾巴(或尺寸)。无论如何,他们即将失效。为什么不简单:

// assuming list is a valid
list_t* list;

while (list->head)
{
    entry_t *tmp = list->head;
    list->head = list->head->next;
    free(tmp);
}
list->head = list->tail = NULL;
list->size = 0;

答案 1 :(得分:1)

从技术上讲,你违反了规则。按6.2.4(2)

  

对象的生命周期是程序执行的一部分,在此期间保证为其保留存储。存在一个对象,具有一个常量地址,并在其整个生命周期内保留其最后存储的值。如果在其生命周期之外引用对象,则行为未定义。当指针指向(或刚刚过去)的对象到达其生命周期的末尾时,指针的值变得不确定。

的C2011标准的n1570草案(在C99标准中几乎相同),在free current之后current其值变得不确定(如果list->tail恰好发生在free之前指向与list->tail相同的对象,NULL的值也变得不确定。因此在比较中使用它会调用未定义的行为。

在一个假设的实现中,它跟踪指针的有效性并检查每个比较等,它可能会崩溃或做其他有趣的事情。

然而,在实践中,我不希望任何实现这样做,所以它几乎肯定会产生预期的结果。

作为WhozCraig pointed out,实际上有一个平台在进行跟踪。但是,这会将所有无效指针视为{{1}},因此,如果我理解正确,即使存在相关代码也会产生预期结果。

答案 2 :(得分:0)

  

评估的结果是什么

C标准表示结果未定义。

  

更重要的是,结果(无论是什么)是确定性的   在您体验的所有不同编译器中

内存一旦被进程分配,通常不会返回到操作系统,直到进程终止。 我所知道的所有编译器都会将current!=list->tail评估为false,而不会抛出异常。

答案 3 :(得分:0)

以下是for loop实施

entry_t *current,*prev,*next;  

  for(
        current = list->head;

        current != NULL;

        prev=current,
        current = current->next,
        free(prev),
        prev=NULL
    );