每7.5,
[errno]扩展为具有int类型的可修改的lvalue175,其值由多个库函数设置为正错误号.未指定errno是宏还是使用外部链接声明的标识符.如果为了访问实际对象而禁止宏定义,或者程序定义名为errno的标识符,则行为未定义.
175)宏errno不必是对象的标识符.它可能会扩展为函数调用产生的可修改的左值(例如,*errno()).
我不清楚这是否足以要求&errno不是违反约束.C语言具有左值(例如寄存器存储类变量;但是这些变量只能是自动的,因此errno无法定义),&运算符是违反约束的.
如果&errno是合法的C,是否需要保持不变?
在另一个问题,主题std::numeric_limits<int>::is_modulo出现了.但我想的越多,看起来规格或GCC或两者都有问题.
让我先从一些代码开始:
#include <limits>
#include <iostream>
bool test(int x)
{
return x+1 > x;
}
int main(int argc, char *argv[])
{
int big = std::numeric_limits<int>::max();
std::cout << std::numeric_limits<int>::is_modulo << " ";
std::cout << big+1 << " ";
std::cout << test(big) << "\n";
}
Run Code Online (Sandbox Code Playgroud)
当我用g++ -O3 -std=c++11(x86_64 GCC 4.7.2)编译它时,它产生以下输出:
1 -2147483648 1
Run Code Online (Sandbox Code Playgroud)
也就是说,is_modulo是的,一个加号INT_MAX是负数,一个加号INT_MAX大于INT_MAX.
如果你是那种有实际机会回答这个问题的人,你已经知道这里发生了什么.C++规范说整数溢出是Undefined Behavior; 允许编译器假设你不这样做; 因此争论x+1不可能INT_MAX; 因此编译器可能(并将会)编译test函数以true无条件地返回.到现在为止还挺好.
但是, …
C如何表示负整数?
它是通过二进制补码表示还是使用MSB(最重要的位)?
-1十六进制是ffffffff.
所以请为我澄清一下.
我有一个unsigned int实际存储有符号值的C++ .我想将此变量转换为a signed int,以便无符号和有符号值具有相同的二进制值.
unsigned int lUnsigned = 0x80000001;
int lSigned1 = (int)lUnsigned; // Does lSigned == 0x80000001?
int lSigned2 = static_cast<int>(lUnsigned); // Does lSigned == 0x80000001?
int lSigned3 = reinterpret_cast<int>(lUnsigned); // Compiler didn't like this
Run Code Online (Sandbox Code Playgroud)
什么时候强制转换在C++中改变变量的位?例如,我知道从a int到a的转换float会改变位,因为它int是二进制补码并且float是浮点数.但其他场景呢?我不清楚C++中的规则.
在C99规范的6.3.1.3节中,它表示从无符号到有符号整数的转换是编译器定义的!
从标准(4.7)看起来,当它们使用相同数量的位时,从int转换为unsigned int,纯粹是概念性的:
如果目标类型是无符号的,则结果值是与源整数一致的最小无符号整数(模2 n,其中n是用于表示无符号类型的位数).[注意:在二进制补码表示中,此转换是概念性的,并且位模式没有变化(如果没有截断). - 结束说明]
因此,在此方向上,转换会保留位掩码.我不确定标准是否保证从unsigned int到int的转换相同(同样,假设使用相同的位数).这里的标准说:
如果目标类型是有符号的,如果它可以在目标类型(和位域宽度)中表示,则该值不变; 否则,该值是实现定义的.
这究竟是什么意思"目的地类型"?例如,2 ^ 32-1不能用32位int表示.这是否意味着它无法在目标类型中表示,因此不能假设位模式保持不变?
这是一个简单的函数,试图从big-endian缓冲区读取一个通用的二进制补码整数,我们假设std::is_signed_v<INT_T>:
template<typename INT_T>
INT_T read_big_endian(uint8_t const *data) {
INT_T result = 0;
for (size_t i = 0; i < sizeof(INT_T); i++) {
result <<= 8;
result |= *data;
data++;
}
return result;
}
Run Code Online (Sandbox Code Playgroud)
不幸的是,这是未定义的行为,因为最后一个<<=转移到符号位.
所以现在我们尝试以下方法:
template<typename INT_T>
INT_T read_big_endian(uint8_t const *data) {
std::make_unsigned_t<INT_T> result = 0;
for (size_t i = 0; i < sizeof(INT_T); i++) {
result <<= 8;
result |= *data;
data++;
}
return static_cast<INT_T>(result);
}
Run Code Online (Sandbox Code Playgroud)
但我们现在正在调用实现定义的行为static_cast,从unsigned转换为signed.
如何在"明确定义"的领域中做到这一点?
混合有符号和无符号算术的典型示例似乎是:
\n unsigned int u = 10;\n int a = -42;\n auto tmp1 = u - a;\n std::cout << tmp1 << std::endl;\n int tmp2 = tmp1;\n std::cout << tmp2 << std::endl;\nRun Code Online (Sandbox Code Playgroud)\n(请注意,我使用的是auto tmp1这样我可以显示中间结果并检查类型。当然,在实际代码中,这都是一行)
如果你启用足够多的警告(-Wconversion在 clang-13 上,另外,-Wsign-conversion在 gcc-11 上),编译器会很好地抱怨:
main.cpp:148:21: warning: conversion to \xe2\x80\x98unsigned int\xe2\x80\x99 from \xe2\x80\x98int\xe2\x80\x99 may change the sign of the result [-Wsign-conversion]\n 148 | auto tmp1 = u - a;\n | ^\nmain.cpp:150:16: warning: conversion to \xe2\x80\x98int\xe2\x80\x99 from \xe2\x80\x98unsigned int\xe2\x80\x99 …Run Code Online (Sandbox Code Playgroud) 我想将char类型转换为int类型而不会丢失签名的含义,所以我在文件int_test.c中编写代码并且它有效:
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#define c2int(x) \
({ \
int t; \
if (x > 0x80) \
t = x | (1 << sizeof(int) * 8) - (1 << sizeof(char) * 8); \
else \
t = x; \
t; \
})
int main()
{
uint8_t a = 0xFE;
int b;
b = c2int(a);
printf("(signed char)a = %hhi, b = %d\n", a, b);
exit(EXIT_SUCCESS);
}
Run Code Online (Sandbox Code Playgroud)
运行结果是:
(signed char)a = -2,b = -2
编译日志是:
gcc -o int_test int_test.c …