C# 中具有未定义行为的代码

发布于 2024-08-13 23:46:46 字数 177 浏览 3 评论 0原文

在 C++ 中,您可以通过多种方式编写可编译的代码,但会产生未定义行为(维基百科)。 C#中有类似的东西吗?我们可以用 C# 编写可以编译但具有未定义行为的代码吗?

In C++ there are a lot of ways that you can write code that compiles, but yields undefined behavior (Wikipedia). Is there something similar in C#? Can we write code in C# that compiles, but has undefined behavior?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

暖心男生 2024-08-20 23:46:46

正如其他人提到的,“不安全”块中的几乎所有内容都可以产生实现定义的行为;滥用不安全块允许您更改构成运行时本身的代码字节,因此所有的赌注都将落空。

除法int.MinValue/-1具有实现定义的行为。

抛出异常并且从不捕获它会导致实现定义的行为——终止进程、启动调试器等等。

在 C# 中还有许多其他情况,我们被迫发出具有实现确定行为的代码。例如,这种情况:

https: //learn.microsoft.com/en-us/archive/blogs/ericlippert/odious-ambigously-overloads-part-two

但是,安全、行为良好的 C# 程序具有实现定义的行为的情况应该是相当罕见的。

As others have mentioned, pretty much anything in the "unsafe" block can yield implementation-defined behaviour; abuse of unsafe blocks allows you to change the bytes of code that make up the runtime itself, and therefore all bets are off.

The division int.MinValue/-1 has an implementation-defined behaviour.

Throwing an exception and never catching it causes implementation-defined behaviour -- terminate the process, start a debugger, and so on.

There are a number of other situations in C# where we are forced to emit code which has implementation-determined behaviour. For example, this situation:

https://learn.microsoft.com/en-us/archive/blogs/ericlippert/odious-ambiguous-overloads-part-two

However, the situations in which a safe, well-behaved C# program has implementation-defined behaviour should be quite rare.

手心的海 2024-08-20 23:46:46

是的!即使在安全的情况下也是如此! (嗯,至少它的实现定义为未定义)

这是 Marek Safar 和 VSadov 在 Roslyn 问题。C# 和 CLI 在 bool 方面不匹配。

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

有趣的是,调试器不同意(必须以不同的方式评估真实性?)

在此处输入图像描述

无论如何,C# 团队似乎得出的结论是(强调):

