【问题标题】:"Address of" (&) an array / address of being ignored be gcc?“地址”(&)被忽略的数组/地址是gcc吗?
【发布时间】:2011-02-23 01:09:14
【问题描述】:

我是一门编程入门课程的助教,有同学犯了这种错误:

char name[20];
scanf("%s",&name);

这并不奇怪,因为他们正在学习......令人惊讶的是,除了 gcc 警告之外,代码还有效(至少这部分)。我一直在努力理解,我写了以下代码:

void foo(int *v1, int *v2) {
  if (v1 == v2)
    printf("Both pointers are the same\n");
  else
    printf("They are not the same\n");
}

int main() {
  int test[50];
  foo(&test, test);
  if (&test == test)
    printf("Both pointers are the same\n");
  else
    printf("They are not the same\n");
}

编译执行:

$ gcc test.c -g
test.c: In function ‘main’:
test.c:12: warning: passing argument 1 of ‘foo’ from incompatible pointer type
test.c:13: warning: comparison of distinct pointer types lacks a cast
$ ./a.out 
Both pointers are the same
Both pointers are the same

谁能解释为什么它们没有不同?

我怀疑这是因为我无法获得数组的地址(因为我不能拥有& &x),但在这种情况下,代码不应编译。

编辑:我知道数组本身与第一个元素的地址相同,但这与这个问题无关,我认为。例如:

int main() {
  int a[50];
  int * p = a;
  printf("%d %d %d\n", p == a, p == &a[0], &p[0] == a);
  printf("%d %d %d\n", p == &a, &p == a, &p == &a);
}

打印:

$ ./a.out 
1 1 1
1 0 0

我不明白为什么第二行以1开头。

【问题讨论】:

  • 您的编辑表明您认为有一些指针与数组一起存储。但事实并非如此。数组仅由个元素组成。
  • 我知道数组只是元素,所以我认为它不应该编译。

标签: c gcc pointers addressof


【解决方案1】:

其实它们是不同的,至少它们没有相同的类型。

但是在C语言中,数组的地址和数组的地址是一样的 数组中的第一个元素,这就是“它们没有区别”的原因,基本上,它们指向同一个东西。

【讨论】:

  • "数组的地址和数组中第一个元素的地址一样"我一直都知道数组本身(而不是不是的地址array) 与第一个元素的地址相同,即test&test[0] 完全相同。我刚刚签入了 K&R,这就是那里写的内容...我将编辑我的问题以添加此内容。
  • @WhirlWind:不,它依赖于系统。将 address-of 运算符应用于数组具有明确定义的语义 - 它必须生成数组本身的地址。数组被定义为基类型对象的连续序列,因此数组的地址和第一个对象的地址在同一个地方(但类型不同)。
【解决方案2】:

我相信这是一个 gcc 优化。想一想。

  • &test 指向test 的地址
  • test 指向test&test[0] 的第一个元素
  • [0]* 相同(大部分)

因此,根据 &test 可能test 不同,但 gcc 对此进行了优化,因为此时没有额外的间接级别。

【讨论】:

  • 这不是 gcc 优化 - 这是 C 标准要求的行为。
  • 看来@Earlz 和我一样误会了 &test 的意思。
  • @dba 哇,我是。对此接受的答案完全清除了它
【解决方案3】:

如果你定义一个数组像

char name[20];

name 可隐式转换为char*,但&name 的类型为char (*)[20](指向20 个字符数组的指针)。地址是一样的。

查看(&name + 1)的地址。它与&name 的区别在于sizeof(char [20])

【讨论】:

  • 谢谢,您让 James McNellis 的回答更容易理解。
  • 最后一句话实际上使它成为恕我直言的最佳答案。如果您了解 C 中指针上的 + 1 如何将指针地址增加类型的大小(sizeof(*ptr)),那么这句话基本上解释了一切:)
【解决方案4】:

数组的名称,在大多数情况下,计算为其初始元素的地址。这两个例外是当它是 sizeof 的操作数或一元 & 时。

一元 & 给出其参数的地址。数组的地址与其初始元素的地址相同,因此(void*)&array == (void*)array 将始终为真。

array,当转换为指向其初始元素的指针时,其类型为T *&array 的类型是T (*)[n],其中n 是数组中元素的数量。因此,

int* p = array;        // Works; p is a pointer to array[0]
int* q = &array;       // Doesn't work; &array has type int (*)[10]
int (*r)[10] = &array; // Works; r is a pointer to array

【讨论】:

  • 谢谢,我只知道sizeof的例外。
【解决方案5】:

在您的示例中,数组 test 是一个包含 50 个 ints 的块。所以它看起来像这样:

| int | int | ... | int |

当您将一元 & 运算符应用于数组时,您将获得数组的地址。就像你把它应用到其他东西上一样,真的。所以&test是一个指向那个50块的指针ints

(&test) -----------> | int | int | ... | int |

指向 50 个整数数组的指针的类型为 int (*)[50] - 这是 &test 的类型。

当您在任何不是sizeof 或一元-& 运算符的操作数的地方使用名称test 时,它会被评估为指向其第一个元素的指针。因此,您传递给 foo()test 评估为指向 test[0] 元素的指针:

(test) -----------------\
                        v
(&test) -----------> | int | int | ... | int |

您可以看到它们都指向同一个地址 - 尽管&test 指向整个数组,而test 指向数组的第一个元素(仅显示在不同的 类型这些值具有)。

【讨论】:

  • 我喜欢 ascii 艺术 :) 现在有了这张图片,很容易解释为什么将 1 添加到 &test 会超出数组,而将 1 添加到 test 只会超出数组第一个元素。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-12-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-22
  • 2018-12-16
相关资源
最近更新 更多