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

When changing the value of a variable in C, is a new primitive created or is the current primitive mutated?

提问人:langtutheky 提问时间:3/25/2015 最后编辑:langtutheky 更新时间:3/26/2015 访问量:1987

问:

我知道“可变”和“不可变”是应该用来描述对象在面向对象的语言(如 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 变量(又名 - 内存地址)中的值。

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

爪哇岛 目标-C c 原始 可变

评论

1赞 AntonH 3/25/2015
要知道,即使在 Java 中,Object 和原始数据类型之间也存在差异。而且原语不是一成不变的。
0赞 Dawood ibn Kareem 3/25/2015
如果您认为更改变量值会在您提到的任何语言中创建一个新对象,那么我建议您进行更多研究。
0赞 ajb 3/25/2015
“我知道,当我更改包含不可变对象的变量的值时,我实际上是在创建一个新对象”——不,在 Java 中不是真的。对于非基元,变量只是引用。如果更改变量的值,则会使其指向其他对象。但是,您调用的方法可能会创建一个新对象。 赋值不会创建新对象。--该方法创建新对象,而不是赋值。String s1 = "abc"; s1 = s2;s1 = s1.substring(2, 3);substring
0赞 langtutheky 3/26/2015
@ajb - 您能否检查上面的问题 #1?
0赞 Hot Licks 3/26/2015
“原始”类似于 or 或 。你似乎(至少在你问题的第一部分)在谈论“对象”,而不是“原语”。intdoublechar

答:

1赞 rcs 3/25/2015 #1

它应该是修改后的当前基元。我在这里使用一个简单的代码进行了测试,它引用了相同的地址。

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

评论

0赞 langtutheky 3/26/2015
感谢您的直截了当的回答 - “当前原始修改”,但我希望能得到更彻底的解释。
6赞 Ulfalizer 3/25/2015 #2

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

在 C 语言中,像

int x;

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

像这样的任务

x = 7;

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

x = y;

生成代码以将存储在 的内存位置的值复制到 的内存位置。(假设是另一个。intyxyint

同样的逻辑也适用于比 s 更复杂的类型。int

在 Java 中,变量是引用(无论如何对于非原始类型),并且可以在不同的时间引用不同的对象。为了获得类似于 C 语言中的引用的东西,你必须显式定义一个指针变量——一个保存地址的变量——并在不同的时间将其指向(为其分配地址)不同的对象。

(我包括了下面的例子,以防万一你感兴趣。

运算符用于获取 C 中变量的地址,因此这将是例如的地址。运算符在应用于指针时提供指向的对象。下面是一个示例,说明如何使用指针在不同时间引用不同的变量:&&xx*

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。这当然是一个愚蠢的例子,但希望它应该传达这个想法。xy

(顺便说一下,C99 及更高版本支持 -style 注释。//

问题的答案

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

问题 #1

任务

x = "Goodbye World";

将具有使引用值为“Goodbye world”的对象的效果。此对象的确切创建时间应该没有区别,只要它是在分配给(或任何其他变量)之前创建的。xStringStringx

它可能在执行分配之前或程序启动时创建。通常你无法分辨出区别。

问题 #2

听起来你已经掌握了高级概念,你的 C 代码是正确的。

(不过,说“是一个指针”比说“是一个指针”更正确。C 语言中有一些语法上的模糊性,这使得在声明 (IMO) 中将名称放在下一个更自然,但您也可以像这种情况一样编写声明。只有在同一行中声明许多变量时,事情才会开始变得奇怪。ptr*ptr*int* ptr;

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

由于 Java 中的每个引用都有一个指针字段(在高层次上,完全忽略了实现是如何执行的),并且由于基元类型不需要此字段,因此一种可能性是重用指针字段并将值存储在其中,而不是用于基元类型。

例如,像这样的作业

x = 1;

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

顺便说一句,a 可以在 C 中用于将两种不同类型的(例如,an 和 a指针)存储在内存中的同一位置,以便它们重叠。当然,分配给其中一种类型也会更改另一种类型的值。unionint

评论

0赞 langtutheky 3/26/2015
感谢您的回答。你的解释非常清楚,最后一个例子是完美的。请您看看我对您上面回答的解释(问题#2,但我很想听听您对问题#1的看法:))?真的很感激。
0赞 Ulfalizer 3/26/2015
@langtutheky:谢谢!我也添加了问题的答案。
0赞 langtutheky 3/26/2015
感谢您的回复。我有一个小问题:当您想将指针分配给另一个指针时,我不需要前面的星号,对吧?示例:int x = 10;int *a;*a = x;int *b;b = a。这将使 b 指向与指针 a 相同的地址,对吗?我问这个问题的原因是为了解释目标 C 表示法:NSString *x = @“Hello”;x = @“世界”。在目标 C 的情况下,我相信 *x 是一个指针,@“World” 是另一个指针。这可以解释为什么 x = @“World” 在目标 C 中是合法的,而不是 *x = @“World”。
1赞 Ulfalizer 3/26/2015
@langtutheky:正确。如果 和 是指针,则将存储在 中的地址复制到 ,以便它们指向之后的同一位置。仅当您要访问指针指向的值时,才执行此操作。xyx = yyx*ptr
0赞 Ulfalizer 3/26/2015
我不知道目标C,但这至少听起来很合理。:)