在 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
。)
同样的逻辑也适用于比 int
s 更复杂的类型。
在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
和指针)存储在内存中的同一位置,以便它们重叠.当然,分配给其中一种类型也会更改另一种类型的值。
我知道 '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
。)
同样的逻辑也适用于比 int
s 更复杂的类型。
在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
和指针)存储在内存中的同一位置,以便它们重叠.当然,分配给其中一种类型也会更改另一种类型的值。