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
.)