栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

从 typedef struct T *T 看C语言的命名空间

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

从 typedef struct T *T 看C语言的命名空间

最近在看《C语言接口与实现》一书. 第二章提到, 一个ADT(抽象数据类型)就是一个借口, 其标准范例是栈. 其接口定义如下所示:

//stack.h
#ifndef STACK_INCLUDED
#define STACK_INCLUDED


#define T Stack_T
typedef struct T *T;

extern T    Stack_new   (void);
extern int  Stack_empty (T stk);
extern void Stack_push  (T stk, void* x);
extern void*Stack_top   (T stk);
extern void*Stack_pop   (T stk);
extern void Stack_free  (T* stk);

#undef T
#endif

众所周知宏定义的作用仅仅是一个替换而已, 因此typedef这句话实际为:

typedef struct Stack_T *Stack_T;

按照书上说法, Stack_T对外接口表现为一个“不透明指针”. 我起初理解是, 以后凡是看到Stack_T的地方实际指代的都是指针Stack_T*. 乍看起来这种理解没错, 但看一下栈的实现后, 便发生了理解不能的事情:

//stack.c
#include 
#include "assert.h"
#include "mem.h"
#include "stack.h"

#define T Stack_T

struct T {
    int count;
    struct elem {
        void* x;
        struct elem* link;
    }* head;
};

T Stack_new(void) {
    T stk;

    NEW(stk);
    stk->count = 0;
    stk->head = NULL;
    return stk;
}

int Stack_empty(T stk) {
    assert(stk);
    return stk->count == 0;
}

void Stack_push(T stk, void* x) {
    struct elem* t;

    assert(stk);
    NEW(t);
    t->x = x;
    t->link = stk->head;
    stk->head = t;
    stk->count++;
}

...

这里#define 指令又把T定义为Stack_T的缩写. 问题来了,  以下结构体的定义和分配并初始化一个结构体函数中的Stack_T分别指什么呢:

struct Stack_T {
    ...
};

Stack_T Stack_new(void) {
    Stack_T stk;

    NEW(stk);
    ...
    return stk;
}

如果按照上述理解 Stack_T 指代的就是指针 Stack_T*, 显然没有这种定义式无法通过编译: struct Stack_T*. 函数返回类型和结构体名称的重名令人困惑, 但这确实是合法而正规的.

究其根本原因是, struct后面的 Stack_T 和 函数的返回类型 Stack_T 不是同一个东西. 这就牵扯到C语言的命名空间约定(不同于C++的namespace, 是个全局的区分C语言标识符的约定):

1) 预处理器宏名
2) 语句标签(goto类型)
3) 结构、枚举、联合结构的标签(结构,联合和枚举的名称)
4) 成员名
5) 其他名称 包括变量名、函数名、typedef名称和枚举常量

因此struct后面的 Stack_T 属于 3), 而函数返回类型的 Stack_T 属于 5), 两者并不冲突. 即前者是个名为Stack_T 结构体的标签, 后者是个typedef的名称, 实际类型式 Stack_T* . 这里我对typedef的Stack_T 重新用 Stack_P 命名下, 上述代码就看的比较清楚了:

//stack.h
#ifndef STACK_INCLUDED
#define STACK_INCLUDED


typedef struct Stack_T* Stack_P;

extern Stack_P    Stack_new   (void);
extern int  Stack_empty (Stack_P stk);
extern void Stack_push  (Stack_P stk, void* x);
extern void*Stack_top   (Stack_P stk);
extern void*Stack_pop   (Stack_P stk);
extern void Stack_free  (Stack_P* stk);

//#undef T
#endif
//stack.c
#include 
#include "assert.h"
#include "mem.h"
#include "stack.h"


struct Stack_T {
    int count;
    struct elem {
        void* x;
        struct elem* link;
    }* head;
};

Stack_P Stack_new(void) {
    Stack_P stk;

    NEW(stk);
    stk->count = 0;
    stk->head = NULL;
    return stk;
}

int Stack_empty(Stack_P stk) {
    assert(stk);
    return stk->count == 0;
}

void Stack_push(Stack_P stk, void* x) {
    struct elem* t;

    assert(stk);
    NEW(t);
    t->x = x;
    t->link = stk->head;
    stk->head = t;
    stk->count++;
}

...

 这种 typedef struct T *T 其实C语言接口库中比较标准的写法, 值得引起借鉴和学习.

参考:
C Namespaceshttps://www.spinellis.gr/cscout/doc/name.html
也谈C语言标识符的NAMESPACE | Tony Baihttps://tonybai.com/2008/05/15/also-talk-about-namespace-in-c

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1001035.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号