【问题标题】:Questions about "Address-of operator" (&) and arrays in c and c++关于 c 和 c++ 中“地址运算符”(&) 和数组的问题
【发布时间】:2015-12-30 20:30:33
【问题描述】:

我发现在 c 中编译与在 c++ 中编译时有一个我不完全理解的差异...假设我声明了一个大小为 5 的 char 数组:

char my_array[5];

根据我的理解,“my_array”实际上是一个字符指针,它允许我这样做:

char* a = my_array;

现在,我知道当我声明我的数组时,我只分配了 5 个字节(其中每个值一个),这意味着“&my_array”不应该存在,因为没有为指向 my_array 的指针分配内存。为了证明这一点,我这样做了:

char* b = &my_array; // compiler error in c++, not in c
char** c = &my_array; // compiler error in c++, not in c

我不明白其中的区别,为什么 c 会允许这样做?不过,假设我想将我的 char[5] 转换为 char*,不使用 a 的方法,我发现我也可以这样做:

char* c = (char*)&my_array; // works both in c and c++

那个我真的不明白。我没有确定“&my_array”不存在吗?更令人震惊的是,这两行返回的值完全相同:

// a and c have the same value (both in c and c++)
char* a = my_array;
char* c = (char*)&my_array;

接下来,假设我想在运行时执行这 5 个字节,所以我声明了一个新类型:

// pFunc is a pointer to function returning void
typedef void (*pFunc)();

然后我尝试声明一个指向函数的指针,该函数将执行我的字节数组:

pFunc pFunc1 = (pFunc)&my_array; // works, but I don't really understand why
pFunc pFunc2 = (pFunc)my_array;  // compiler error in c++, not in c

突然间,不仅 &my_array 编译没有问题,而且它也是使它在 c++ 中工作的唯一方法(据我所知)。有人介意向我解释发生了什么吗?完整代码如下:

#include <stdio.h>

typedef void (*pFunc)();

int main()
{
    char my_array[5];

    char* a = my_array;
    char* b = &my_array; // compiler error in c++, not in c
    char* c = (char*)&my_array;
    char** d = &my_array; // compiler error in c++, not in c
    char** e = (char**)&my_array;

    pFunc pFunc1 = (pFunc)&my_array; // works, but I don't really understand why
    pFunc pFunc2 = (pFunc)my_array;  // compiler error in c++, not in c

    printf("char*:\n");
    printf("0x%X\n", a);
    printf("0x%X\n", b);
    printf("0x%X\n", c);
    printf("0x%X\n", d);
    printf("0x%X\n", e);
    printf("\n");

    while(1);

    return 1;
}

【问题讨论】:

  • ""my_array" 实际上是一个字符指针" 这是不正确的
  • 如果你用 Java 编译器编译 C 代码,你会得到什么?请阅读 C-tag 信息页面。 C 和 C++ 是不同的语言。对“C C++ 差异”的简单搜索将回答您的问题。并且有充分的理由将数组称为“数组”而不是“指针”(反之亦然)。
  • @Slava 好吧,这取决于上下文,因为它可以衰减为 char*,但也可以只是 char[5]
  • @cad:是的,它衰减(即自动转换为指向第一个元素的指针)。但是 is 是否与指针不同。
  • 需要使用演员表应该清楚地表明你做错了什么。除非您完全了解所有含义,否则不要使用强制转换!你的编译器警告类型不匹配是有原因的。并启用警告;你的 C 编译器也应该抱怨。

标签: c++ c arrays pointers


【解决方案1】:

C++ 中的数组不是指针。当适当类型的指针使用数组的名称初始化时(例如当数组传递给适当指针类型的函数参数时),它将衰减为指针,但它本身不是指针。

当您声明char my_array[5] 时,您正在创建一个包含5 个chars 的数组;没有创建指针,但存在char[5],可以被其他指针指向。

