热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

递增一个空指针是否定义良好?-Isincrementinganullpointerwell-defined?

Therearelotsofexamplesofundefinedunspecifiedbehaviorwhendoingpointerarithmetics-pointe

There are lots of examples of undefined/unspecified behavior when doing pointer arithmetics - pointers have to point inside the same array (or one past the end), or inside the same object, restrictions on when you can do comparisons/operations based on the above, etc.

在做指针运算时,有很多未定义/未指定行为的例子——指针必须指向同一个数组(或一个数组结束后)或同一个对象内,以及基于上述条件进行比较/操作的限制等等。

Is the following operation well-defined?

以下操作是否定义良好?

int* p = 0;
p++;

8 个解决方案

#1


37  

§5.2.6/1:

§5.2.6/1:

The value of the operand object is modified by adding 1 to it, unless the object is of type bool [..]

操作数对象的值通过向其添加1来修改,除非对象类型为bool [..]

And additive expressions involving pointers are defined in §5.7/5:

和添加剂包括指针表达式中定义§5.7/5:

If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is undefined.

如果指针操作数和结果点都指向同一数组对象的元素,或数组对象的最后一个元素之后的元素,则计算不产生溢出;否则,行为就没有定义。

#2


14  

There seems to be quite low understanding what "undefined behaviour" means.

对于“未定义的行为”意味着什么,似乎有很低的理解。

In C, C++, and related languages like Objective-C, there are four kinds of behaviour: There is behaviour defined by the language standard. There is implementation defined behaviour, which means the language standard explicitely says that the implementation must define the behaviour. There is unspecified behaviour, where the language standard says that several behaviours are possible. And there is undefined behaviour, where the language standard doesn't say anything about the result. Because the language standard doesn't say anything about the result, anything at all can happen with undefined behaviour.

在C、c++和Objective-C等相关语言中,有四种行为:一种是由语言标准定义的行为。有实现定义的行为,这意味着语言标准明确地说,实现必须定义行为。有一些未指明的行为,语言标准说有几种行为是可能的。还有一种行为是没有定义的,语言标准没有对结果做任何说明。因为语言标准没有对结果做任何说明,任何事情都可能发生在未定义的行为上。

Some people here assume that "undefined behaviour" means "something bad happens". That's wrong. It means "anything can happen", and that includes "something bad can happen", not "something bad must happen". In practice it means "nothing bad happens when you test your program, but as soon as it is shipped to a customer, all hell breaks loose". Since anything can happen, the compiler can actually assume that there is no undefined behaviour in your code - because either it is true, or it is false, in which case anything can happen, which means whatever happens because of the compiler's wrong assumption is still correct.

这里有些人认为“不确定的行为”意味着“坏事发生”。这是错误的。它的意思是“任何事情都有可能发生”,包括“坏事可能发生”,而不是“坏事必须发生”。在实践中,它意味着“当您测试您的程序时,没有什么糟糕的事情发生,但是一旦它被交付给客户,所有的麻烦就会发生”。由于任何事情都可能发生,编译器实际上可以假设在代码中没有未定义的行为——因为要么是真的,要么是假的,在这种情况下任何事情都可能发生,这意味着由于编译器的错误假设而发生的任何事情仍然是正确的。

Someone claimed that when p points to an array of 3 elements, and p + 4 is calculated, nothing bad will happen. Wrong. Here comes your optimising compiler. Say this is your code:

有人声称当p点到3个元素的数组时,p + 4是计算出来的,没有什么不好的事情发生。错了。接下来是优化编译器。这是你的代码:

int f (int x)
{
    int a [3], b [4];
    int* p = (x == 0 ? &a [0] : &b [0]);
    p + 4;
    return x == 0 ? 0 : 1000000 / x;
}

Evaluating p + 4 is undefined behaviour if p points to a [0], but not if it points to b [0]. The compiler is therefore allowed to assume that p points to b [0]. The compiler is therefore allowed to assume that x != 0, because x == 0 leads to undefined behaviour. The compiler is therefore allowed to remove the x == 0 check in the return statement and just return 1000000 / x. Which means your program crashes when you call f (0) instead of returning 0.

