初始化与C中的赋值
我的导师最近告诉C中的数组初始化有两种方式,即:
- 手动像
int a[5]={1,2,3,4,5};
- 使用
scanf()
如int a[5], i; for(i=0;i<5;i++) scanf("%d", &a[i]);
int a[5], i; for(i=0;i<5;i++) scanf("%d", &a[i]);
在我看来,第二种“方式”是一种分配而不是初始化的方式 。 所以我决定检查这里的人们对此有何看法。 我偶然发现了一篇文章,其中一个答案声称:
如果您要问的是术语(*从您的问题中并不是很清楚),那么变量的“初始化”实际上是第一次为其赋值。 这个术语来自于你给变量它的“初始”值。
这应该(显然)在第一次使用之前发生。
int x=5;
是一个声明和一个初始化,实际上只是方便的简写
int x; x=5;
如果我要遵循这个特定答案所声称的内容,那么第二种“初始化”方式是正确的,因为在scanf()
语句之前没有赋值。 但是,由于我对静态变量的了解,我脑子里出现了一个新的疑问。 请考虑以下代码:
#include void first_way(){ static int x[2]={1,2}; printf("first_way called %d time(s)\n",++x[0]); } void second_way(){ int i; static int x[2]; for(i=0;i<2;i++)scanf("%d",&x[i]); printf("second_way called %d time(s)\n",++x[0]); } int main(void){ int i; for(i=0;i<3;i++) first_way(); printf("\n#######\n"); for(i=0;i<3;i++) second_way(); return 0; }
它的输出是这样的:
first_way called 2 time(s) first_way called 3 time(s) first_way called 4 time(s) ####### 1 2 second_way called 2 time(s) 1 2 second_way called 2 time(s) 1 2 second_way called 2 time(s)
此输出再次使我认为scanf()
版本更像是赋值版本而不是初始化,即使在scanf()
语句之前没有为x[]
元素赋值。 回到原点。
那么,第二个版本是否真的像我的导师声称的初始化或仅仅是一个任务(我相信)?
编辑:
有人指出后,我觉得我的static
数组示例很差,因为static
变量无论如何都被隐式初始化为0。 然后,有人将我指向const
变量。
考虑const int x = 2;
这里可以初始化x
,但是在初始化之后不能为它分配任何值。 但这与声称的答案相矛盾(我再次引用它):
int x = 5;
是一个声明和一个初始化,实际上只是简单的int x; x=5;
简写int x; x=5;
int x; x=5;
那么,毕竟这个, scanf()
版本是否有资格作为初始化器?
在C标准中,只有选项(1)是初始化。
在编程术语中,两者都可以被认为是初始化。 你的问题实际上是在询问单词的含义。
人们使用具有各种常见含义的单词而不是为特定语言切换术语是正常的。 另一个例子是“通过引用传递”。 C是否通过引用传递? 有些人认为它只是通过值传递,其他人则认为通过指针实现“通过引用传递”的概念。
然后我们可以谈谈深拷贝与浅拷贝(C标准根本没有提到),或术语“堆栈”和“堆”(C标准也没有提到,但C程序员常用) ), 等等。
如果你说{ int b; b = 5; }
{ int b; b = 5; }
不是初始化(因为C标准说它不是)然后,为了保持一致,你还应该说b
不是堆栈变量。
这里有两个非常密切相关的概念。
初始化器是特定的句法结构。 在宣言中
int n = 42;
42
是初始化器 。 在声明中
n = 42;
42
不是initializer
; n = 42
在语法上是赋值表达式 。
另一方面,标准也使用“初始化”一词来指代初始化者以外的东西。 例如,引用N1570第6.3.2.1节:
如果左值指定了一个自动存储持续时间的对象,该对象可以使用
register
存储类声明(从未使用其地址),并且该对象未初始化(未使用初始化程序声明,并且在使用之前未对其进行任何赋值) ),行为未定义。
因此初始化程序始终是声明的一部分,而不是赋值 – 但是如果一个对象是用初始化程序定义的,如果它是隐式初始化的,因为它是静态的,或者如果已为其赋值 ,则说该对象被初始化 。
1)声明
int a;
2)初始化
int a=10;
3)分配
a=10;
数组可以初始化,但不能分配给。
int arrA[3] = {1,3,2};
这将是非法的:
arrB = arrA;
根据N1570 6.7.9.1:
initializer: assignment-expression { initializer-list } { initializer-list , } initializer-list: designationopt initializer initializer-list , designationopt initializer designation: designator-list = designator-list: designator designator-list designator designator: [ constant-expression ] . identifier
显然,第二种方式实际上并不是“初始化”。 但是,在大多数情况下,它在function上与初始化相同。 更重要的是,在指定存储在包含10000个元素的数组中的初始值时,您不希望使用初始化列表。
你是对的,有点儿。 使用“初始化”来指代这两种情况令人困惑,因为存在非常重要的区别。
你所谓的“第一种方式”通常称为“静态初始化”。 当您编写“static int x [2] = {1,2}”时,编译器将生成一个包含值“1,2”的数据块,然后初始化X以指向包含初始值的内存。 因此,在代码中引用x [0]始终是安全的。
当您只编写“static int x [2]”时,编译器将为x保留内存,但您不知道该内存的初始化内容。 一般来说,在使用x [0]之前,您的代码必须在该内存中写入内容。 在复杂的应用程序或库中,特别是对于多个线程,确保x只被初始化一次并且只被初始化一次是非常棘手的。
静态初始化总是更可取的。