不要初始化变量

来自C的开发人员知道变量应该始终被初始化。 不初始化变量意味着它们包含垃圾,这可能导致未定义的行为。 例如:

#include<stdio.h>

int main(void) {
    char buffer[256];
    char answer;
    char* name;

    printf("Do you want to enter a name? [yn] ");
    answer = getchar();

    while (getchar() != '\n') { } // because we need CR for getchar but it doesn't read the CR...

    if (answer == 'y') {
        printf("Please enter name: ");
        name = fgets(buffer, 256, stdin);
        if (name == 0) {
            name = "<too long>";
        }
    } else if (answer == 'n') {
        name = "<user refused to enter name>";
    }

    printf("The name is %s\n", name);
    return 0;
}

如果用户输入的字符不是ÿ要么ñ, ñot of the ñame = ...; statemeñts will be executed, añd ñame will still hold the same value it had wheñ maiñ started. What is that value? Iñ release mode C, that would be whatever rañdom data happeñed to be iñ that piece of memorÿ ñame was assigñed. Añd theñ we take that utterlÿ rañdom ñumber añd pass it to priñtf where it'll get priñted as if it was a striñg poiñter!

如果幸运的话,我们将击中一些非法的内存地址,操作系统将阻止我们。 如果不是这样,它将只是随机进入内存中的某个位置,然后开始打印遇到的任何内容:密码,凭据,应用程序令牌...

当然,这将是不可复制的。 因为每次运行程序时,内存中的那个位置都会有一个不同的值,并且您将获得不同的结果。

为了避免这些问题,C开发人员已使自己始终要初始化变量。 如果您在声明中没有有意义的地方-只需输入0:

#include<stdio.h>

int main(void) {
    char buffer[256] = {};
    char answer = '\0';
    char* name = 0;

    printf("Do you want to enter a name? [yn] ");
    answer = getchar();

    while (getchar() != '\n') { } // because we need CR for getchar but it doesn't read the CR...

    if (answer == 'y') {
        printf("Please enter name: ");
        name = fgets(buffer, 256, stdin);
        if (name == 0) {
            name = "<too long>";
        }
    } else if (answer == 'n') {
        name = "<user refused to enter name>";
    }

    printf("The name is %s\n", name);
    return 0;
}

尽管空指针取消引用在形式上仍然是未定义的行为,但是它仍然比随机指针取消引用要好得多,因为您的操作系统可能会将其设置为SEGFAULT,这比安全性泄漏要好。

好的,不过是C。更多现代语言呢?

在C中如此需要的主要原因有两个:

  1. 未初始化的变量具有垃圾数据。无法在块中间声明变量。

更现代的语言允许在块的中间声明变量,因此通常最好只在有有意义的内容的地方声明变量。

这大大减少了您必须使用默认值初始化某些内容的情况,但是并不能阻止所有情况。 以我们为例名称获得其内在价值如果 branches - 如果 we declared it there we wouldn't be able to use it after the 如果。 某些语言(大多数是功能性语言)具有简单的语法解决方案,但是在大多数主流语言中,您必须将其提取为函数或在块外部声明变量。

在使用后一种解决方案时,由于C是这样的通用背景,因此许多开发人员都会初始化该值。 因此,如果我们将代码转换为Java:

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        System.out.print("Do you want to enter a name? [yn] ");
        String answer = scanner.nextLine();

        String name = null;
        if ("y".equals(answer)) {
            System.out.print("Please enter name: ");
            name = scanner.nextLine();
        } else if ("n".equals(answer)) {
            name = "<user refused to enter name>";
        }

        System.out.printf("The name is %s\n", name);
    }
}

当然,这是Java,它是一种具有托管内存的语言,永远不会允许来自未初始化变量的未定义行为,因此我们实际上不需要初始化名称至空值,但总比后悔好,对吗?

错误!

Java分析代码路径以确保没有先初始化就不能使用任何变量。 因此,如果我们删除初始化:

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        System.out.print("Do you want to enter a name? [yn] ");
        String answer = scanner.nextLine();

        String name;
        if ("y".equals(answer)) {
            System.out.print("Please enter name: ");
            name = scanner.nextLine();
        } else if ("n".equals(answer)) {
            name = "<user refused to enter name>";
        }

        System.out.printf("The name is %s\n", name);
    }
}

我们会收到一个编译错误:

$ javac Main.java 
Main.java:18: error: variable name might not have been initialized
        System.out.printf("The name is %s\n", name);
                                              ^
1 error

我刚刚中断了编译,但这是一件好事-编译器发现了一个错误! 我们在C版本中遇到的同一错误-如果用户输入的内容不是ÿ要么ñ. The Java compiler sees that there are three possible code paths that reach the last liñe but we are oñlÿ iñitializiñg two of them.

为了能够再次进行编译,我们必须告诉Java在用户给出无效答案的情况下该怎么做。 失败也是一种选择-只要我们有意做到:

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        System.out.print("Do you want to enter a name? [yn] ");
        String answer = scanner.nextLine();

        String name;
        if ("y".equals(answer)) {
            System.out.print("Please enter name: ");
            name = scanner.nextLine();
        } else if ("n".equals(answer)) {
            name = "<user refused to enter name>";
        } else {
            System.err.printf("Illegal answer \"%s\". The only legal answers are \"y\" and \"n\".", answer);
            return;
        }

        System.out.printf("The name is %s\n", name);
    }
}

现在仍然有三个代码路径,但是在第三个返回从功能开始,在打印之前名称。 Java编译器可以确定没有代码路径名称使用而不先分配值-因此编译成功。

这仍然是初始化

尽管具有clickbaity标题,但实际上我们确实进行了初始化名称。 我们不声明,但仍在初始化它。 这样编译:

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        System.out.print("Do you want to enter a name? [yn] ");
        String answer = scanner.nextLine();

        final String name;
        if ("y".equals(answer)) {
            System.out.print("Please enter name: ");
            name = scanner.nextLine();
        } else if ("n".equals(answer)) {
            name = "<user refused to enter name>";
        } else {
            System.err.printf("Illegal answer \"%s\". The only legal answers are \"y\" and \"n\".", answer);
            return;
        }

        System.out.printf("The name is %s\n", name);
    }
}

等等-怎么样? 他们不是告诉我们您无法改变商品的价值最后变量?

好吧,是的,但是我们没有改变任何东西的价值最后这里的变量-我们只是在初始化它。以来名称之前从未在分配给它的任何一条路径中分配过,这些分配实际上是初始化-对于最后变量。它不会与最后String名称=null,但无需在声明中进行初始化就可以了,即使没有最后名称couldbeusedinlambdas(providedtheyappeared后thefirstassignment).

结论

请初始化您的变量-但是当您无法使用适当的变量初始化它们时,不要总是强制使用默认值。 了解您的语言如何使用未初始化的变量来表现,并选择发现错误的最佳策略。

from: https://dev.to//idanarye/dont-initialize-your-variables-40d

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值