如果p指向[0],则评估p + 4是未定义的行为,但如果p指向b[0],则不是。因此,编译器可以假设p指向b[0]。因此,编译器可以假设x != 0,因为x = 0会导致未定义的行为。因此,编译器可以在return语句中删除x == 0检查,返回1000000 / x,这意味着当您调用f(0)而不是返回0时,程序崩溃。

Another assumption made was that if you increment a null pointer and then decrement it again, the result is again a null pointer. Wrong again. Apart from the possibility that incrementing a null pointer might just crash on some hardware, what about this: Since incrementing a null pointer is undefined behavour, the compiler checks whether a pointer is null and only increments the pointer if it isn't a null pointer, so p + 1 is again a null pointer. And normally it would do the same for the decrementing, but being a clever compiler it notices that p + 1 is always undefined behaviour if the result was a null pointer, therefore it can be assumed that p + 1 isn't a null pointer, therefore the null pointer check can be ommitted. Which means (p + 1) - 1 is not a null pointer if p was a null pointer.

另一个假设是,如果您增加一个空指针,然后再次递减它,那么结果就是一个空指针。又错了。除了增加一个空指针可能只是在一些硬件,这:由于增加一个空指针是未定义行为,编译器检查指针是否为空,只有增加如果不是一个空指针的指针,所以p + 1又是一个空指针。通常会做同样的递减,但作为一个聪明的编译器它注意到p + 1总是未定义的行为如果结果是一个空指针,因此,可以认为,p + 1不是一个空指针,因此空指针的检查可以查找。这意味着(p + 1) - 1不是空指针,如果p是空指针。

#3


13  

Operations on a pointer (like incrementing, adding, etc) are generally only valid if both the initial value of the pointer and the result point to elements of the same array (or to one past the last element). Otherwise the result is undefined. There are various clauses in the standard for the various operators saying this, including for incrementing and adding.

指针上的操作(如递增、添加等)通常只在指针的初始值和结果点同时指向相同数组的元素(或指向一个元素后的元素)时才有效。否则,结果是未定义的。在标准中有各种各样的子句,包括增加和增加。

(There are a couple of exceptions like adding zero to NULL or subtracting zero from NULL being valid, but that doesn't apply here).

(有一些例外,比如将零添加到NULL或从NULL中减去零是有效的,但这里不适用)。

A NULL pointer does not point at anything, so incrementing it gives undefined behaviour (the "otherwise" clause applies).

空指针不会指向任何东西,因此递增它会给出未定义的行为(使用“否则”子句)。

#4


1  

It turns out it's actually undefined. There are systems for which this is true

它实际上是没有定义的。有些系统是这样的

int *p = NULL;
if (*(int *)&p == 0xFFFF)

Therefore, ++p would trip the undefined overflow rule (turns out that sizeof(int *) == 2)). Pointers aren't guaranteed to be unsigned integers so the unsigned wrap rule doesn't apply.

因此,+p会触发未定义的溢出规则(结果是sizeof(int *) = 2)。指针不能保证是无符号整数,因此不适用无符号包装规则。

#5


0  

As said by Columbo it is UB. And from a language lawyer point of view this is the definitive answer.

正如科伦坡所说,它是UB。从语言律师的角度来看,这是最终的答案。

However all C++ compiler implementations I know will give same result :

但是,我知道的所有c++编译器实现都会得到相同的结果:

int *p = 0;
intptr_t ip = (intptr_t) p + 1;

cout <

gives 0, meaning that p has value 4 on a 32 bit implementation and 8 on a 64 bits one

给出0,意味着p在32位实现上有值4,在64位上有8。

Said differently :

不同的说:

int *p = 0;
intptr_t ip = (intptr_t) p; // well defined behaviour
ip += sizeof(int); // integer addition : well defined behaviour 
int *p2 = (int *) ip;      // formally UB
p++;               // formally UB
assert ( p2 == p) ;  // works on all major implementation

#6


0  

From ISO IEC 14882-2011 §5.2.6:

从ISO IEC 14882 - 2011§. 5.2.6:

The value of a postfix ++ expression is the value of its operand. [ Note: the value obtained is a copy of the original value —end note ] The operand shall be a modifiable lvalue. The type of the operand shall be an arithmetic type or a pointer to a complete object type.

