C:传递(双重)作业
我在C中使用了这样的构造:
list->head = list->tail = NULL;
现在我考虑这是否真的意味着我想的。
这是什么意思?
-
list->head = NULL; list->tail = NULL;
要么
-
list->head = list->tail; list->tail = NULL;
thx澄清
这些都不正确。
由于simple assignment =
运算符是从右到左的关联,因此您的表达式与以下内容相同:
list->head = (list->tail = NULL);
将NULL分配给tail,然后将tail(具有空指针的值)分配给head。
赋值运算符是从右到左的关联。
因此这个表达式声明
list->head = list->tail = NULL;
相当于
list->tail = NULL; list->head = list->tail;
他们都不是。 它的意思是
list->tail = NULL; list->head = list->tail;
1是不正确的,因为如果list->tail
的类型不是指针,它可能会产生不同的结果,因为该值将转换为赋值的目标类型。
2中的两个语句的顺序不正确。
这是多项任务,因为你的第一个选择是正确的
list->head = list->tail = NULL;
这是流动的魔力。
最初, tail
从右到左设置为NULL
list->tail = NULL;
然后list->head = list->tail ;
现在tail
为NULL
因此head
也会分配一个NULL
值
赋值运算符的相关性=
从右到左。 这意味着如果一个语句中有多个=
运算符,那么首先评估最右边的,然后评估最右边的运算符,并按顺序,直到最后评估最左边的=
运算符。
这意味着当你这样做
list->head = list->tail = NULL;
最右边的赋值,即list->tail = NULL
首先被评估。 所以list->tail
将为NULL
。
之后将对list->head = list->tail
进行评估。 由于list->tail
现在是NULL
(因为之前的评估 – 即list->tail = NULL
),现在list->head
也是NULL
。
根据您在问题中的表现方式,就像
list->tail = NULL; list->head = list->tail;
有了所有不完整的答案,我必须澄清一下。
首先,表达式从右到左进行评估:
list->head = (list->tail = NULL);
该标准将赋值运算符的行为定义为:
赋值运算符将值存储在左操作数指定的对象中。 赋值表达式具有赋值后的左操作数的值,111)但不是左值。 赋值表达式的类型是左值操作数在左值转换后将具有的类型。 在左右操作数的值计算之后,对更新左操作数的存储值的副作用进行排序。 对操作数的评估是不确定的。
现在问题是,标准有两种方法可以获得作业的价值。**
// variant 1 (read the left-hand side after writing it) list->tail = NULL; list->head = list->tail;
也就是说, list->tail
在获得其值后被读取,并且该值被分配给list->head
。
// variant 2 (use temporary storage) typeof(list->tail) temp = NULL; // get the value/type of the RHS of the inner assignment list->tail = temp; list->head = temp;
(RHS:右侧,运营商右侧的术语)。 谢谢,对于@chux,这两个作业也可以互换。
见脚注111 :
允许实现读取对象以确定值,但不需要,即使对象具有volatile限定类型 。
这些变体在抽象机器方面表现相同 – 除非list->tail
是限定的volatile
(更一般:除了最左边的对象之外)。 简而言之, volatile
告诉编译器对对象的访问有副作用。 它通常用于外围硬件寄存器,例如USART。 虽然在桌面应用程序中很少使用(如果经常是错误的),但它通常用于OS内核驱动程序和裸机嵌入式系统。
对这些寄存器的写入和读取通常将写入(外部)硬件的数据传递给它。 读取产生这种硬件寄存器的值。 更糟糕的是,这些寄存器可以是只写的也可以是只读的。 对于只写,读取会产生与写入内容无关的不确定值。 这里相关的是必须非常小心地控制和排序对这些对象的访问。 没有意外的读或写。
所以,请考虑上面的代码。 然后变体将生成对硬件的不同访问:
- 变体1将导致写入,然后是读取。 如果寄存器是只写的,则读取产生不相关的数据。 这可能不是我们想要的。
- 变体2只会写入,但会重用为第二个赋值所写的值。
- 变体2具有执行写入顺序的不确定序列,这是
volatile
左值的另一个问题。
注意使用哪种变体不是由用户决定的,而是由编译器决定的。 它甚至可能因代码中的不同表达式而异。
因此,一旦涉及到volatile
,链式分配就是不可行的。