提问人:langtutheky 提问时间:3/25/2015 最后编辑:langtutheky 更新时间:3/26/2015 访问量:1987
在C语言中更改变量的值时,是创建了新的原语,还是当前的原语发生了变异?
When changing the value of a variable in C, is a new primitive created or is the current primitive mutated?
问:
我知道“可变”和“不可变”是应该用来描述对象在面向对象的语言(如 Java 和目标 C)中更改值的能力的术语。但是,我想提出它,因为它与我关于基元数据的问题有关。我知道,当我更改包含不可变对象的变量的值时,我实际上是在创建一个新对象。但是,我想知道 C 中的基元数据是否与不可变对象的行为类似。我的意思是,当我更改保存原始数据的变量的值时,该变量会创建并引用新数据。还是现有的原语实际上变异/修改了存储的数据值?
编辑#1:
问题#1:我想澄清一些误解(无论是我自己还是其他人),因为我在说“当我更改包含不可变对象的变量的值时,我实际上是在创建一个新对象”时没有说清楚。当我这么说时,我并不是要将变量分配给现有对象。例如:
// Example 1: I did not mean this
-------------------------
String x = "Hello World";
String y = x;
-------------------------
// Example 2: What I meant is this
-------------------------
String x = "Hello World";
//This will print out "Hello World"
System.out.println(x);
x = "Goodbye World";
//This will print out "Goodbye World"
System.out.println(x);
-------------------------
当然,将变量 y 分配给变量 x,如示例 1 所示,这是你们提出的情况,仅将变量 y 引用到 x 引用的对象。当然,在这种情况下,没有新对象;只是同一个对象“Hello World”被 2 个变量引用。
我的意思是,当示例 2 中的 x = “Goodbye World” 时,变量 x 引用值为 “Goodbye World” 的新 String 对象,而不是初始化 x 的第一个 String 对象 “Hello World”。String 是 Java 中的不可变对象。更改 String 变量值的唯一方法是让该变量引用现有对象或新的 String 对象。如果没有现有对象(“Goodbye World”String 对象尚未在任何地方创建),则上面的代码只是创建了一个新的 String 对象并引用 x。我说得对吗?如果没有,请纠正我。
问题#2:我想总结一下答案,尤其是来自 Ulfalizer 的答案:
1)变量实际上有两种形式可以存在:
a) “内存地址” - 这是 C 语言中的变量,以及 Java 和 Objective C 中关于原始类型数据的情况。例如:int x = 1。这里的变量 x 本身是一个实际的内存地址,它的类型为整数,初始化值为 1。
b) “引用” - 对于非原始类型的数据(对象),Java 中的大多数变量都是这种情况。例如:String x = “Hello World”。变量 x 只是指向“存在于某处的内存地址”的指针/引用,它包含值“Hello World”作为其内容。
2) C、Java 和 Objective C 中基元类型数据的变量表现为“内存地址”。因此,当我这样做时:
-------------------------
int x = 10;
x = 2;
-------------------------
x 变量(又名 - 内存地址)的值实际上从 10 更改为 2。换句话说,存储在“内存地址”变量中的值可以修改/更改。
3)在C语言中,如果变量声明为“*”-指针类型,则该变量也可以充当引用。我将使用 Ulfalizer 的例子:int *ptr。ptr 是一个指针变量,可以指向另一个变量(又名内存地址),例如:ptr = &x。如果 x 初始化为:int x = 10,则 x 是保存值为 10 的实际内存地址。所以在下面的代码中
-------------------------
int x;
int *ptr;
ptr = &x;
*ptr = 1;
-------------------------
我实际上可以通过 ptr 指针变量修改存储在 x 变量(又名 - 内存地址)中的值。
请确认我的解释是否正确/错误。谢谢。
答:
它应该是修改后的当前基元。我在这里使用一个简单的代码进行了测试,它引用了相同的地址。
#include <stdio.h>
int main(void) {
// your code goes here
int a = 5;
printf ("%p = %i\n", (void*) &a, a);
a = 10;
printf ("%p = %i\n", (void*) &a, a);
return 0;
}
评论
理解 C 工作原理的最好方法是将其视为高级汇编语言。变量只是内存中的位置,为变量赋值会将该值存储到该位置。从高级语言的角度来看,这将是最纯粹的突变形式。
在 C 语言中,像
int x;
告诉编译器为变量保留一些内存。(在函数内部,内存将来自堆栈。在全局范围内,它将来自数据段。int
x
像这样的任务
x = 7;
只需生成代码即可将值 7 复制到该内存位置。同样,像这样的任务
x = y;
生成代码以将存储在 的内存位置的值复制到 的内存位置。(假设是另一个。int
y
x
y
int
同样的逻辑也适用于比 s 更复杂的类型。int
在 Java 中,变量是引用(无论如何对于非原始类型),并且可以在不同的时间引用不同的对象。为了获得类似于 C 语言中的引用的东西,你必须显式定义一个指针变量——一个保存地址的变量——并在不同的时间将其指向(为其分配地址)不同的对象。
(我包括了下面的例子,以防万一你感兴趣。
运算符用于获取 C 中变量的地址,因此这将是例如的地址。运算符在应用于指针时提供指向的对象。下面是一个示例,说明如何使用指针在不同时间引用不同的变量:&
&x
x
*
int x;
int y;
/* Declares 'ptr' as a pointer, and says that it points to an int.
The pointed-to type is used by the compiler for type checking
and type conversions. */
int *ptr;
ptr = &x; // Store the address of 'x' in 'ptr'.
*ptr = 1; // Store 1 into the memory 'ptr' points to ('x').
ptr = &y; // Store the address of 'y' in 'ptr'.
*ptr = 2; // Store 2 into the memory 'ptr' points to ('y').
上述代码的最终结果是设置为 1 和 2。这当然是一个愚蠢的例子,但希望它应该传达这个想法。x
y
(顺便说一下,C99 及更高版本支持 -style 注释。//
问题的答案
(注意:我的Java有点生疏,所以我不得不做一些阅读。希望细节应该是正确的。请随时纠正我。
问题 #1
任务
x = "Goodbye World";
将具有使引用值为“Goodbye world”的对象的效果。此对象的确切创建时间应该没有区别,只要它是在分配给(或任何其他变量)之前创建的。x
String
String
x
它可能在执行分配之前或程序启动时创建。通常你无法分辨出区别。
问题 #2
听起来你已经掌握了高级概念,你的 C 代码是正确的。
(不过,说“是一个指针”比说“是一个指针”更正确。C 语言中有一些语法上的模糊性,这使得在声明 (IMO) 中将名称放在下一个更自然,但您也可以像这种情况一样编写声明。只有在同一行中声明许多变量时,事情才会开始变得奇怪。ptr
*ptr
*
int* ptr;
在谈论 Java 是如何实现的时,引用可能必须比指向后台对象的指针更高级一些。例如,JVM 可能会在内存中移动对象(这将更改其地址)以减少内存碎片,但引用仍必须保持有效。一种方法是在移动对象时“固定”指针,另一种方法是将引用作为指针表的索引。无论在 JVM 实现中如何完成,指针至少是正确的想法。
由于 Java 中的每个引用都有一个指针字段(在高层次上,完全忽略了实现是如何执行的),并且由于基元类型不需要此字段,因此一种可能性是重用指针字段并将值存储在其中,而不是用于基元类型。
例如,像这样的作业
x = 1;
可能会将值 1 直接存储到引用的指针字段中。像这样的技术很常见。x
顺便说一句,a 可以在 C 中用于将两种不同类型的(例如,an 和 a指针)存储在内存中的同一位置,以便它们重叠。当然,分配给其中一种类型也会更改另一种类型的值。union
int
评论
x
y
x = y
y
x
*ptr
评论
String s1 = "abc"; s1 = s2;
s1 = s1.substring(2, 3);
substring
int
double
char