是&errno合法C?

R..*_*R.. 50 c errno language-lawyer

每7.5,

[errno]扩展为具有int类型的可修改的lvalue175,其值由多个库函数设置为正错误号.未指定errno是宏还是使用外部链接声明的标识符.如果为了访问实际对象而禁止宏定义,或者程序定义名为errno的标识符,则行为未定义.

175)宏errno不必是对象的标识符.它可能会扩展为函数调用产生的可修改的左值(例如,*errno()).

我不清楚这是否足以要求&errno不是违反约束.C语言具有左值(例如寄存器存储类变量;但是这些变量只能是自动的,因此errno无法定义),&运算符是违反约束的.

如果&errno是合法的C,是否需要保持不变?

nel*_*age 17

所以§6.5.3.2p1指定

一元&运算符的操作数应该是函数指示符,[]或一元*运算符的结果,或者是一个左值,它指定一个不是位字段的对象,并且不用寄存器存储类说明符声明.

认为可以理解&lvalue为对于不属于这两个类别的任何左值都可以.正如您所提到的,errno不能使用寄存器存储类说明符声明,并且我认为(虽然现在不追逐引用检查)您不能拥有类型为plain的位域int.

所以我认为规范要求&(errno)合法C.

如果&errno是合法的C,它是否需要保持不变?

据我所知,允许errno成为一个宏(以及它在例如glibc中的原因)的一部分是允许它作为对线程局部存储的引用,在这种情况下它肯定不会在线程间保持不变.我没有理由认为它必须是不变的.只要errno保留指定语义的值,我就没有理由认为反常的C库&errno在程序过程中无法更改以引用不同的内存地址 - 例如,每次设置时释放并重新分配后备存储errno.

您可以想象维护一个由库设置的最后N个errno值的环形缓冲区,并&errno始终指向最新的.我不认为它会特别有用,但我看不出它违反规范的任何方式.


Nem*_*emo 14

我很惊讶没有人引用C11规范.对长报价表示歉意,但我认为这是相关的.

7.5错误

标题定义了几个宏...

...和

errno

它扩展为可修改的左值(201),其具有类型int和线程本地存储持续时间,其值由若干库函数设置为正误差数.如果为了访问实际对象而禁止宏定义,或者程序使用名称定义标识符errno,则行为是未定义的.

的值errno在初始线程是在程序启动时零(初始值errno中的其他线程是一个不确定的值),但不被任何库函数设置为零.(202)错误号的值可以通过被设置为非零库函数调用是否存在错误,前提errno是本国际标准中的函数描述中未记录使用 .

(201)宏errno不必是对象的标识符.它可能会扩展为函数调用产生的可修改的左值(例如*errno()).

(202)因此,errno用于错误检查的程序应在库函数调用之前将其设置为零,然后在后续库函数调用之前检查它.当然,库函数可以保存输入值,errno然后将其设置为零,只要原始值恢复,如果errno在返回之前值仍然为零.

"线程本地"意味着register出局.类型int意味着位域输出(IMO).所以&errno看起来对我合法.

持续使用像"它"和"字价值"建议标准没有考虑的作者&errno是不恒定的.我想可以想象一个实现&errno在一个特定的线程中不是常量,但是要按照脚注所说的方式使用(设置为零,然后在调用库函数后检查),它必须是故意对抗的,并且可能需要专门的编译器支持只是为了对抗.

简而言之,如果规范确实允许非常数&errno,我认为这不是故意的.

[更新]

R.在评论中提出了一个很好的问题.在考虑之后,我相信我现在知道他的问题和原始问题的正确答案.亲爱的读者,让我看看能否说服你.

R.指出GCC在顶层允许这样的事情:

register int errno asm ("r37");  // line R
Run Code Online (Sandbox Code Playgroud)

这将被宣布errno为登记的全球价值r37.显然,它将是一个线程局部可修改的左值.那么,一致的C实现是否可以这样声明errno

答案是否定的.当您或我使用"声明"一词时,我们通常会考虑口语和直观的概念.但是标准并不是口语或直觉; 它准确地说,它的目的只是使用明确定义的术语.在"声明"的情况下,标准本身定义了术语; 当它使用该术语时,它使用自己的定义.

通过阅读规范,您可以准确地了解"声明"是什么,并确切地知道它不是什么.换句话说,标准描述了语言"C".它没有描述"某些不是C的语言".就标准而言,"C with extensions"只是"某种语言不是C".

因此,从标准的角度来看,R行根本不是声明.它甚至没有解析!不妨阅读:

long long long __Foo_e!r!r!n!o()blurfl??/**
Run Code Online (Sandbox Code Playgroud)

就规范而言,这与R线一样是"声明"; 即,根本没有.

因此,当C11规范说,在6.5.3.2节中:

一元运算&符的操作数应该是函数指示符,[]一元或一元运算*符的结果,或者是一个左值,它指定一个不是位字段的对象,并且不用寄存器存储类指定器声明.

......它意味着一些非常精确的东西,并不是指像Line R这样的东西.

现在考虑的申报int 对象,其errno指.(注意:我并不是指errno 名称的声明,因为当然,如果errno是宏,可能没有这样的声明.我的意思是声明基础int对象.)

上面的语言说你可以获取左值的地址,除非它指定一个位字段或它指定一个"声明"的对象register.底层errno对象的规范说它是一个可修改的int左值,具有线程局部持续时间.

现在,规范确实没有说errno必须声明底层对象.也许它只是通过一些实现定义的编译器魔术出现.但同样,当规范说"使用寄存器存储类说明符声明"时,它使用自己的术语.

因此,基础errno对象在标准意义上是"声明"的,在这种情况下,它不能同时是register线程本地的; 或者没有宣布在所有的,在这种情况下,它没有声明register.无论哪种方式,因为它是左值,你可以拿它的地址.

(除非它是一个位字段,但我认为我们同意位字段不是类型的对象int.)