作为编程中的一般概念,赋值是一种将新值存储到可以存储值的位置的构造,例如,在变量中。这些位置被称为左值,因为它们是保存值的位置。
在C中,赋值是一个表达式,因为它有一个值;我们称之为赋值表达式。一个简单的赋值看起来像
lvalue = value-to-store
它将表达式value-to-store的值分配给位置 lvalue。
然而,这不是使用左值的唯一方法,也不是所有左值都可以被赋值。要在赋值的左侧使用左值,它必须是可修改的。在C中,这意味着它不是用类型限定符const声明的。
赋值表达式的值是新值存储在其中后的左值。这意味着您可以在其他表达式中使用赋值。赋值运算符是右关联的,因此
x = y = z = 0;
相当于,
x = (y = (z = 0));
另一方面,
((x = y) = z) = 0;
将无效,因为赋值表达式(如x=y)作为左值无效。
警告:如果将赋值嵌套在另一个表达式中,请在其周围写括号,除非该表达式是条件表达式、逗号分隔序列或另一个赋值。
简单赋值表达式计算右侧操作数的值,并将其存储到左侧的lvalue中。下面是一个简单的赋值表达式,它将5存储在i中:
i = 5
我们说这是给变量i赋值,它给i赋值5。它没有分号,因为它是一个表达式(所以它有一个值)。在结尾添加分号将使其成为语句。
下面是简单赋值表达式的另一个示例。它的操作数并不简单,但赋值是简单赋值。
x[foo ()] = y + 6
如果可能,使用两种不同数据类型的简单赋值将右侧数值转换为lvalue的类型。它可以将任何数字类型转换为任何其他数字类型。
一些非数字类型也允许简单赋值:指针、结构和联合。
警告:数组不允许赋值,因为C中没有数组值;C变量可以是数组,但数组不能作为整体进行操作。
标识保存值的内存空间的表达式称为左值,因为它是可以保存值的位置。
左值的标准类型为:
如果表达式的最外层操作是任何其他运算符,则该表达式不是左值。因此,变量x是左值,但x+0不是左值,即使这两个表达式计算相同的值(假设x是数字)。
数组可以是左值(上面的规则确定它是否为左值),但在表达式中使用数组会自动将其转换为指向第一个元素的指针。转换的结果不是左值。因此,如果变量a是一个数组,则不能使用a本身作为赋值的左操作数。但你可以给a的元素赋值,例如a[0]。这是左值,因为a是左值。
您可以缩写通用构造
lvalue = lvalue + expression
as
lvalue += expression
这称为modifying assignment。例如,
i = i + 5;
i += 5;
两个语句是等价的。第一个使用简单赋值;第二个使用modifying assignment。
例如,你可以像这样从左值中减去一些东西,
lvalue -= expression
或者将其乘以一定量,
lvalue *= expression
或者像这样移位一定量。
lvalue <<= expression
lvalue >>= expression
在大多数情况下,此特性不会让语言更强大,但它提供了极大的方便。此外,当左值包含具有副作用的代码时,简单赋值执行这些副作用两次,而修改赋值执行一次。例如,
x[foo ()] = x[foo ()] + 5;
函数foo调用了两次,每次都可能返回不同的值。如果foo()第一次返回1,第二次返回3,则效果可能是将x[3]和5相加并将结果存储在x[1]中,或者将x[1]和5相加并存储在x[3]中。我们不知道两个中哪一个会发生,因为C没有指定哪个foo调用首先被计算。
这样的语句是不明确的,不应该使用。
相比之下
x[foo ()] += 5;
是明确的:它只调用foo一次,以确定要调整x的哪个元素,并通过向其加5来调整该元素。
运算符“++”和“--”是递增和递减运算符。当用于数值时,它们加或减1。我们不认为它们是赋值,但它们等同于赋值。
在左值之前使用“++”或“--”作为前缀称为预增量或预减量。这将加或减1,其结果成为表达式的值。例如,
#include /* Declares printf. */
int
main (void)
{
int i = 5;
printf ("%d
", i);
printf ("%d
", ++i);
printf ("%d
", i);
return 0;
}
打印包含5、6和6的行。表达式++i将i从5增加到6,并具有值6,因此该行上printf的输出是“6”。
在左值后面使用“++”或“--”会做一些特殊的事情:它直接从左值中获取值,然后递增或递减。因此,i++的值与i的值相同,但i++“稍后”会递增i。这称为后增或后减。
例如
#include
int
main (void)
{
int i = 5;
printf ("%d
", i);
printf ("%d
", i++);
printf ("%d
", i);
return 0;
}
打印包含5、5和6的行。表达式i++的值为5,这是当时i的值,但稍后它将i从5增加到6。
“晚一点”是多久?这是灵活的。增量必须在下一个序列点发生。在简单的情况下,这意味着语句的结尾。
如果一元运算符位于后增或后减表达式之前,则增量嵌套在内层:
-a++ is equivalent to -(a++)
因为-a不是左值,因此不能递增。
在C语言中,表达式各部分的计算顺序是不固定的。除少数特殊情况外,操作可以按任何顺序计算。如果表达式的一部分具有对x的赋值,而表达式的另一部分使用x,则结果是不可预测的,因为该使用可能在赋值之前或之后计算。
下面是一个模棱两可的代码示例:
x = 20;
printf ("%d %d
", x, x = 4);
如果第二个参数x是在第三个参数x=4之前计算的,则第二个变量的值将为20。如果按其他顺序计算,则第2个参数的值将是4。
这里有一种方法可以使代码明确:
y = 20;
printf ("%d %d
", y, x = 4);
这是另一种方式,具有另一种含义:
x = 4;
printf ("%d %d
", x, x);
此问题适用于所有类型的赋值,以及与赋值等效的递增和递减运算符。
但是,在if条件或while测试中把赋值跟逻辑运算符一起使用可能很有用。
在if条件中编写赋值通常很方便,但这会降低程序的可读性。下面是一个避免的示例:
if (x = advance (x))
…
这里的想法是推进x并测试值是否为非零。但是,读者可能会忽略它使用“=”而不是“=”的事实。事实上,在条件中写入“=”是一个常见的错误,因此当“=”以某种方式显示为错误时,GNU C可以发出警告。
将赋值写为单独的语句更清楚,如下所示:
x = advance (x);
if (x != 0)
…
这清楚地表明,x被赋予了一个新值。
另一种方法是使用逗号运算符,如下所示:
if (x = advance (x), x != 0)
…
但是,将赋值放在单独的语句中通常更清楚,除非赋值很短,因为它减少了嵌套。
留言与评论(共有 0 条评论) “” |