我们在用户输入期间使用 & 符号,但为什么我们在 C 中的赋值期间不需要它?
We use ampersand during user input but Why we don't need this during assignment in C?
我们在用户输入时使用 & 运算符。这意味着我们正在将特定值分配给特定变量的内存地址。但是在赋值期间,我们不需要&符号。在没有任何内存地址规范的情况下如何传递值?示例代码如下。
#include<stdio.h>
int main()
{
int a, b, total;
scanf("%d%d", &a, &b); //& for memory address
total = a+b; //how the value is being tranfarred to total without any memory address?
printf("%d", total);
return 0;
}
赋值是一种 C 语言结构。
C 知道赋值不是对其左侧操作数的值起作用,而是对其地址*起作用。你不需要明确地说 &
。
对于用户定义的函数,尤其是无原型的旧式函数(未指定参数类型)和可变参数函数,如 scanf
,C 无法知道他们想要什么——他们可能想要地址,或者他们可能同样需要该值,因此您必须通过在前面加上或不在前面加上和号来指定它。
现在有一个原型函数,例如:
void take_address(int *x);
理论上你可以扩展语言,这样
void use_take_address(void){ int x=42; take_address(x); }
会隐含地获取参数的地址(毕竟 C++ 对引用做了类似的事情),但它会,可以说,伤害
可读性。
有了&x
你可以肯定这个函数理论上至少可以修改x
;没有 &
你可以或多或少地确定它不能(除非 x
是一个数组)。
这是一种语言设计选择。
*这实际上不是 100% 准确。赋值也可能与没有地址的基于寄存器的变量一起工作,并且在 C 中对变量使用 &
实际上会使它不能被显式标记为 register
,但这些是细节一个好的优化器可以通过。
变量是一个抽象单元,具有与之关联的地址。
使用 int var
等语法声明的变量表示位于特定地址 &var
.
上的值
scanf
期望指向变量的指针,即地址,这就是您使用&符号的原因。这是必需的,因为当您将变量传递给函数时(没有&符号),您只传递它们的值,而不是实际变量的地址信息。
有些人喜欢说,当你将一个变量传递给一个函数时,该函数取变量的值的"a copy"。
如果你想 decalre 指针(代表其特定类型地址的变量,而不是值)你这样做:
int* var;
该变量将用于尝试访问类型为 int
的另一个变量,方法是将其地址分配给它。 int* var = &some_int
以下面的场景为例。你想创建一个函数,将一些值存储在传递给它的变量中。为此,您必须像这样定义函数:
void foo (int* var)
{
*var = 1; // * is the de-reference operator which shows the value of the address `var` and we assign the value to 1.
}
scanf
函数必须访问正在读取值的 对象 。所有 C 函数参数都是按值传递的,所以如果你写
int a, b;
scanf("%d%d", a, b); // THIS IS WRONG
scanf
函数只能看到 a
和 b
函数被调用时的值。它将无法修改 a
.
赋值需要一个左值作为它的左操作数。左值(稍微简化)是指定对象的表达式;变量名是最常见的一种左值。由于赋值运算符 =
内置于该语言中,因此它可以有适用于它的特殊规则。特别是,=
的 LHS 上的左值标识了被分配给的对象;它不会产生该对象的(先前)值。这就是语言定义赋值的方式。
例如,如果你写:
a = b;
a
和 b
都是左值,但由于它们的上下文,它们被区别对待。 a
指的是名称为a
的对象。 b
也引用一个对象,但在该上下文中它产生该对象的当前值。
如果赋值是作为函数调用实现的,它必须获取被赋值对象的地址:
int assign(int *lhs, int rhs);
assign(&a, b);
语言可能 需要赋值的 LHS 地址,但只允许目标对象的名称更方便。
(为了解释上面的简化,当前标准对 lvalue 的定义是 "an expression (with an object type other than void) that potentially designates an object"。词 "potentially" 在那里是因为,例如,*ptr
其中 ptr
是一个指针,即使 ptr
的当前值为 NULL
也是左值。作为左值是一个编译时概念。试图将一个值赋给*ptr
是合法的,但有未定义的行为。)
我们在用户输入时使用 & 运算符。这意味着我们正在将特定值分配给特定变量的内存地址。但是在赋值期间,我们不需要&符号。在没有任何内存地址规范的情况下如何传递值?示例代码如下。
#include<stdio.h>
int main()
{
int a, b, total;
scanf("%d%d", &a, &b); //& for memory address
total = a+b; //how the value is being tranfarred to total without any memory address?
printf("%d", total);
return 0;
}
赋值是一种 C 语言结构。
C 知道赋值不是对其左侧操作数的值起作用,而是对其地址*起作用。你不需要明确地说 &
。
对于用户定义的函数,尤其是无原型的旧式函数(未指定参数类型)和可变参数函数,如 scanf
,C 无法知道他们想要什么——他们可能想要地址,或者他们可能同样需要该值,因此您必须通过在前面加上或不在前面加上和号来指定它。
现在有一个原型函数,例如:
void take_address(int *x);
理论上你可以扩展语言,这样
void use_take_address(void){ int x=42; take_address(x); }
会隐含地获取参数的地址(毕竟 C++ 对引用做了类似的事情),但它会,可以说,伤害 可读性。
有了&x
你可以肯定这个函数理论上至少可以修改x
;没有 &
你可以或多或少地确定它不能(除非 x
是一个数组)。
这是一种语言设计选择。
*这实际上不是 100% 准确。赋值也可能与没有地址的基于寄存器的变量一起工作,并且在 C 中对变量使用 &
实际上会使它不能被显式标记为 register
,但这些是细节一个好的优化器可以通过。
变量是一个抽象单元,具有与之关联的地址。
使用 int var
等语法声明的变量表示位于特定地址 &var
.
scanf
期望指向变量的指针,即地址,这就是您使用&符号的原因。这是必需的,因为当您将变量传递给函数时(没有&符号),您只传递它们的值,而不是实际变量的地址信息。
有些人喜欢说,当你将一个变量传递给一个函数时,该函数取变量的值的"a copy"。
如果你想 decalre 指针(代表其特定类型地址的变量,而不是值)你这样做:
int* var;
该变量将用于尝试访问类型为 int
的另一个变量,方法是将其地址分配给它。 int* var = &some_int
以下面的场景为例。你想创建一个函数,将一些值存储在传递给它的变量中。为此,您必须像这样定义函数:
void foo (int* var)
{
*var = 1; // * is the de-reference operator which shows the value of the address `var` and we assign the value to 1.
}
scanf
函数必须访问正在读取值的 对象 。所有 C 函数参数都是按值传递的,所以如果你写
int a, b;
scanf("%d%d", a, b); // THIS IS WRONG
scanf
函数只能看到 a
和 b
函数被调用时的值。它将无法修改 a
.
赋值需要一个左值作为它的左操作数。左值(稍微简化)是指定对象的表达式;变量名是最常见的一种左值。由于赋值运算符 =
内置于该语言中,因此它可以有适用于它的特殊规则。特别是,=
的 LHS 上的左值标识了被分配给的对象;它不会产生该对象的(先前)值。这就是语言定义赋值的方式。
例如,如果你写:
a = b;
a
和 b
都是左值,但由于它们的上下文,它们被区别对待。 a
指的是名称为a
的对象。 b
也引用一个对象,但在该上下文中它产生该对象的当前值。
如果赋值是作为函数调用实现的,它必须获取被赋值对象的地址:
int assign(int *lhs, int rhs);
assign(&a, b);
语言可能 需要赋值的 LHS 地址,但只允许目标对象的名称更方便。
(为了解释上面的简化,当前标准对 lvalue 的定义是 "an expression (with an object type other than void) that potentially designates an object"。词 "potentially" 在那里是因为,例如,*ptr
其中 ptr
是一个指针,即使 ptr
的当前值为 NULL
也是左值。作为左值是一个编译时概念。试图将一个值赋给*ptr
是合法的,但有未定义的行为。)