IE 语言将完全不关心非标准布尔值。特定的实现(如 CIL 上的 MS C# 中)将承认非标准布尔值的存在,并将其行为指定为 未定义

Yes! There is, even in a safe context! (Well, it's implementation defined to be undefined, at least)

Here's one from Marek Safar and VSadov in the Roslyn issues.There is a mismatch between C# and the CLI in regards to bool.

C# believes that there is only one kind of true, and one kind of false.

CLI believes that false is a byte containing 0, and all other values are true.

This discrepancy means we can coerce C# to do some a (marginally) interesting things thing:

//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]);

The above outputs:

true

true

false

Interestingly, the debugger disagrees (must evaluate truth differently?)

enter image description here

Anyways, the conclusion the C# team appears to have come to is (emphasis added):

I.E. the language will stay entirely unconcerned about nonstandard bools. The particular implementation (as in MS C# on CIL) will acknowledge the existence of nonstandard bools and specify their behavior as undefined

贱贱哒 2024-08-20 23:46:46

查看关于未定义行为的维基百科文章,发生未定义行为的情况要么是不允许的或者在 C# 中抛出异常。

然而,在不安全的代码中,我认为未定义的行为是可能的,因为这允许您使用指针等。

编辑:看起来我是对的:http://msdn.microsoft.com/en-us/library/aa664771%28VS.71%29.aspx

有一个C# 中未定义行为的示例

Looking at the Wikipedia article on undefined behaviour, the situations in which undefined behavior happens are either not allowed or throw an exception in C#.

However in Unsafe code, undefined behavior I believe is possible, as that allows you to use pointers etc.

Edit: It looks like I'm right: http://msdn.microsoft.com/en-us/library/aa664771%28VS.71%29.aspx

Has an example of undefined behavior in c#

温柔女人霸气范 2024-08-20 23:46:46

根据 ECMA-334 文件(第 473 页):

一个不包含任何内容的程序
unsafe 修饰符的出现
不能展示任何未定义的
行为。

这将“实现定义”提升到最坏的情况,请参阅 Eric Lippert 的回答。

According to the ECMA-334 document (p. 473):

A program that does not contain any
occurrences of the unsafe modifier
cannot exhibit any undefined
behavior.

That promotes 'implementation-defined' to the worst case, see Eric Lippert's answer.

记忆里有你的影子 2024-08-20 23:46:46

许多子程序都有要求,可以概括为:

  1. 当给定有效数据时,产生有效输出。

  2. 即使输入无效,也不要发射核导弹或否定时间和因果律。

    即使输入无效,

Java 和 .NET 语言的主要设计目标之一是,除非代码使用某些标记为“不安全”的内容,否则通常不需要特别努力来满足上述第二个约束[尽管与垃圾收集和 <从时间/因果关系的角度来看,code>Finalize 可能有点奇怪,这些可以被描述为正常因果关系规则的例外,而不是完全撤销它们]。这种情况与 C 中的情况非常不同,在 C 中,许多种与数据相关的错误(例如整数溢出)可能导致编译器以任意方式运行,包括做出任何必要的假设来避免溢出。超现代 C 哲学所鼓励的真正可怕的未定义行为在“不安全”块之外的 C# 或其他 .NET 语言中并不存在。

Many and subprograms have requirements that can be summarized as:

  1. When given valid data, produce valid output.

  2. Refrain from launching nuclear missiles or negating the laws of time and causality, even when given invalid input.

One of the major design goals of Java and .NET languages is that unless code makes use of certain which are marked as "unsafe", no particular effort is generally required to meet the second constraint above [though some behaviors related to garbage collection and Finalize can be a little weird from the time/causality standpoint, those can be described as exceptions to normal rules of causality, rather than a total revocation of them]. That situation is very different from the situation in C, where many kinds of data-dependent errors (e.g. integer overflow) may result in compilers behaving in arbitrary fashion including making whatever assumptions would be necessary to avoid overflow. The truly horrible kinds of Undefined Behavior which are encouraged in hypermodern C philosophy do not exist in C# or other .NET languages outside of "unsafe" blocks.

和影子一齐双人舞 2024-08-20 23:46:46

不完全是维基意义上的,但我想我想到的最明显的例子就是简单地编写一些线程代码,但在任何语言中都是如此。

Not really in the exactly Wiki sense but I suppose the most obvious example that comes to my mind is simply writing some threaded code, but then it's like that in any language.

怂人 2024-08-20 23:46:46

C# 规范列出了更多未定义、实现定义和未指定的行为:问题” rel="nofollow noreferrer">https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specation/portability-issues ...这些在规格如下:

行为,实现定义 - 未指定的行为,其中每个实现都记录如何做出选择

行为,未定义 - 使用不可移植或错误构造或错误数据时的行为,本规范对此没有强加要求

行为,未指定 - 本规范提供两种或多种可能性并且对任何实例中选择的行为不施加进一步要求的行为

https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/terms-and-definitions

规范不断发展,对于 StackOverflow 的答案来说,列表太长,而且它并不包罗万象。由于规范只有一个术语“未定义行为”(“未定义行为在本规范中仅由‘未定义行为’一词表示。”)谷歌搜索“未定义”站点:https://learn.microsoft.com /en-us/dotnet/csharp/language-reference/" 揭示了一些有趣的花絮:

跨部分类的字段初始化未定义

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

模式评估顺序未定义

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

此外,关于列表模式:

对所使用的成员做出以下假设:

当且仅当类型可索引时,假定使类型可计数的属性始终返回非负值。例如,模式 { Length: -1 } 永远无法匹配数组。

假设使类型可切片的成员行为良好,即返回值永远不会为 null,并且它是包含列表的正确子切片。

如果上述任何假设不成立,则模式匹配操作的行为是未定义的。

https://learn。 microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-11.0/list-patterns

同样,如果集合行为不佳,则集合文字行为未定义:

假定集合行为良好。例如:

假设集合上的 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 的类型)的值均为 null。所有值类型都具有 0 位模式。这意味着 Nullable.HasValue 属性为 false,并且 Nullable.Value 属性未定义。在 .NET 实现中,Value 属性会引发异常。

The C# spec lists a few more undefined, implementation-defined, and unspecified behaviors: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/portability-issues ... these are defined in the spec as follows:

behavior, implementation-defined – unspecified behavior where each implementation documents how the choice is made

behavior, undefined – behavior, upon use of a non-portable or erroneous construct or of erroneous data, for which this specification imposes no requirements

behavior, unspecified – behavior where this specification provides two or more possibilities and imposes no further requirements on which is chosen in any instance

https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/terms-and-definitions

The spec evolves and the list is too long for a StackOverflow answer, plus it's not all-encompassing. As the spec only has one term for "undefined behavior" ("Undefined behavior is indicated in this specification only by the words ‘undefined behavior.’") googling "undefined" site:https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/" reveals some interesting tidbits:

Field Initialization across Partial Classes is Undefined

Field initialization order can be significant within C# code, and some guarantees are provided, as defined in §15.5.6.1. Otherwise, the ordering of members within a type is rarely significant, but may be significant when interfacing with other languages and environments. In these cases, the ordering of members within a type declared in multiple parts is undefined.

Pattern Evaluation Order is Undefined

The order in which patterns are checked is undefined. At run time, the right-hand nested patterns of or and and patterns can be checked first.

Furthermore, regarding list patterns:

The following assumptions are made on the members being used:

The property that makes the type countable is assumed to always return a non-negative value, if and only if the type is indexable. For instance, the pattern { Length: -1 } can never match an array.

The member that makes the type sliceable is assumed to be well-behaved, that is, the return value is never null and that it is a proper subslice of the containing list.

The behavior of a pattern-matching operation is undefined if any of the above assumptions doesn't hold.

https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-11.0/list-patterns

Likewise, Collection Literal behavior is undefined if collections are not well-behaved:

Collections are assumed to be well-behaved. For example:

It is assumed that the value of Count on a collection will produce that same value as the number of elements when enumerated.

The types used in this spec defined in the System.Collections.Generic namespace are presumed to be side-effect free. As such, the compiler can optimize scenarios where such types might be used as intermediary values, but otherwise not be exposed.

It is assumed that a call to some applicable .AddRange(x) member on a collection will result in the same final value as iterating over x and adding all of its enumerated values individually to the collection with .Add.

The behavior of collection literals with collections that are not well-behaved is undefined.

https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-12.0/collection-expressions

default(Nullable).Value is implementation-defined (not undefined)

The elements of an array can be initialized to known values when the array is created. Beginning with C# 12, all of the collection types can be initialized using a Collection expression. Elements that aren't initialized are set to the default value. The default value is the 0-bit pattern. All reference types (including non-nullable types), have the values null. All value types have the 0-bit patterns. That means the Nullable.HasValue property is false and the Nullable.Value property is undefined. In the .NET implementation, the Value property throws an exception.

羁拥 2024-08-20 23:46:46

一般来说我会说不。

在初始化之前使用自动变量。

所有变量都必须初始化。如果没有出现异常。

除以零

抛出异常。

对数组进行索引越界

抛出异常

正如 Aequitarum Custos 指出的那样,您可以使用不安全的代码。话又说回来,这并不是真正的 C#,您明确选择退出 C# 环境。

In general I would say no.

Use Automatic variable before it’s initialized.

All variables must be initialized. If not an exception occurs.

Division by zero

Exception is thrown.

Indexing an array out of bounds

Exception is thrown

As Aequitarum Custos pointed out you can use unsafe code. Then again this isn't really C#, you are explicitly opting out of the C# environment.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文