在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; }