在C中更改变量的值时,是创建了一个新原语还是当前原语是变异的?
我知道’mutable’和’immutable’是应该用来描述对象改变面向对象语言(如Java和Objective 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); -------------------------
当然,如示例1所示,将变量y赋值给变量x,这是你们提出的情况,只引用变量y到x引用的对象。 当然,在这种情况下,没有新的对象; 只有同一个对象“Hello World”被2个变量引用。
我的意思是当例子2中x =“Goodbye World”时,变量x引用一个新的String对象,其值为“Goodbye World”,而不是x初始化的第一个String对象“Hello World”。 String是Java中的Immutable对象。 更改String变量值的唯一方法是让变量引用现有对象或新String对象。 如果没有现有对象(“Goodbye World”字符串对象未在任何地方创建),则上面的代码只是创建了一个新的String对象并引用了它。 我对吗? 如果没有,请纠正我。
问题#2:我想总结答案,特别是Ulfalizer的答案:
1)实际上有两种forms可以存在变量:
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变量(也就是内存地址)中的值。
请确认我的解释是否正确/错误。 谢谢。
理解C如何工作的最好方法是将其视为高级汇编语言。 变量只是内存中的位置,为变量赋值会将值存储到该位置。 从高级语言的角度来看,这将是最纯粹forms的变异。
在C中,声明/定义如
int x;
告诉编译器为int
变量x
保留一些内存。 (在函数内部,内存将来自堆栈。在全局范围内,它将来自数据段。)
像。这样的作业
x = 7;
只需生成代码即可将值7复制到该内存位置。 同样,一个如此的作业
x = y;
生成代码以将存储在y
的内存位置的int
值复制到x
的内存位置。 (假设y
是另一个int
。)
对于比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').
上面代码的最终结果是将x
设置为1,将y
为2.这当然是一个愚蠢的例子,但希望它应该得到这个想法。
(C99及以后支持//
风格的评论。)
问题的答案
(警告:我的Java有点生疏,所以我不得不做一些阅读。希望细节应该是正确的。请随意纠正我。)
问题#1
分配
x = "Goodbye World";
会产生使x
引用值为“Goodbye world”的String
对象的效果。 确切地说,创建此String
对象时 ,只要在将其分配给x
(或任何其他变量)之前创建它就不会产生影响。
它可能在执行赋值之前或程序启动时创建。 通常你无法区分它们。
问题#2
听起来你有高级概念,你的C代码是正确的。
(说“ ptr
是一个指针”比说“ *ptr
是一个指针”更正确。在C中有一些语法含义使得在声明(IMO)中放下*
名称更为自然,但你也可以编写像int* ptr;
这样的声明int* ptr;
对于那种情况。当你在同一行声明许多变量时,事情才开始变得怪异。)
在谈论如何实现Java时,引用可能必须比仅仅指向引擎下的对象更高级。 例如,JVM可能会在内存中移动对象(这将更改其地址)以减少内存碎片,但引用仍必须保持有效。 一种方法是在移动对象时“修复”指针,另一种方法是将引用转换为指针表的索引。 无论在JVM实现中如何完成, 指针至少是正确的思路。
由于Java中的每个引用都有一个指针字段(在高级别,忽略了实现的确切方式),并且由于原始类型不需要该字段,因此一种可能性是重用指针字段并将值存储在其中而是原始类型。
例如,像
x = 1;
可能会将值1直接存储到引用x
的指针字段中。 像这样的技术很常见。
作为旁注,可以使用union
在C中将两种不同类型(例如, int
和指针)存储在存储器中的相同位置,以使它们重叠。 当然,分配其中一种类型也会改变另一种类型的值。
它应该是当前原始修改。 我在这里使用一个简单的代码测试,它指的是相同的地址。
#include 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; }