【问题标题】:Pointers, casting and different compilers指针、强制转换和不同的编译器
【发布时间】:2014-02-14 17:04:20
【问题描述】:

我现在正在学习 ANSI C 编程语言课程并尝试从讲师的幻灯片中运行这段代码:

#include<stdio.h>

int main()
{
    int a[5] = {10, 20, 30, 40, 50};
    double *p;

    for (p = (double*)a; p<(double*)(a+5); ((int*)p)++)
    {
        printf("%d",*((int*)p));
    }

    return 0;
}

不幸的是,它不起作用。在 MacOS、XCode、Clang 上出现错误:"Assignment to cast is illegal, lvalue casts are not supported",在 Ubuntu gcc 上出现下一个错误:"lvalue required as increment operand"

我怀疑问题是编译器,因为我们学习 ANCI C,它有自己的要求,可能会违反其他标准。

【问题讨论】:

  • 我认为编译器是对的,幻灯片是错的,((int*)p)++ 部分充其量看起来很阴暗。
  • 强制转换是一种操作。强制转换的结果不是您可以分配给的值。所以幻灯片错了。差不多就是这样。
  • 这是可怕的代码,除了如何不编写有效、可读的代码之外,它并没有真正说明任何有用的东西。我严重怀疑你讲师的能力。
  • 这张幻灯片应该展示什么?
  • 讲师是否正在教授关于混淆 C 编程的课程?做这一切有什么意义?可能有一个更好的示例来演示类型转换和取消引用。

标签: c pointers gcc casting


【解决方案1】:

关于((int*)p)++

(int *) p 的结果是一个值。这与左值不同。左值(可能)指定一个对象。例如,在int x = 3; 之后,名称x 表示我们定义的对象。我们一般可以在表达式中使用它,比如y = 2*x,然后用x作为它的值。但是我们也可以在赋值中使用它,比如x = 5,然后x用于对象。

表达式(int *) p 接受p 并将其转换为指向int 的指针。结果只是一个值。它不是左值,所以不代表可以修改的对象。

++ 运算符修改对象。所以它只能应用于左值。由于(int *) p 不是左值,所以++ 不能应用于它。

幻灯片中的代码,正如您所展示的那样,是不正确的,我不希望它在任何 C 实现中工作。 (C 确实允许实现进行许多扩展,但扩展 C 以允许此操作是不寻常的。)

关于(double*)a(int*)p

C 确实允许您将指向对象的指针转换为指向不同类型对象的指针。对此有各种规则。一个重要的问题是生成的指针必须与其指向的类型正确对齐。

对象有各种对齐要求,这意味着它们必须放置在内存中的特定地址。通常,char 对象可以有任何地址,int 对象必须是 4 字节的倍数,double 对象必须是 8 字节的倍数。这些要求因 C 实现而异。我将使用这些值进行说明。

当您将正确的double * 转换为int * 时,我们知道结果指针是四的倍数,因为它以八的倍数开始(假设上述要求)。所以这是一个安全的转换。当您将int * 转换为double * 时,它可能有错误的对齐方式。特别是,给定int 的数组a,我们知道a[0]a[1] 必须不正确地对齐double,因为如果其中一个是八字节的倍数,则另一个必须从八的倍数中减去四个字节。因此,这段代码中int *double *的转换不是C标准定义的。

它们可能适用于许多 C 实现,但您不应依赖它们。

C 规则还规定,当指向对象的指针转换回其原始类型时,结果等于原始指针。因此,如果遵守对齐规则,示例代码中的往返转换就可以了:您可以将 int * 转换为 double *,然后再转换回 int *,前提是遵守对齐要求。

如果 double * 已用于访问 double,则将违反别名规则。通常,当访问一种类型的对象时,C 不会定义行为,就好像它是另一种类型一样。有一些例外,特别是字符类型。但是,简单地来回转换指针而不使用它们来访问对象是可以的,除了对齐问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多