c++ C#中未定义行为的代码

doinxwow  于 12个月前  发布在  C#
关注(0)|答案(8)|浏览(173)

在C++中,有很多方法可以编写编译代码,但产生undefined behavior (Wikipedia)。C#中是否有类似的东西?我们可以用C#编写编译代码,但具有未定义的行为吗?

thigvfpy

thigvfpy1#

一般来说,我会说没有。

在初始化前使用Automatic变量。

所有的变量都必须初始化,否则会发生异常.

除以零

异常被抛出。

数组索引越界

引发异常
正如Aequitarum Custos指出的那样,你可以使用不安全的代码,但这又不是真正的C#,你是在明确地选择退出C#环境。

mi7gmzs6

mi7gmzs62#

正如其他人所提到的,“unsafe”块中的几乎任何东西都可以产生实现定义的行为;滥用unsafe块允许您更改构成运行时本身的代码字节,因此所有的赌注都被取消了。
除法int.MinValue/-1具有实现定义的行为。
抛出异常而不捕获它会导致实现定义的行为--终止进程、启动调试器等等。
在C#中还有许多其他情况,我们被迫发出具有实现决定行为的代码。例如,这种情况:
https://learn.microsoft.com/en-us/archive/blogs/ericlippert/odious-ambiguous-overloads-part-two
然而,一个安全的、行为良好的C#程序具有实现定义的行为的情况应该是非常罕见的。

vi4fp9gy

vi4fp9gy3#

是的!有,甚至在安全的上下文中!(好吧,它的实现定义为未定义,至少)
下面是Marek Safar和VSadov在Roslyn issues中的一个例子。关于bool,C#和CLI之间存在不匹配。
C#认为只有一种true和一种false
CLI认为false是一个包含0的字节,所有其他值都是true
这种差异意味着我们可以强迫C#做一些(稍微)有趣的事情:

//non-standard bool
//We're setting a bool's value to a byte value of 5.
var a = new bool[1];
Buffer.SetByte(a, 0, 5);

//non-standard bool
//We're setting a bool's value to a byte value of 10.
var b = new bool[1];
Buffer.SetByte(b, 0, 10);

//Both are true.
Console.WriteLine(a[0]);
Console.WriteLine(b[0]);

//But they are not the same true.
Console.WriteLine(a[0] == b[0]);

字符串
上述产出:
true
true
false
有趣的是,调试器不同意(必须以不同的方式评估真理?)
x1c 0d1x的数据
无论如何,C#团队似乎已经得出的结论是(重点补充):
也就是说,语言将完全不关心非标准的bool。特定的实现(如在CIL上的MS C#中)将承认非标准bool的存在,并将其行为指定为undefined

nnvyjq4y

nnvyjq4y4#

看看Wikipedia article on undefined behaviour,在C#中,发生未定义行为的情况要么是不允许的,要么是抛出异常。
然而,在不安全的代码中,我相信未定义的行为是可能的,因为这允许你使用指针等。
编辑:看起来我是对的:http://msdn.microsoft.com/en-us/library/aa664771%28VS.71%29.aspx
有一个c#中未定义行为的例子

rhfm7lfc

rhfm7lfc5#

根据ECMA-334文件(第473页):
不包含unsafe修饰符的程序不能显示任何未定义的行为。
这将“实现定义”推向了最坏的情况,请参阅Eric Lippert的回答。

ssgvzors

ssgvzors6#

许多子程序都有要求,可以概括为:
1.当给定有效数据时,产生有效输出。
1.不要发射核导弹,也不要否定时间和因果律,即使输入的是无效的信息。
Java和.NET语言的主要设计目标之一是,除非代码使用某些标记为“不安全”的代码,否则通常不需要特别的努力来满足上述第二个约束[尽管从时间/因果关系的Angular 来看,与垃圾收集和Finalize相关的一些行为可能有点奇怪,这些行为可以被描述为正常因果关系规则的例外,而不是完全撤销它们]。这种情况与C中的情况非常不同,其中有很多种与数据相关的错误(例如整数溢出)可能会导致编译器以任意的方式工作,包括做出任何必要的假设来避免溢出。在超现代的C哲学中鼓励的真正可怕的未定义行为,不存在于C#或其他.NET语言中的“不安全”块之外。

dgsult0t

dgsult0t7#

虽然不是真正意义上的Wiki,但我认为我想到的最明显的例子就是简单地编写一些线程代码,但在任何语言中都是如此。

1zmg4dgp

1zmg4dgp8#

C# 7.0规范列出了一些未定义的,实现定义的和未指定的行为:https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/portability-issues.这些在规范中定义如下:
行为,实现定义的-未指定的行为,其中每个实现记录如何做出选择
行为,未定义-在使用不可移植或错误的构造或错误的数据时的行为,本规范对此没有强制要求
行为,未指定-本规范提供了两种或多种可能性,并且在任何情况下都没有对选择的行为提出进一步的要求
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/terms-and-definitions
规范不断发展,对于StackOverflow的答案来说,列表太长了,而且它并不是包罗万象的。由于规范中只有一个术语“未定义的行为”(“未定义的行为在本规范中仅由”未定义的行为“一词表示。”)谷歌"undefined" site:https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/"揭示了一些有趣的花絮:

字段跨分部类重复未定义

字段初始化顺序在C#代码中可能很重要,并且提供了一些保证,如§15.5.6.1中定义的。否则,类型中成员的顺序很少重要,但在与其他语言和环境交互时可能很重要。在这些情况下,在多个部分中声明的类型中成员的顺序是未定义的。

模式求值顺序未定义

未定义模式的检查顺序。在运行时,可以首先检查或和模式的右侧嵌套模式。

此外,关于列表模式:
对所使用的成员进行以下假设:
当且仅当类型是可索引的时,使类型可计数的属性被假定为总是返回非负值。例如,模式{ Length:-1 }永远不能匹配数组。
使类型可切片的成员被假设为行为良好,也就是说,返回值永远不为null,并且它是包含列表的正确子切片。
如果上面的任何一个假设都不成立,那么模式匹配操作的行为就是未定义的。
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-11.0/list-patterns

同样,如果集合行为不规范,则Collection Literal行为未定义:

集合被假定为行为良好。例如:
假设集合上的Count值将产生与枚举时的元素数相同的值。
在System.Collections.Generic命名空间中定义的此规范中使用的类型被假定为无副作用。因此,编译器可以优化此类类型可能用作中间值但不会公开的情况。
假设对集合中某个适用的.AddRange(x)成员的调用将导致与迭代x并使用. Add将其所有枚举值单独添加到集合中相同的最终值。
对于行为不规范的集合,集合文字的行为是未定义的。
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-12.0/collection-expressions

默认值(可为空)。值是实现定义的(不是未定义的)

数组的元素可以在创建数组时初始化为已知值。从C# 12开始,所有集合类型都可以使用Collection表达式初始化。未初始化的元素设置为默认值。默认值为0位模式。所有引用类型(包括不可为空的类型)的值为null。所有值类型都具有0位模式。这意味着Nullable.HasValue属性为false,并且Nullable.Value属性未定义。在.NET实现中,Value属性引发异常。

相关问题