标准 C 中的约束是啥?
Posted
技术标签:
【中文标题】标准 C 中的约束是啥?【英文标题】:What are the Constraints in Standard C?标准 C 中的约束是什么? 【发布时间】:2016-01-29 07:12:17 【问题描述】:C 标准谈论约束,例如。 G。 ISO/IEC 9899:201x 定义了术语
约束 限制,无论是句法还是语义,通过它 语言元素的阐述将被解释
并在符合性
一章中说如果“应”或“不应”要求出现在 约束或运行时约束被违反,行为是 未定义。
在Environment一章的Diagnostics小节中提到
符合要求的实现应至少产生一个诊断 消息(以实现定义的方式标识)如果 预处理翻译单元或翻译单元包含一个 违反任何语法规则或约束,即使行为是 也明确指定为未定义或实现定义。
因此,了解 C 中的约束是很重要的,例如,编译器编写者可以判断何时需要诊断,或者 C 程序员何时可以预期诊断,而不仅仅是未定义的行为。 现在,标准文档中到处都有标题为 Constraints 的部分,但我无法找到明确的措辞来说明标准中的术语 constraint 究竟涵盖了哪些内容。
标题是约束部分中出现的所有约束吗? 在这些部分之外陈述的每个要求都不是限制条件吗? 我错过的标准中有没有对约束的全面描述?【问题讨论】:
标准中有趣的问题和有趣的措辞。从字面上看,这听起来好像违反约束内的“应 [not]”不一定是 UB,但我认为它们的意思是它始终是 UB,并附加要求“约束”违反需要实施强制诊断。跨度> 【参考方案1】:所有的约束都出现在标题为“约束”的部分中吗?
在 n1570 3.8 的意义上(对程序施加的限制,需要符合要求的实现在违反时发出编译时诊断消息),我认为是的。
在这些部分之外陈述的每个要求都不是约束吗?
在 3.8 的意义上,我认为是的,但出于更循环的原因:标准的结构相当正式。只要适用,似乎就有一个明确的 Constraints 部分。因此,我理解 根据定义 任何不在 Constraints 部分中的东西都不是 3.8 意义上的约束。Constraints 部分之外有一些“shall”子句,它们看起来完全可以在编译时强制执行,参见。下面举几个例子。它们通常位于相邻的语义部分中。我可能会遗漏在一般情况下阻止编译时检测的细微之处(因此不能强制进行诊断),或者标准可能不完全一致。但我认为编译器可以简单地翻译违规程序,正是因为要求不在约束部分中。
我错过的标准中有没有对约束的全面描述?
我认为 3.8 就是你得到的全部。我尝试探索以下术语并同意该定义不令人满意。
我深入研究了标准以找出答案。这是我的研究。
术语约束
让我们从基础开始。您引用的 3.8 中“约束”的定义令人惊讶地难以理解,至少在没有上下文的情况下(“限制,句法或语义,通过它来解释语言元素的阐述”)。 “限制”和“约束”是同义词,因此重新措辞并没有增加太多; “语言元素的展示”是什么意思?说明是一个有多种含义的词;让我们以Dictionary.com 的“主要用于传达信息的写作或演讲”为例,假设它们的意思是标准。那么它基本上意味着该标准中的 约束 是对该标准中所说内容的约束。哇,我没想到。
约束 根据 3.8
只要检查标准中的实际约束部分,就会发现它们列出了对符合程序施加的编译时间限制。这是有道理的,因为只能在编译时检查编译时约束。 这些附加限制是那些不能用 C 语法表达的限制。1
约束部分之外的约束
约束部分之外的“应”的大多数用法对符合的实现施加限制。示例:“所有具有静态存储持续时间应初始化(设置为 初始值)在程序启动之前”,一个符合要求的实现的工作。
在 Constraints 部分之外,有一些“shall”子句对 program(不是实现)施加了限制。我认为大多数与 3.18 中提到的“调用库函数时程序上的运行时约束 [...]”属于同一类别。它们似乎是在编译时通常无法检测到的运行时限制(因此诊断不是强制性的)。
这里有几个例子。
在 6.5/7 n1570 中详细介绍了备受争议的别名规则:
一个对象只能访问其存储的值 通过具有以下之一的左值表达式 以下类型:
与对象的有效类型兼容的类型 类型兼容的合格版本 与对象的有效类型, [...]
在 6.5.16.1 中,“简单赋值”:
如果存储在一个对象中的值是从另一个以任何方式重叠的对象中读取的 第一个对象的存储,那么重叠应该是精确的[..]。”
其他示例涉及指针算法 (6.5.6/8)。
Shall 子句可以在 Constraints 部分中
但是还有其他的 should 子句,它们的违反应该在编译时被检测到;如果它们出现在相应的约束部分,我不会眨眼。
6.6/6, "整数常量中的转换运算符 表达式只能将算术类型转换为整数类型”(在“语义”下);如果您无法检测常量和强制类型转换,您可以在编译时检测到什么? 6.7/7,“如果声明对象的标识符时没有链接,则对象的类型应在其声明符的末尾完成”(在“语义”下)。对我来说,这似乎是一项基本的编译器任务,用于检测代码中某个类型是否完整。当然,我从来没有写过 C 编译器。还有几个例子。但正如我所说,我认为诊断违规行为不需要实现。设法偷偷溜过编译器的违规程序只会暴露未定义的行为。
1 例如,我知道语法不处理类型——它只有通用的“表达式”。因此,每个运算符都有一个 Constraints 部分,详细说明其参数的允许类型。移位运算符的示例:“每个操作数都应具有整数类型。” 尝试对浮点数进行移位的程序违反了此约束,实现必须发出诊断。子>
【讨论】:
感谢您的详尽回答;这听起来是决定性的。您会(反对)批准以下两个细节吗? 1. 由于 6.3.2.2 中的要求“void 表达式(具有void
类型的表达式)的(不存在的)值不得以任何方式使用,隐式或显式转换(除了void
) 不应应用于这样的表达式”出现在约束部分之外,对 void 表达式的值的赋值不会违反约束,对吧? (因此不需要诊断,尽管任何合理质量的每个实施都会产生一个。)
2.考虑 7.7 浮动类型的特征 float.h
再次:这些是对符合的实现施加的明确要求/限制,与对符合程序施加限制的3.8意义上的“约束”无关. (即那些在语法中无法表达的限制,例如运算符的参数可以具有哪些类型——我知道 C 语法描述不处理类型)。
约束只对严格符合 C 程序的限制。【参考方案2】:
C 委员会在回复 Defect Report # 033 时解决了这个问题。该缺陷报告中的问题是:
是否需要符合标准的实现来诊断标准中所有违反“应”和“不应”语句的行为,即使这些语句出现在标记为“约束”的部分之外?
该缺陷报告的作者提出了几种解释标准语言的可能替代方法。他列出的第二种选择(部分)说:
语法规则是标准的语法部分中列出的那些项目。 约束是标准的约束部分中列出的那些项目。
委员会的部分回应是:
建议的解释 #2 是正确的。
我相信这相当完整地涵盖了您的问题,但只是为了更直接地回答您的问题:
所有的约束都出现在标题为“约束”的部分中吗? 在这些部分之外陈述的每个要求都不是限制条件吗?
“约束”是在明确标记为“约束”的部分中说明的要求。在此类部分之外说明的任何要求都不是约束。
我错过的标准中是否有全面的约束描述?
至少据我所知,标准本身不包含关于什么是或不是约束的更具体的声明,但链接的缺陷报告包含。
【讨论】:
【参考方案3】:所有的约束都出现在标题为“约束”的部分中吗?
似乎它们大多是(有些情况不是,fx:在其中一个约束部分中声明“递增等于加 1”)。
在这些部分之外陈述的每个要求都不是约束吗?
我没有看到这些部分之外的“约束”。
我错过的标准中是否有关于约束的全面描述?
可能不会,如果有这样的权威,它将在标准中并且可能是“约束”部分(并明确提到这些都是“约束”)。
我的解释是,第 3 章的解释应使定义的术语的每次使用都具有该部分中定义的含义。尤其是在使用“约束”一词的任何地方,都应该根据您的第一句话来理解。
您的第二个报价也不例外。在术语“约束”的定义中指出,不要求将约束明确称为约束。这意味着您必须通过检查它是否是这样的限制来确定它是否是“限制”。
然而,似乎有相当多的“应该”和“不应”的例子可以被认为是这样的限制,而没有明确地这样称呼。这将使所有发生的“应”和“不应”强制或禁止实现的某种行为 - 如果这些没有实现,那么是的,行为可能是未定义的(因为您使用的实现不t符合标准)。
看起来符合“约束”定义的所有内容似乎都出现在“约束”部分下,而“约束”部分中的所有内容似乎都是“约束”。
【讨论】:
感谢您的回答。你说我没有看到这些部分之外的“约束”。但是考虑例如 7.7 浮动类型的特征所有的约束都出现在标题为约束的部分吗?
是的。标准中提到的每个句法和语义限制都是约束。
例如,对常量表达式的约束 (C11-6.6/3):
常量表达式不应包含赋值、递增、递减、函数调用或逗号运算符,除非它们包含在未计算的子表达式中。115)
因此,常量表达式
3 = 5;
10++;
显示违反约束。
请注意,在这种情况下,shall 要求以及 constraint 都被违反了。
在这些部分之外陈述的每个要求都不是约束吗?
对于符合标准的 C,是的。 A shall 对整数常量表达式的要求 (C11-6.6/6):
整数常量表达式117)应具有整数类型 [...]
例如,非可变长度数组的大小需要整数常量表达式。因此,
int arr[5+1.5];
违反应要求。表达式5+1.5
的类型不是整数类型。这个shall的要求是不受约束的。
需要注意的是,shall 要求也可能是一个约束。
【讨论】:
也许是因为最后一句话没有多大意义。问题是关于标准,而不是任何(可能是不符合标准的)实施甚至程序。 [否则我认为你是正确的,3.8 意义上的所有约束。在 n1570 中是明确标记为“约束”的内部部分。] @PeterSchneider;什么?你的评论没有多大意义。最后一个问题的不确定答案可能是no。 感谢您的回答(我没有投反对票)。例如,这意味着分配 void 表达式 的值不会违反约束,对吧? OP 的问题是“是否每个要求 [出现在名为“ISO C 标准”的书中] 在这些部分之外[标记为“约束”]不是约束?”换句话说,标准中部分的内容是标有“约束”的所有且仅是标准中的所有约束。您的回答“对于符合标准的 C,是的”不适用。我们不是在谈论 C,我们在谈论特定的书。答案只能是“是”或“否”,或者“我不知道”。 (哦,我也没有投反对票,只是想提供帮助。) @PeterSchneider;那本特定的书是标准确认,即我说的是 ISO C11 标准。【参考方案5】:在我的需求工程工作中,“约束”和“需求”这两个词有不同的范围。对于标准来说,明确定义这些也很重要。我在标准中搜索了“约束”一词,似乎可以得出以下结论:
约束是对标准部分描述的行为的输入(前置条件)或输出(后置条件)的限制。对于输入,这意味着输入必须遵守约束(例如,argc
应为正数)。对于输出,这意味着它必须满足标准的任何后续单元的约束才能具有明确定义的输入(其前提条件)。
要求是标准部分行为规范的一部分。 “Shall”是对所需内容的正面描述; “不应”通常是一种限制,但不是一种约束——它可能会参与满足其输出的约束。
约束和要求可以看作是“外部接口”(约束)和“系统行为/处理”(要求)。
Shall 通常表示要求(没有“shall”的短语因此不是要求)。然后,约束中使用的“应”用于定义输入或输出(例如,argc
应为正数)或指定有关验证约束的行为(例如“...应给出诊断消息”)。
严格来说,用于指定验证输入约束的行为的“shall”不应列在约束部分(不应列在接口规范中),而应列在处理部分(行为部分)中。
请注意,不能验证输出约束,因为输出应符合规范;如果这些约束在其输入约束中,则只有下一个 uit 可以检查这些约束。
这可能是个人观点,但似乎符合标准中这些词的用法。
【讨论】:
感谢您的回答。这似乎是一个连贯的观点,但恐怕它不太符合标准。例如,您说 对于输入,这意味着它可以假设满足约束……但可以检查约束(它不必),而标准说 符合要求的实现应产生至少一个诊断消息……如果预处理翻译单元或翻译单元包含违反任何语法规则或约束(因此它必须检查)。 它还谈到了出现在约束之外的“应”或“不应”要求,这表明要求很可能会出现在约束内,这听起来像术语 constraint 的含义与您所描述的不同。 注意:“argc
应为正数”是一个有问题的示例“argc
的值应为非负数。” C11 5.1.2.2.1 2 - 它可能是 0。【参考方案6】:
约束
句法或语义上的限制, 语言元素的阐述将被解释
这意味着c标准以任何方式设置的程序逻辑或语法的每个显式限制都是一个约束。这包括语法约束(例如,块必须以;
终止)和语义约束(例如,在初始化变量之前不得使用变量),基本上是语法(符号)或语义(使用正确符号)的所有内容-wise) 不允许或定义为不允许(未定义的行为)。
在这些部分之外陈述的每个要求不是 约束?
我确实认为 C 语言编程的所有明确要求都属于句法或语义约束。
标准中是否有对约束的全面描述 我错过了什么?
据我所知。
【讨论】:
仅仅因为一个约束是一个约束,那么是否每个约束都是一个约束? @Columbo 我确实认为每个明确的限制都是一个约束。你有反例吗? 感谢您的回答(我没有投反对票)。这似乎是一个合理的观点。但是,也有相反的观点,例如。 G。 All constraints are explicitly defined in the standard, in paragraphs clearly marked "Constraints". @Armali 语言律师通常如此,这仅在边缘案件中很重要。【参考方案7】:标准中约束的目的是指定符合要求的实现需要发出诊断的条件,或者允许实现以与在没有约束的情况下所需的方式相反的方式处理程序可能比其他指定的行为有用。虽然严格符合 C 程序不允许违反约束(没有违反约束的程序是严格符合 C 程序),但没有此类限制适用于旨在符合但不是严格符合的程序。
C 标准是作为多个重叠派系之间的妥协而编写的,包括
-
那些认为它应该阻止程序员编写不能在所有平台上互换使用的代码的人
那些认为它应该允许以已知平台为目标的程序员利用他们需要支持的所有平台共有的功能,即使它们不能在所有平台上都支持
那些认为应该允许编译器诊断构造和操作的人,这些构造和操作会更频繁地被意外执行而不是故意执行
那些认为它应该允许程序员执行诸如执行地址计算之类的事情的人,这些事情看起来是错误的,但如果按照指定的方式精确执行,就会产生程序员所期望的对象的地址。
为了在这些群体之间达成共识,标准对在严格符合 C 程序中可以做的事情施加了限制,但同时也将符合 C 程序的定义写得足够宽泛,几乎没有有用的程序会被标记为不符合无论他们所依赖的扩展多么晦涩难懂。如果源代码构造违反了可诊断的约束,但实现的客户无论如何都会发现它有用,那么实现可以输出其客户可以忽略的诊断(甚至是无条件的:“警告:此实现不会打扰输出诊断它的作者认为是愚蠢的,除了这个“就足够了),每个人都可以继续生活。
【讨论】:
但是“严格遵守的程序应仅使用本国际标准中指定的语言和库的那些特性”本身就不受约束。似乎编译器可以将每个程序视为严格符合程序,并且由于它违反了这个应该,所以说这个程序有未定义的行为,然后做任何编译器喜欢的事情。跨度> @TuffContender:符合但无用的编译器可能会说“如果这个程序匹配一个名义上行使翻译限制的特定无用程序,则表现得像那个程序,否则会以如下方式轰炸堆栈:实现与该程序无法区分的结果”,这在 as-if 规则下将允许它简单地表现得像无用的程序,甚至无需查看源代码。从严格规范的角度来看,该标准确实对严格符合程序之外的任何内容提出了任何要求。 这是C标准的措辞问题吗?在 C++ 草案中,“shall”表示“必须”,“behavior”的范围在程序中是有限的。我无法想象 C 标准中规定的实现的未定义行为是什么。例如,“每个枚举类型......应该能够表示枚举的所有成员的值”,允许实现违反这个“应该”并将其视为未定义的行为,这很奇怪。我认为该标准应关注符合标准的实现并提及符合或不符合标准的程序。 @TuffContender:我认为标准是否应该指定一种完全有用的语言,或者是针对不同平台和目的实现的一组核心语言特性,我认为从来没有达成共识将根据这些平台和目的进行适当扩展。据我所知,对于标准的哪些方面应该是规范性的,哪些部分只是指导方针,也没有达成共识。除非或直到标准的作者能够就这些问题达成共识,否则将不可能有一个有用且有意义的规范性标准。 @TuffContender:在 C 标准中最有用的是它的许多部分用强烈的建议替换大多数实现要求,但添加一个要求,即偏离推荐实践的实现记录任何这样的偏差。以上是关于标准 C 中的约束是啥?的主要内容,如果未能解决你的问题,请参考以下文章
请问C语言中的:stdio.h是啥意思,说仔细些,我刚自学的,不懂,还请指教