C中指针赋值之间的差异

时间:2018-02-16 06:37:00

标签: c pointers

下面显示的指针赋值有什么区别:

int i = 5;
int *ptr = &i;

VS

int i = 5;
int *ptr;
ptr = &i;

VS

int i = 5;
int *ptr;
*ptr = &i;

我认为第1和第2是相同的但第3是不同的。所以我的问题是1,2和3之间的差异是什么?

1 个答案:

答案 0 :(得分:2)

前两个语句做同样的事情,但有理由(我会进入)更喜欢第一种风格到第二种风格。第三个是未定义的行为,这非常糟糕。它也应该是一个类型错误。

如果你的编译器没有大惊小怪地接受这个代码,你就没有打开足够的警告进行编译。它应该注意到你正在取消引用一个未初始化的指针,并且你正在指定一个指向int而没有强制转换的指针。在gcc或clang上,您可以添加标记-Wall -Wextra -Wpedantic -Wconversion-std=c11或您要定位的任何内容。

在第三种情况下,陈述int i = 5;与下一步的内容大多无关。声明int *ptr;是一个未初始化的指针。然后,*ptr = &i;取消引用未初始化的指针,如果你幸运的话会立即崩溃程序,然后在那里,让你找到问题。如果你运气不好,它会把某些内存的其他部分弄得一团糟,这会造成一个你无法重现的奇怪且不可预测的错误,并且不会暗示它来自哪里。

防御性编码的一个好习惯是将尽可能多的变量写为静态单一赋值。也就是说,只要有可能,您将变量声明并初始化为const,并且永远不会再次修改它们。在这种情况下,那将是:

const int i = 5;
const int * const ptr = &i;

这也消除了一大类错误,我使用较新的变量值,我打算使用较旧的变量值,反之亦然。并且编译器无法为我捕获这些。如果iptr只有一个含义,那么当我重构时,不可能混淆它在任何给定时间具有的含义。至于效率,它是一种洗涤。如果我正在根据一个值进行计算,我要么会再次需要原始值,要么我不会。如果我这样做,覆盖它就是一个bug。如果不这样做,依赖性分析应该确定编译器不需要再保持值。

你不能总是这样做:一个非常常见的例子是循环计数器。在这些情况下,一个体面的习惯(虽然你可以找到我不虔诚地遵循我自己的建议的例子)是始终将所有内容初始化为0NULL或其他一些明显无效的值。这样,如果你在真正初始化之前不小心使用了一个,那么至少结果是错误的和可重复的。所以:

int i = 5;
int *ptr = NULL;
*ptr = &i; /* FIXME: Undefined behavior! */

在您将要定位的大多数操作系统上,这至少会在发生逻辑错误的位置立即崩溃您的程序,从而使其易于查找。 (因为在过去,我的C和C ++答案引起了一两个人的注意,他们想要指出语言标准不能保证这个,我同意,它不可移植。但是我发现它在实践中很有用。)否则,如果您在调试器中单步执行,查找错误,那么变量在使用时未初始化是非常明显的,而如果它只是包含堆栈中的垃圾,可能不是。

另一方面,如果您的编译器可以检测到正在使用的未初始化变量,这种习惯可能会阻止它这样做。