C:传递(双重)作业

我在C中使用了这样的构造:

list->head = list->tail = NULL; 

现在我考虑这是否真的意味着我想的。

这是什么意思?

  1. list->head = NULL; list->tail = NULL;

要么

  1. 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 ;

现在tailNULL因此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 ,链式分配就是不可行的。