我早先使用过工会; 今天,当我读到这篇文章并开始知道这段代码时,我感到震惊
union ARGB
{
uint32_t colour;
struct componentsTag
{
uint8_t b;
uint8_t g;
uint8_t r;
uint8_t a;
} components;
} pixel;
pixel.colour = 0xff040201; // ARGB::colour is the active member from now on
// somewhere down the line, without any edit to pixel
if(pixel.components.a) // accessing the non-active member ARGB::components
Run Code Online (Sandbox Code Playgroud)
实际上是未定义的行为即从工会成员读取而不是最近编写的那个导致未定义的行为.如果这不是工会的预期用途,那是什么?有人可以详细解释一下吗?
更新:
事后我想澄清一些事情.
如果标准布局联合包含多个共享公共初始序列的标准布局结构,并且如果此标准布局联合类型的对象包含其中一个标准布局结构,则允许检查任何标准布局结构的公共初始序列.标准布局结构成员.§9.2/ 19:如果相应的成员具有布局兼容类型且两个成员都不是位字段,或者两者都是具有相同宽度的位字段,则一个或多个初始序列的两个标准布局结构共享一个公共初始序列成员.
C89/90在未指明的行为(附件J)中称之为,而K&R的书称其实施已定义.来自K&R的报价:
这是联合的目的 - 一个可以合法地保存几种类型中的任何一种的变量.[...]只要用法一致:检索到的类型必须是最近存储的类型.程序员有责任跟踪当前存储在联合中的类型; 如果将某些内容存储为一种类型并将其提取为另一种类型,则结果将依赖于实现. …
可以unsigned long int在32位计算机上保存十位数字(1,000,000,000 - 9,999,999,999)吗?
此外,有什么的范围unsigned long int,long int,unsigned int,short int,short unsigned int,和int?
有一个类似的问题在这里,但在这一问题的用户似乎有一个更大的阵列,或载体。如果我有:
bool boolArray[4];
Run Code Online (Sandbox Code Playgroud)
我想检查所有元素是否为假,我可以分别检查[0],[1],[2]和[3],也可以循环遍历。由于(据我所知)false应该具有值0,而除0以外的其他任何东西都为true,所以我想到了简单地做:
if ( *(int*) boolArray) { }
Run Code Online (Sandbox Code Playgroud)
这行得通,但是我意识到它依赖于布尔值是一个字节,整数是四个字节。如果我强制转换为(std :: uint32_t)可以,还是一个坏主意?我刚好在一个数组中有3或4个布尔值,并且想知道这是否安全,如果不是,那么是否有更好的方法可以做到。
另外,如果我最终得到超过4个布尔值,但少于8个布尔值,那么我可以使用std :: uint64_t或unsigned long long之类的东西做同样的事情吗?
我正在学习C,并且对unsigned int和signed int的值范围中的“ -1”有一个愚蠢的问题。我似乎在任何地方都找不到它的解释。
以下段落说明了数据范围。但是,它没有解释“ -1”。“ -1”代表什么意思?是-1是因为它跳过0并且0没有值?
在32位整数中,无符号整数的范围为0到2 ^ 32 -1 = 0到4,294,967,295或大约40亿。签名版本从-2 ^ 31 -1到2 ^ 31,即–2,147,483,648到2,147,483,647,即大约-20亿到+20亿。范围是相同的,但是它在数字行上移动。
自述
甲"陷阱值",或"陷阱表示"类型T,是一个比特组合(底层存储的),其产生的无效的值T.试图解释为无效值的表示将导致未定义的行为.
另一个问题已经开始了一个激烈的讨论char,以及实现具有陷阱表示的可能性.
问题:
char可能有陷阱值?在前面的论证中,这些部分是引用最多的部分,它们是否相矛盾?
3.9.1p1基本类型[basic.fundamental]它是实现定义的,是否
char可以保持负值.可以显式声明字符signed或unsigned.甲
char,一个signed char,和unsigned char占据存储相同量的并具有相同的对准要求(3.11); 也就是说,它们具有相同的对象表示.对于字符类型,对象表示的所有位都参与值表示.对于无符号字符类型,值表示的所有可能位模式表示数字.这些要求不适用于其他类型.
在任何特定实现中,普通
char对象可以采用与asigned char或unsigned char;实现定义的值相同的值.
3.9p2 类型
[basic.types]对于平凡可复制类型的任何对象(基类子对象除外),
T,无论对象是否包含有效的类型值,构成对象T的基础字节(1.7)都可以复制到数组char或unsigned char.如果阵列的内容
char或unsigned char将被复制回对象,该对象随后应保持其原始值.
我试图从浮点数中提取位而不调用未定义的行为.这是我的第一次尝试:
unsigned foo(float x)
{
unsigned* u = (unsigned*)&x;
return *u;
}
Run Code Online (Sandbox Code Playgroud)
据我了解,由于严格的别名规则,这不能保证工作,对吧?如果使用字符指针进行中间步骤,它是否有效?
unsigned bar(float x)
{
char* c = (char*)&x;
unsigned* u = (unsigned*)c;
return *u;
}
Run Code Online (Sandbox Code Playgroud)
或者我是否必须自己提取单个字节?
unsigned baz(float x)
{
unsigned char* c = (unsigned char*)&x;
return c[0] | c[1] << 8 | c[2] << 16 | c[3] << 24;
}
Run Code Online (Sandbox Code Playgroud)
当然,这有一个缺点,取决于字节顺序,但我可以忍受.
工会黑客肯定是未定义的行为,对吧?
unsigned uni(float x)
{
union { float f; unsigned u; };
f = x;
return u;
}
Run Code Online (Sandbox Code Playgroud)
为了完整起见,这里有一个参考版本foo.也是未定义的行为,对吗?
unsigned ref(float x)
{ …Run Code Online (Sandbox Code Playgroud) 这可以被认为是这个问题的扩展(我只对C感兴趣,但添加C++来完成扩展)
6.3.2.3.3中的C11标准说:
值为0的整型常量表达式或此类表达式转换为类型
void *称为空指针常量.
我个人对此的看法是0并且(void *)0表示空指针,其整数值实际上可能不是0,但是不包括0强制转换为任何其他类型.
但是,标准然后继续:
如果将空指针常量转换为指针类型,则生成的指针称为空指针,...
它包含(int *)0为空指针,因为强制转换是一种显式转换(C11,6.3),它在转换方法下列出.
然而,令我惊讶的是以下短语
...或者这样的表达式转换为
void *...
有了上面的语义,这句话似乎完全没用.问题是,这句话完全没用吗?如果没有,它有什么影响?因此,是否(int *)0为空指针?
另一个可以帮助讨论的问题如下.被(long long)123视为"123转换为long long",或"123与类型long long".换句话说,有没有转换(long long)123?如果没有,则上面的第二个引用不包括(int *)0为空指针.
我在c99标准中找到了这个
3.17.2
1 indeterminate value
either an unspecified value or a trap representation
Run Code Online (Sandbox Code Playgroud)
以上陈述对我来说并不清楚.任何人都可以解释这是什么,它的优点和缺点是什么?
一些例子将受到高度赞赏.
在C99中,术语算术运算出现了16次,但我没有看到它的定义.
术语算术运算符仅在文本中出现两次(同样没有定义)但它确实出现在索引中:
算术运算符
添加剂,6.5.6,
G.5.2按位,6.5.10,6.5.11,6.5.12
递增和递减
,6.5.2.4,6.5.3.1乘法6.5.5,G.5.1
移位,6.5.7
一元,6.5. 3.3
然后我们将+ - | &(二进制)++ -- *(二进制)/ % << >> ~作为算术运算符,如果索引被认为是规范的!
也许我们应该将算术运算识别为算术运算符的使用.但F9.4.5表示该sqrt()功能也是算术运算,详情请参考IEC 60559(又名IEEE754).因此,必须有算术运算,而不仅仅是算术运算符的使用.
我一直以为C不接受NULL参数,直到我开始学习指针.在某些编程语言中,如python for one,可以将NULL参数作为参数传递,但在CI中始终认为这会导致未定义的行为.
我的问题只是一个好奇心,一个功能怎么可能,比如...
waitpid(child_pid, &status, options); //pointer &status
Run Code Online (Sandbox Code Playgroud)
...接受一个NULL指针作为参数而不会运行到未定义的行为,不是NULL指针只是指向什么?
简单地说,为什么这在C中可以接受?