后缀+表达式的值是其操作数的值。【注:所得值为原值-尾注的副本】操作数为可修改的lvalue。操作数的类型应该是算术类型或指向完整对象类型的指针。

Since a nullptr is a pointer to a complete object type. So I wouldn't see why this would be undefined behaviour.

因为nullptr是指向完整对象类型的指针。所以我不明白为什么这是不确定的行为。

As has been said before the same document also states in §5.2.6/1:

由于之前一直说同样的文档也在§5.2.6/1:

If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is undefined.

如果指针操作数和结果点都指向同一数组对象的元素,或数组对象的最后一个元素之后的元素,则计算不产生溢出;否则,行为就没有定义。

This expression seems a bit ambiguous. In my interpretation, the undefined part might very well be the evaluation of the object. And I think nobody would disagree with this being the case. However, pointer arithmetics seem to only require a complete object.

这个表达似乎有点模棱两可。在我的解释中,未定义的部分很可能是对对象的评价。我认为没有人会反对这种情况。然而,指针算术似乎只需要一个完整的对象。

Of course postfix [] operators and subtractions or multiplications on pointer to array objects are only well defined, if they in fact point to the same array. Mostly important because one might be tempted to think that 2 arrays defined in succession in 1 object, can be iterated over like they were a single array.

当然,后缀[]运算符和数组对象指针上的减法或乘法只有在它们指向同一个数组时才被很好地定义。最重要的是,人们可能会认为在一个对象中连续定义了两个数组,它们可以像单个数组一样进行迭代。

So my conclusion would be that the operation is well defined, but evaluation would not be.

所以我的结论是,这个操作定义得很好,但评估却不是。

#7


-1  

Back in the fun C days, if p was a pointer to something, p++ was effectively adding the size of p to the pointer value to make p point at the next something. If you set the pointer p to 0, then it stands to reason that p++ would still point it at the next thing by adding the size of p to it.

回到有趣的C时代,如果p是一个指向某个东西的指针,那么p+实际上是将p的大小添加到指针值,使p指向下一个东西。如果你将指针p设置为0,那么它说明了p++仍然会通过增加p的大小来指向下一个东西。

What's more, you could do things like add or subtract numbers from p to move it along through memory (p+4 would point at the 4th something past p.) These were good times that made sense. Depending on the compiler, you could go anywhere you wanted within your memory space. Programs ran fast, even on slow hardware because C just did what you told it to and crashed if you got too crazy/sloppy.

更重要的是,你可以做一些事情,比如从p中加减数字来移动它通过内存(p+4会指向p后的4点)这些都是有意义的好时光。根据编译器的不同,您可以在内存空间中任意移动。程序运行得很快,即使是在运行缓慢的硬件上,因为C只是按照你的要求运行,如果你太疯狂或太草率,它就会崩溃。

So the real answer is that setting a pointer to 0 is well-defined and incrementing a pointer is well-defined. Any other constraints are placed on you by compiler builders, os developers and hardware designers.

所以真正的答案是,将指针设置为0是有定义的,而增加指针是有定义的。编译器构建程序、操作系统开发人员和硬件设计人员会对您施加任何其他约束。

#8


-2  

Given that you can increment any pointer of a well-defined size (so anything that isn't a void pointer), and the value of any pointer is just an address (there's no special handling for NULL pointers once they exist), I suppose there's no reason why an incremented null pointer wouldn't (uselessly) point to the 'one after NULL'est item.

考虑到你可以增加任何指针的定义良好的大小(因此,任何不是一个空指针),和任何指针的值只是一个地址(没有特殊处理NULL指针一旦存在),我想没有理由一个空指针不会增加(无用地)指向一个空是项。

Consider this:

考虑一下:

// These functions are horrible, but they do return the 'next'
// and 'prev' items of an int array if you pass in a pointer to a cell.
int *get_next(int *p) { return p+1; }
int *get_prev(int *p) { return p-1; }

int *j = 0;

int *also_j = get_prev(get_next(j));

also_j has had maths done to it, but it's equal to j so it's a null pointer.

also_j做过数学运算,但它等于j,所以它是一个空指针。

