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

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

我知道 'mutable' 和 'immutable' 是应该用来描述对象在面向对象语言中改变值的能力的术语,例如 Java 和 Objective C.但是,我想提出来,因为它与我关于基元数据的问题有关。我知道当我改变一个持有不可变对象的变量的值时,我实际上是在创建一个新对象。但是,我想知道 C 中的基元数据的行为是否类似于不可变对象。我的意思是,当我更改保存原始数据的变量的值时,会创建一个新数据并由该变量引用。或者现有的原语实际上 mutated/modified 存储的数据值?

编辑#1:

问题 #1: 我想澄清一些误解(无论是对我还是对其他人),因为我在说 "when I change the value of a variable holding immutable object, I am in fact creating a new object." 当我这么说时,我并不是要将变量分配给现有对象。例如:

// 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 中的 x = “Goodbye World” 时,变量 x 正在引用一个新的 String 对象,其值为“Goodbye World”,而不是 x 的第一个 String 对象“Hello World”被初始化。字符串是 Java 中的不可变对象。更改 String 变量值的唯一方法是让变量引用现有对象 新 String 对象。如果不存在现有对象(“再见世界”String 对象尚未在任何地方创建),那么上面的代码只是创建了一个新的 String 对象并引用 x 到它。我对吗?如果不对,请指正。

问题 #2: 我想总结一下答案,尤其是 Ulfalizer:

1) 变量实际上可以存在两种形式:

a) “内存地址”——这是 C 语言中的变量,以及 Java 和 Objective C 关于原始类型数据的情况。例如:int x = 1。这里的变量x本身就是一个实际的内存地址,类型为integer,初始化值为1。

b) “参考”- Java 中的大多数变量都是这种情况,关于非原始类型数据(对象)。例如:String x = “Hello World”。变量 x 只是一个 pointer/reference 到“某处存在的内存地址”,其内容为“Hello World”。

2) C、Java 和 Objective C 中原始类型数据的变量表现为“内存地址”。因此,当我这样做时:

-------------------------
int x = 10;
x = 2;
-------------------------

x变量(又名-内存地址)的值实际上从10变为2。换句话说,存储在“内存地址”变量中的值可以是modified/changed。

3) 在C语言中,如果变量声明为‘*’——指针类型,它也可以作为引用。我将使用 Ulfalizer 的示例:int *ptr。 ptr 是一个指针变量,可以指向另一个变量(又名内存地址),例如:ptr = &x。如果x被初始化为:int x = 10,那么x是一个实际的内存地址,它的值为10。所以在下面的代码中

-------------------------
int x;
int *ptr;
ptr = &x;
*ptr = 1;
-------------------------

我实际上可以通过 ptr 指针变量修改存储在 x 变量(又名 - 内存地址)中的值。

请确认我的解释是否correct/false。谢谢你。

应该是修改的当前图元。我用一个简单的代码 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;
}

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

在 C 中,declaration/definition 喜欢

int x;

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

这样的作业
x = 7;

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

这样的赋值
x = y;

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

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

在Java中,变量是引用(无论如何对于非基本类型),并且可以在不同的时间引用不同的对象。要获得类似于 C 中引用的内容,您必须显式定义一个 pointer 变量——一个保存地址的变量——并将其指向(为其分配地址)不同的对象在不同的时间。

(我提供了以下示例以防您感兴趣。)

&运算符用于获取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; 之类的声明。当您在同一行中声明许多变量时,事情只会开始变得奇怪。 )

在谈论如何实现 Java 时,引用可能必须比仅指向引擎盖下的对象的指针更高级一些。例如,JVM 可能会在内存中移动对象(这会改变它们的地址)以减少内存碎片,但引用必须仍然有效。一种方法是在对象移动时指向 "fix" 指针,另一种方法是将引用作为指向 table 指针的索引。不管在JVM实现中是如何实现的,pointer至少是正确的思路。

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

例如,像这样的作业

x = 1;

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

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