如果你做char* a = my_array;my_array会衰减成一个指针;创建一个新的char* 指向my_array 的第一个元素。
char* b = &amp;my_array; 失败,因为my_array 不是char,因此&amp;my_array 不是char*char** d = &amp;my_array; 失败,因为 my_array 不是 char*,因此 &amp;my_array 不是 char**&amp;my_arraychar (*)[5](指向 5 个 chars 的数组的指针)。这些在 C 中“起作用”是因为 C 只要求编译器对不相关的指针类型之间的隐式转换发出警告,但 C++ 根本不允许这些隐式转换;它们会导致编译错误。您需要在 C++ 中进行显式强制转换,但即便如此,如果您取消引用结果指针,行为也是未定义的。

最后,pFunc pFunc1 = (pFunc)&amp;my_array; 在类似地表示函数指针和对象指针的平台上“工作”(大多数现代系统),但它不需要在所有平台上工作。同样,使用结果指针将调用未定义的行为。
pFunc pFunc2 = (pFunc)my_array; 实际上使用 GCC 也可以编译,但在 MSVC 下编译失败。我实际上不确定哪个编译器的行为是正确的,但无论哪种方式,结果都不太可能有用。

【讨论】:

  • “因为 C 允许在不相关的指针类型之间进行隐式转换” - 它不允许。这是一个需要诊断的约束违规。 GCC 只是碰巧发出警告而不是错误。
  • @T.C.它需要诊断,但 GCC 仍然“允许”让代码编译。
  • @PCLuddite 按照这个逻辑,C“允许”阳光下的一切。
  • 更新到需要在 C 中发出警告。
  • 迄今为止的最佳答案。但是,在函数指针上,您有点不正确:从/向其他类型的函数指针的转换不能保证以任何方式工作,因为函数指针可以是与数据指针完全不同的野兽。我记得在 PPC Mac 上,一个函数指针实际上由 两个 指针组成,一个指向全局数据表 (TOC),另一个指向代码。您不能将 char* 强制转换为这样的函数指针。我猜,gcc 允许这样做是因为 linux 人需要并使用它(看看 libdl)。
【解决方案2】:

当一个数组被定义后,就会在内存中为其元素分配空间。整个空间(在您的情况下为 5 字节)分配给数组。因此,空间被分配给数组元素以及数组本身。

需要注意的是,数组不是指针,反之亦然。 my_array 是一个数组而不是一个指针。在大多数情况下,数组转换为指向其第一个元素的指针,但这并不意味着数组是指针。

数组my_array的地址是char (*)[5]类型,一个指向5个chars数组的指针。它与char **pFunct 类型不兼容。分配不兼容的指针类型会导致错误。

【讨论】:

  • 回答了大部分问题,但我仍然不明白为什么“pFunc pFunc2 = (pFunc)my_array;”不行吗?
  • @MyUsername112358:正如我已经评论过你的问题:想一想“未定义”一词的含义,如 未定义的行为
  • 我认为未定义的行为是将 &my_array 转换为 char*?我不明白为什么是“char* b = &my_array;”未定义的行为,但我可以将 my_array 强制转换为函数指针的唯一方法是使用 &my_array 这种方式:“pFunc pFunc1 = (pFunc)&my_array;”,但这不起作用:pFunc pFunc2 = (pFunc)my_array;.
  • @MyUsername112358 你有没有想过你要做什么? “我可以将 my_array 转换为函数指针的唯一方法”,我的意思是,什么?为什么要将 char 数组转换为函数指针?我无法想象 任何 用例...
  • 这样我就可以在运行时执行这些数组,正如我在原始帖子中所说...我正在将代码注入正在运行的可执行文件中,我通过跳转到我的我注入的代码中有自己的函数。我将这 5 个字节存储在一个 char 数组中。我的函数做它的事情,然后在返回可执行文件之前最后执行 5 个字节。这样我就可以执行我的功能,而无需以任何方式修改目标可执行文件。不知道为什么您认为这与询问相关,但您可以继续。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-28
  • 2022-01-23
  • 1970-01-01
相关资源
最近更新 更多