Therefore, I would suggest that's it's well-defined, just useless.

因此,我认为它定义得很明确,毫无用处。

(And the null pointer appearing to have the value zero when printfed is irrelevant. The value of the null pointer is platform dependent. The use of a zero in the language to initialise pointer variables is a language definition.)

(当printfed不相关时,空指针显示为零。空指针的值与平台相关。在语言中使用0初始化指针变量是一种语言定义。


推荐阅读
  • 本文将深入探讨 Unreal Engine 4 (UE4) 中的距离场技术,包括其原理、实现细节以及在渲染中的应用。距离场技术在现代游戏引擎中用于提高光照和阴影的效果,尤其是在处理复杂几何形状时。文章将结合具体代码示例,帮助读者更好地理解和应用这一技术。 ... [详细]
  • 本文通过C++语言实现了一个递归算法,用于解析并计算数学表达式的值。该算法能够处理加法、减法、乘法和除法操作。 ... [详细]
  • 洛谷 P4009 汽车加油行驶问题 解析
    探讨了经典算法题目——汽车加油行驶问题,通过网络流和费用流的视角,深入解析了该问题的解决方案。本文将详细阐述如何利用最短路径算法解决这一问题,并提供详细的代码实现。 ... [详细]
  • Irish budget airline Ryanair announced plans to significantly increase its route network from Frankfurt Airport, marking a direct challenge to Lufthansa, Germany's leading carrier. ... [详细]
  • 如何从BAM文件绘制ATAC-seq插入片段长度分布图?
    在ATAC-seq数据处理中,插入片段长度的分布图是一个重要的质量控制指标,它能反映出核小体的周期性排列。本文将详细介绍如何从BAM文件中提取并绘制这些数据。 ... [详细]
  • Android与JUnit集成测试实践
    本文探讨了如何在Android项目中集成JUnit进行单元测试,并详细介绍了修改AndroidManifest.xml文件以支持测试的方法。 ... [详细]
  • 在Qt框架中,信号与槽机制是一种独特的组件间通信方式。本文探讨了这一机制相较于传统的C风格回调函数所具有的优势,并分析了其潜在的不足之处。 ... [详细]
  • 使用QT构建基础串口辅助工具
    本文详细介绍了如何利用QT框架创建一个简易的串口助手应用程序,包括项目的建立、界面设计与编程实现、运行测试以及最终的应用程序打包。 ... [详细]
  • 申请地址:https://developer.apple.com/appstore/contact/?topic=expedite 常见申请理由:1. 我们即将发布新产品,这是一个媒体活动,我们无法承担任何风险,因此在多个方面努力提升应用质量。 ... [详细]
  • WPF项目学习.一
    WPF项目搭建版权声明:本文为博主初学经验,未经博主允许不得转载。一、前言记录在学习与制作WPF过程中遇到的解决方案。使用MVVM的优点是数据和视图分离,双向绑定,低耦合,可重用行 ... [详细]
  • 本文提出了一种基于栈结构的高效四则运算表达式求值方法。该方法能够处理包含加、减、乘、除运算符以及十进制整数和小括号的算术表达式。通过定义和实现栈的基本操作,如入栈、出栈和判空等,算法能够准确地解析并计算输入的表达式,最终输出其计算结果。此方法不仅提高了计算效率,还增强了对复杂表达式的处理能力。 ... [详细]
  • ImimplementingacustomlexerinC++andwhenattemptingtoreadinwhitespace,theifstreamwont ... [详细]
  • AssumeIhaveastructurewithtwopointerseachpointingtoanobjectthathasanimplementeddestr ... [详细]
  • 本文介绍了C++中省略号类型和参数个数不确定函数参数的使用方法,并提供了一个范例。通过宏定义的方式,可以方便地处理不定参数的情况。文章中给出了具体的代码实现,并对代码进行了解释和说明。这对于需要处理不定参数的情况的程序员来说,是一个很有用的参考资料。 ... [详细]
  • HTML前端开发:UINavigationController与页面间数据传递详解
    本文详细介绍了如何在HTML前端开发中利用UINavigationController进行页面管理和数据传递,适合初学者和有一定基础的开发者学习。 ... [详细]
author-avatar
8023pxeb_256
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有