是否有C标准的着名“概况”?

时间:2011-08-24 13:46:45

标签: c language-lawyer

我编写C代码,对实现做出某些假设,例如:

  • char是8位。
  • 签名的整数类型是两个补码。
  • >>有符号整数sign-extends。
  • 整数除法将负商数向零舍入。
  • double是IEEE-754的两倍,并且可以与uint64_t进行类型惩罚并获得预期结果。
  • 涉及NaN的比较总是评估为假。
  • 空指针全是零位。
  • 所有数据指针具有相同的表示形式,并且可以转换为size_t并再次返回,而不会丢失信息。
  • char*上的指针算法与size_t上的普通算术相同。
  • 函数指针可以转换为void*并再次返回,而不会丢失信息。

现在,所有这些都是C标准所不能保证的,所以严格来说我的代码是不可移植的。但是,它们在我目前正在瞄准的体系结构和ABI上都是正确的,经过仔细考虑后,我已经确定他们未能在某些架构上保留的风险,我将来需要将其作为可接受的低与我从假设现在得出的实用好处相比。

问题是:我如何最好地记录这个决定?我的许多假设是由几乎每个人(非八位位组char s?或符号幅度整数?在未来的商业上成功的架构上制作的?)。其他人更有争议 - 最危险的可能是关于函数指针的问题。但是,如果我只是列出所有,我认为超出了标准给我的范围,读者的眼睛只会茫然,他可能不会注意到实际上重要的那些。

那么,是否有一些众所周知的关于成为“有点正统”的架构的假设,我可以通过引用合并,然后只明确记录我甚至超出的范围? (实际上,这样的“概况”将定义一种新的语言,它是C的超集,但它可能不会承认这么多的话 - 并且它可能不是一种实用的方法来考虑它。)

澄清:我正在寻找记录我的选择的简写方法,而不是自动测试给定编译器是否符合我的期望的方法。后者显然也很有用,但并不能解决所有问题。例如,如果业务合作伙伴与我们联系说“我们正在制作基于Google新G2015芯片的设备;您的软件会在其上运行吗?” - 那么能够回答“我们还没有使用过那个拱门,但如果它有一个满足这样的C编译器就不应该成为一个问题”。

澄清更多,因为有人投票决定关闭“没有建设性”:我不是在这里寻找讨论,只是为了指出可以简化我的文档的实际,现有,正式文件通过引用纳入。

4 个答案:

答案 0 :(得分:6)

我将介绍一个STATIC_ASSERT宏并将所有假设放在这样的断言中。

答案 1 :(得分:1)

不幸的是,不仅缺乏C语言的标准,而且结合了在20世纪90年代已经成为事实标准的扩展(两个补充,普遍排名的指针等),但是编制者的趋势正朝着相反的方向发展。鉴于函数的以下要求:

* Accept int parameters x,y,z:
* Return 0 if x-y is computable as "int" and is less than Z
* Return 1 if x-y is computable as "int" and is not less than Z
* Return 0 or 1 if x-y is not computable */

20世纪90年代绝大多数编制者都允许:

int diffCompare(int x, int y, int z)
{ return (x-y) >= z; }

在某些平台上,如果x-y之间的差异不能计算为int,那么计算一个" wrap"会更快。 x-y的两个补码值并对其进行比较,而在其他情况下,使用大于int的类型执行计算会更快并进行比较。然而,到了20世纪90年代末,几乎每个C编译器都会实现上述代码,以使用其中一种方法中的任何一种在其硬件平台上更高效。

然而,自2010年以来,编译器编写者似乎采取的态度是,如果计算溢出,编译器不应该以他们的平台正常的任何方式执行计算,让发生的事情发生,也不应该识别陷阱(这会打破一些代码,但可以防止某些错误的程序行为),但相反,他们应该溢出作为否定时间和因果关系法则的借口。因此,即使程序员对1990年代编译器产生的任何行为都非常满意,程序员也必须用以下代码替换代码:

{ return ((long)x-y) >= z; }

会大大降低许多平台的效率,或

{ return x+(INT_MAX+1U)-y >= z+(INT_MAX+1U); }

这需要指定程序员实际上并不想要的一堆计算,希望优化器会省略它们(使用带符号的比较来使它们不必要),并且会降低许多平台(尤其是DSP)的效率)使用(long)的表单效率会更高。

如果有标准配置文件允许程序员使用INT_MAX+1U来避免像上面那样令人讨厌的可怕的kludges,那将会很有帮助,但如果趋势继续下去,它们将变得越来越必要。

答案 2 :(得分:0)

大多数编译器文档都包含一个描述依赖于实现的功能的特定行为的部分。你能指出gcc或msvc文档的那一部分来描述你的假设吗?

答案 3 :(得分:0)

您可以编写一个标题文件"document.h"来收集所有假设 然后,在您知道非标准假设的每个文件中,您可以#include这样的文件 也许"document.h"根本不会有真正的句子,只会评论文本和一些宏。

   // [T] DOCUMENT.H
   //

   #ifndef DOCUMENT_H
   #define DOCUMENT_H 
   // [S] 1. Basic assumptions.
   // 
   // If this file is included in a compilation unit it means that
   // the following assumptions are made:
   //      [1] A char has 8 bits.
   // [#]

   #define MY_CHARBITSIZE 8

   //      [2] IEEE 754 doubles are addopted for type: double.
   // ........
   // [S] 2. Detailed information
   //
   #endif

括号中的标记:[T] [S] [#] [1] [2]代表:

* [T]: Document Title
* [S]: Section
* [#]: Print the following (non-commented) lines as a code-block.
* [1], [2]: Numbered items of a list.

现在,这里的想法是以不同的方式使用文件“document.h”:

  • 解析文件以将“document.h”中的注释转换为某些可打印文档或一些基本HTML。

因此,标签[T] [S] [#]等旨在由解析器解释,该解析器将任何注释转换为HTML文本行(例如),并生成<h1></h1>, <b></b>(当标签出现时,或者你想要的任何东西。

如果您将解析器保持为一个简单的小程序,这可以让您轻松处理这种文档。