初始化与C中的赋值

时间:2016-02-26 22:20:58

标签: c language-lawyer

我的导师最近告诉C中的数组初始化有两种方式,即:

  1. 手动赞int a[5]={1,2,3,4,5};
  2. 使用scanf()int a[5], i; for(i=0;i<5;i++) scanf("%d", &a[i]);
  3. 在我看来,第二种方式&#34;是一种赋值而不是初始化的方式。所以我决定检查这里的人们对此有何看法。我偶然发现this帖子,其中一个答案声称:

      

    如果您要询问的是术语(*不是很清楚   从你的问题),&#34;初始化&#34;变量的字面意思是   第一次为其分配值。这个词来自事实   你给变量它&#34;&#34; initial&#34;值。

         

    这应该(显然)在第一次使用之前发生。

         

    int x=5;是一个声明和初始化,实际上就是这样   

    的便捷简写      

    int x; x=5;

    如果我要遵循这个特定答案所声称的内容,那么第二种方式是&#34;初始化&#34;是正确的,因为在scanf()语句之前没有赋值。但是,由于我对静态变量的了解,我脑子里出现了一个新的疑问。请考虑以下代码:

    #include <stdio.h>
    
    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()版本更像是赋值版本而不是初始化,即使x[]的元素没有值在scanf()语句之前分配了。回到原点。

    那么,第二个版本是否真的像我的导师声称的初始化或仅仅是一个任务(我相信)?

    编辑:

    有人指出,我觉得我的static数组示例很差,因为static变量无论如何都隐式初始化为0。然后,有人向我指出const变量。

    考虑const int x = 2;这里可以初始化x,但在初始化后无法为其分配任何值。但这与声称的答案相矛盾(我再次引用它):

      

    int x = 5;是一个声明和初始化,实际上就是这样   int x; x=5;

    的便捷简写

    所以,在这之后,scanf()版本是否有资格作为初始化程序?

5 个答案:

答案 0 :(得分:7)

在C标准中,只有选项(1)是初始化。

在编程术语中,两者都可以被认为是初始化。你的问题实际上是在询问单词的含义。

人们使用具有各种常见含义的单词是正常的,而不是为特定语言切换术语。另一个例子是“通过引用传递”。 C是否通过引用传递?有些人认为它只是通过价值传递,其他人则认为通过指针传递实现“通过引用传递”的概念。

然后我们可以讨论深拷贝与浅拷贝(C标准根本没有提到),或者术语“堆栈”和“堆”(C标准没有提到,但是常用)由C程序员),等等。

如果你说{ int b; b = 5; }没有初始化(因为C标准说它不是),那么为了保持一致,你还应该说b不是堆栈变量。

答案 1 :(得分:2)

这里有两个非常密切相关的概念。

初始值设定项是一种特定的句法结构。在声明中

int n = 42;

42初始值设定项。在声明中

n = 42;

42不是initializer; n = 42在语法上是赋值表达式

另一方面,标准也使用&#34;初始化&#34;引用初始值设定项以外的内容。例如,引用N1570第6.3.2.1节:

  

如果左值指定一个自动存储持续时间的对象   可以使用 register 存储类声明(从来没有   它的地址),并且该对象未初始化(未声明   使用初始化程序并且之前未执行任何赋值   使用),行为未定义。

因此,初始化程序始终是声明的一部分,而不是赋值 - 但如果使用初始化程序定义了一个对象,则称该对象是初始化,如果它是隐式初始化的,因为它是静态的,或者一个值已分配给它

答案 2 :(得分:1)

1)声明

 int a;

2)初始化

int a=10;

3)分配

a=10;

可以初始化阵列,但不能分配。

int arrA[3] = {1,3,2};

这将是非法的:

arrB = arrA; 

答案 3 :(得分:1)

根据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

很明显,第二种方式并非实际&#34;初始化&#34;。但是,在大多数情况下,它在功能上与初始化相同。更重要的是,在指定存储在包含10000个元素的数组中的初始值时,您不想使用初始化列表。

答案 4 :(得分:-2)

你是对的,有点儿。使用“初始化”来引用这两种情况令人困惑,因为存在非常重要的区别。

您所称的“第一路”通常称为“静态初始化”。当您编写“static int x [2] = {1,2}”时,编译器将生成一个包含值“1,2”的数据块,然后初始化X以指向包含初始值的内存。因此,在代码中引用x [0]始终是安全的。

当您只编写“static int x [2]”时,编译器将为x保留内存,但您不知道该内存的初始化内容。一般来说,在使用x [0]之前,您的代码必须在该内存中写入内容。在复杂的应用程序或库中,特别是对于多个线程,确保x只被初始化一次并且只被初始化一次可能很棘手。

静态初始化始终是首选。