既不是句法也不是语义的错误?
Posted
技术标签:
【中文标题】既不是句法也不是语义的错误?【英文标题】:Error that is neither syntactic nor semantic? 【发布时间】:2014-02-14 20:23:53 【问题描述】:我在家庭作业中遇到了这个问题(别担心,已经完成了):
[使用你最喜欢的命令式语言,举一个例子 每个 ...] 编译器既不能捕获也不能轻松生成代码的错误 catch (这应该是违反语言定义的,而不仅仅是 程序错误)
来自“编程语言语用学”(第 3 版)Michael L. Scott
我的回答是,通过传入相同的参数(在 C 和 Java 中)从 main
调用 main
,灵感来自 this。但我个人觉得这只是一个语义错误。
对我来说,这个问题是在问如何产生一个既不是句法也不是语义的错误,坦率地说,我真的想不出它不会落入的情况。
会不会是容易被利用的代码,比如缓冲区溢出(可能还有其他我从未听说过的利用)?语言结构中的某种坑(IDK,但惰性评估/弱类型检查)?我想要一个 Java/C++/C 中的简单示例,但欢迎使用其他示例。
【问题讨论】:
在 Java 中,几乎所有错误都由异常发出信号。 这个问题似乎是题外话,因为它属于programmers.stackexchange.com 【参考方案1】:Undefined behaviour 浮现在脑海中。调用 UB 的语句在语法和语义上都不是错误的,而是代码的结果无法预测并被认为是错误的。
这方面的一个例子是(来自***页面)尝试修改字符串常量:
char * str = "Hello world!";
str[0] = 'h'; // undefined-behaviour here
但并非所有 UB 语句都那么容易识别。例如,在这种情况下考虑有符号整数溢出的可能性,如果用户输入的数字太大:
// get number from user
char input[100];
fgets(input, sizeof input, stdin);
int number = strtol(input, NULL, 10);
// print its square: possible integer-overflow if number * number > INT_MAX
printf("%i^2 = %i\n", number, number * number);
这里不一定有有符号整数溢出。由于涉及用户输入,因此无法在编译或链接时检测到它。
【讨论】:
char * str = "Hello world!";
不应该在 C++ 中编译(不确定 C),因为字符串文字的类型是 const char *
(实际上它通过数组到指针的转换衰减到那个...)
@MFH 在 C 中,字符串文字没有 const
类型。 (不过,您仍然不能实际修改它们。)
@MFH:在 C++ 中,字符串文字的类型是“const char
的数组”,但在 C 中是“char
的数组”。因此,在 C++03 中,为了与 C 兼容,存在从字符串文字到 char*
的隐式转换。这种兼容性在 C++03 中已弃用,并在 C++11 中删除。因此,如果使用 C++11,“不应该在 C++ 中编译”是正确的,而大多数人不是这样。或者如果使用例如-Wwrite-strings -Werror
,大多数人不是但也许应该是;-)【参考方案2】:
调用未定义行为1的语句在语义上和语法上都是正确的,但会使程序行为不规律。
a[i++] = i; // Syntax (symbolic representation) and semantic (meaning) both are correct. But invokes UB.
另一个例子是使用指针而不对其进行初始化。逻辑错误既不是语义也不是句法。
1。 Undefined behavior:任何事情都有可能发生;该标准没有规定任何要求。程序可能无法编译,也可能执行不正确(崩溃或默默地生成错误结果),或者它可能恰好按照程序员的意图执行。
【讨论】:
我看到这个也在@Kninnug提供的***页面中,我没有想到有UB这么简单的声明。 @SGM1 更简单的是i = i++;
:)
编译器可以捕捉到这个例子,只是一般他们不会打扰。所以对这个问题有一些解释,它是否想要一个编译器必然无法捕捉的例子,或者只是一个你的编译器因为不够聪明而无法捕捉的例子......跨度>
@SteveJessop;但是,由于这些语句在语法上(语义)是正确的,因此编译器不会将它们作为错误(通常)提出。
@SteveJessop 这也是由于未定义行为的定义:“行为,在使用不可移植或错误的程序构造或错误数据时,本国际标准没有要求”。编译器不需要引发错误,甚至可以make demons come out of your nose。【参考方案3】:
这是 C++ 的示例。假设我们有一个函数:
int incsum(int &a, int &b)
return ++a + ++b;
那么下面的代码有未定义的行为,因为它修改了一个对象两次,没有中间的序列点:
int i = 0;
incsum(i, i);
如果对 incsum
的调用与函数的定义在不同的 TU 中,则 不可能 在编译时捕获错误,因为这两个代码本身都不是错误的自己的。它可以在链接时被足够智能的链接器检测到。
您可以生成任意数量的此类示例,其中一个 TU 中的代码具有对于另一个 TU 传递的某些输入值有条件地未定义的行为。我选择了一个有点模糊的,你可以很容易地使用无效的指针取消引用或有符号整数算术溢出。
您可以争辩说生成代码来捕获它是多么容易——我不会说这很容易,但是如果a
和b
为同一个对象别名,编译器可能会注意到++a + ++b
无效,并在该行添加 assert (&a != &b);
的等价物。因此可以通过局部分析生成检测代码。
【讨论】:
这不是 C,这是 C++。 C 没有引用。 @ElchononEdelson:谢谢,已修复。我不知道我为什么写“C”:-)以上是关于既不是句法也不是语义的错误?的主要内容,如果未能解决你的问题,请参考以下文章