17. 标准C

现代C编译器支持一些或全部的ANSI提议的标准C。无论何时可能的话,尽量用标准C编写和运行程序,并且使用诸如函数原型,常量存储以及volatile(易失性)存储等特性。标准C通过给优化器提供有有效的信息以提升程序的性能。标准C通过保证所有编译器接受同样的输入语言以及提供相关机制隐藏机器相关内容或对于那些机器相关代码提供警告的方式提升代码的可移植性。

17.1 兼容性

编写很容易移植到老编译器上的代码。例如,有条件地在global.h中定义一些新(标准中的)关键字,比如const和volatile。标准编译器预定义了预处理器符号STDC(见脚注8)。void类型很难简单地处理正确,因为很多老编译器只理解void,但不认识void。最简单的方法就是定义一个新类型VOIDP(与机器和编译器相关),通常在老编译器下该类型被定义为char*。

#if __STDC__
    typedef void *voidp;
#    define COMPILER_SELECTED
#endif
#ifdef A_TARGET
#    define const
#    define volatile
#    define void int
    typedef char *voidp;
#    define COMPILER_SELECTED
#endif
#ifdef ...
    ...
#endif
#ifdef COMPILER_SELECTED
#    undef COMPILER_SELECTED
#else
    { NO TARGET SELECTED! }
#endif

注意在ANSI C中,#必须是同一行中预处理器指示符的第一个非空白字符。在一些老编译器中,它必须是同一行中的第一个字符。

当一个静态函数具有前置声明时,前置声明必须包含存储修饰符。在一些老编译器中,这个修饰符必须是"extern"。对于ANSI编译器,这个存储修饰符必须为static,但全局函数依然必须声明为extern。因此,静态函数的前置声明应该使用一个#define,例如FWD_STATIC,并通过#ifdef适当定义。

一个"#ifdef NAME"应该要么以"#endif"结尾,要么以"#endif /* NAME */结尾,不应该用"#endif NAME"结尾。对于短小的#ifdef不应该使用注释,因为通过代码我们可以明确其含义。

ANSI的三字符组可能导致内容包含??的字符串的程序神秘的中断。

17.2 格式化

ANSI C的代码风格与常规C一样,但有两点意外:存储修饰符(storage qualifiers)和参数列表。

由于const和volatile的绑定规则很奇怪,因此每个const或volatile对象都应该单独声明。

int const *s;        /* YES */
int const *s, *t;    /* NO */

具备原型的函数将参数声明和定义归并在一个参数列表中了。应该在函数的注释中提供各个参数的注释。

/*
 * `bp': boat trying to get in.
 * `stall': a list of stalls, never NULL.
 * returns stall number, 0 => no room.
 */
int
enter_pier (boat_t const *bp, stall_t *stall)
{
    ...

17.3 原型

Function prototypes should be used to make code more robust and to make it run faster. Unfortunately, the prototyped declaration

应该使用函数原型使得代码更加健壮并且运行时性能更好。不幸地是原型的声明

extern void bork (char c);

与定义不兼容。

void
bork (c)
char c;
 ...

原型中c应该以机器上最自然的类型传入,很可能是一个字节。而非原型化(向后兼容)的定义暗示c总是以一个整型传入。如果一个函数具有可类型提升的参数,那么调用者和被调用者必须以相等地方式编译。要么都必须使用函数原型,要么都不使用原型。如果在程序设计时参数就是可以提升类型的,那么问题就可以被避免,例如bork可以定义成接受一个整型参数。

如果定义也是原型化的,上面的声明将工作正常。

void
bork (char c)
{
    ...

不幸地是,原型化的语法将导致非ANSI编译器拒绝这个程序。

但我们可以很容易地通过编写外部声明来同时适应原型与老编译器。

#if __STDC__
#    define PROTO(x) x
#else
#    define PROTO(x) ()
#endif

extern char **ncopies PROTO((char *s, short times));

注意PROTO必须使用双层括号。

最后,最好只使用一种风格编写代码(例如,使用原型)。当需要非原型化的版本时,可使用一个自动转换工具生成。

17.4 Pragmas

Pragmas用于以一种可控的方式引入机器相关的代码。很显然,pragma应该被视为机器相关的。不幸地是,ANSI pragmas的语法使得我们无法将其隔离到机器相关的头文件中了。

Pragmas分为两类。优化相关的可以被安全地忽略。而那些影响系统行为(需要pragmas)的Pragmas则不能忽略。需要的pragmas应该结合#ifdef使用,这样如果一个pragma都没有选到,编译过程将退出。

两个编译器可能通过两个不同的方式使用同一个给定的pragma。例如,一个编译器可能使用haggis发出一个优化信号。而另一个可能使用它暗示一个特定语句,一旦执行到此,程序应该退出。不过,一旦使用了pragma,它们必须总是被机器相关的#ifdef包围。对于非ANSI编译器,Pragmas必须总是被#ifdef。确保对#pragma的#进行缩进,否则一些较老的预处理器处理它时会挂起。

#if defined(__STDC__) && defined(USE_HAGGIS_PRAGMA)
    #pragma (HAGGIS)
#endif

"ANSI标准中描述的'#pragma'命令具有任意实现定义的影响。在GNU C预处理中,'#pragma'首先尝试运行游戏'rogue';如果失败,它将尝试运行游戏'hack';如果失败,它将尝试运行GNU Emacs显示汉诺塔;如果失败,它将报告一个致命错误。无论如何,预处理将不再继续。"

-- GNU CC 1.34 C预处理手册。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,175评论 5 466
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,674评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,151评论 0 328
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,597评论 1 269
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,505评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,969评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,455评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,118评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,227评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,213评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,214评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,928评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,512评论 3 302
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,616评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,848评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,228评论 2 344
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,772评论 2 339

推荐阅读更多精彩内容