当在C中更改变量的值时,是创建了一个新原语还是当前原语是变异的?

lan*_*eky 4 c java primitive objective-c mutable

我知道'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);
-------------------------
Run Code Online (Sandbox Code Playgroud)

当然,如示例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)实际上有两种形式可以存在变量:

a)"存储器地址" - 对于基本类型数据,C语言中的变量以及Java和目标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;
-------------------------
Run Code Online (Sandbox Code Playgroud)

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;
-------------------------
Run Code Online (Sandbox Code Playgroud)

我实际上可以通过ptr指针变量修改存储在x变量(也就是内存地址)中的值.

请确认我的解释是否正确/错误.谢谢.

Ulf*_*zer 6

理解C如何工作的最好方法是将其视为高级汇编语言.变量只是内存中的位置,为变量赋值会将值存储到该位置.从高级语言的角度来看,这将是最纯粹形式的变异.

在C中,声明/定义如

int x;
Run Code Online (Sandbox Code Playgroud)

告诉编译器为int变量保留一些内存x.(在函数内部,内存将来自堆栈.在全局范围内,它将来自数据段.)

像.这样的作业

x = 7;
Run Code Online (Sandbox Code Playgroud)

只需生成代码即可将值7复制到该内存位置.同样,一个任务就像

x = y;
Run Code Online (Sandbox Code Playgroud)

生成代码以将int存储在y内存位置的值复制到x内存位置.(假设y是另一个int.)

对于比ints 更复杂的类型,同样的逻辑也适用.

在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').
Run Code Online (Sandbox Code Playgroud)

上面代码的最终结果将是设置x为1和y2.这当然是一个愚蠢的例子,但希望它应该得到这个想法.

(//顺便提一下C99及以后的支持式评论.)

问题的答案

(警告:我的Java有点生疏,所以我不得不做一些阅读.希望细节应该是正确的.请随意纠正我.)

问题#1

分配

x = "Goodbye World";
Run Code Online (Sandbox Code Playgroud)

将具有x引用String具有值"Goodbye world" 的对象的效果.确切地说,String创建此对象,只要在将其分配给x(或任何其他变量)之前创建它就不会有所作为.

它可能在执行赋值之前或程序启动时创建.通常你无法区分它们.

问题#2

听起来你有高级概念,你的C代码是正确的.

(说" ptr是一个指针"而不是说" *ptr是一个指针" 更正确.C中有一些语法含义使得将*下一个名称放在声明(IMO)中更自然,但你可以也就int* ptr;这种情况编写声明.当你在同一行中声明许多变量时,事情才开始变得奇怪.)

在谈论如何实现Java时,引用可能必须比仅仅指向引擎下的对象更高级.例如,JVM可能会在内存中移动对象(这将更改其地址)以减少内存碎片,但引用仍必须保持有效.一种方法是在移动对象时"修复"指针,另一种方法是将引用转换为指针表的索引.无论在JVM实现中如何完成,指针至少是正确的思路.

由于Java中的每个引用都有一个指针字段(在高级别,忽略了实现的确切方式),并且由于原始类型不需要该字段,因此一种可能性是重用指针字段并将值存储在其中而是原始类型.

例如,像

x = 1;
Run Code Online (Sandbox Code Playgroud)

可能会将值1直接存储到引用的指针字段中x.像这样的技术很常见.

作为旁注,a union可以用于在C中将两种不同类型(例如,a int和指针)存储在存储器中的相同位置,以使它们重叠.当然,分配其中一种类型也会改变另一种类型的值.