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

时间:2015-03-25 05:01:48

标签: java objective-c c primitive 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);
-------------------------

当然,如示例1所示,将变量y赋值给变量x,这是你们提出的情况,只引用变量y到x引用的对象。当然,在这种情况下,没有新的对象;只有同一个对象“Hello World”被2个变量引用。

我的意思是当示例2中的x =“Goodbye World”时,变量x引用一个新的String对象,其值为“Goodbye World”,而是第一个String对象“Hello World”,x被初始化用。 String是Java中的Immutable对象。更改String变量值的唯一方法是让变量引用现有对象 OR 一个新的String对象。如果没有现有对象(“Goodbye World”字符串对象未在任何地方创建),则上面的代码只是创建了一个新的String对象并引用了它。我对吗?如果没有,请纠正我。

问题#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变量(也就是内存地址)中的值。

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

2 个答案:

答案 0 :(得分:6)

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

在C中,声明/定义如

int x;

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

这样的作业
x = 7;

只需生成代码即可将值7复制到该内存位置。同样,像

这样的作业
x = y;

生成代码,将存储在int内存位置的y值复制到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;的声明。当你在同一行中声明许多变量时,事情才开始变得奇怪。)< / p>

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

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

例如,像

这样的作业
x = 1;

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

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

答案 1 :(得分:1)

应修改当前原语。我使用简单的代码here进行了测试,它引用了相同的地址。

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