14

这取自C,并以此为基础。假设我们有一个 32 位指针

char* charPointer;

它指向内存中包含一些数据的某个位置。它知道这个指针的增量是 1 个字节,等等。另一方面,

int* intPointer;

还指向内存中的某个位置,如果我们增加它,它知道如果我们向它添加 1,它应该增加 4 个字节。

问题是,我们如何能够使用这些指针来寻址完整的 32 位可寻址空间 (2^32) - 4 GB,如果它们显然包含一些允许它们彼此分开的信息,例如char*int*,所以这给我们留下的不是 32 个字节,而是更少。

当我输入这个问题时,我开始思考,也许这都是语法糖,真的是编译器吗?也许原始指针只是 32 位并且它不关心类型?是这样吗?

4

6 回答 6

18

您可能会对编译时间和运行时间感到困惑。

在编译期间,gcc(或任何 C 编译器)知道指针的类型,特别是知道该指针变量指向的数据的类型。因此gcc可以发出正确的机器代码。因此,变量的增量int *(在具有 32 位的 32 位机器上int)被转换为 4(字节)的增量,而char*变量的增量被转换为 1 的增量。

在运行时,已编译的可执行文件(它不关心或不需要gcc)仅处理机器指针,通常是字节地址(或某个字的开头)。

类型(在C程序中)在运行时是未知的。

其他一些语言(Lisp、Python、Javascript ......)需要在运行时知道类型。在最近的 C++(但不是 C)中,某些对象(具有虚函数的对象)可能具有RTTI

于 2012-11-16T11:55:46.350 回答
13

它确实是语法糖。考虑以下代码片段:

int t[2];
int a = t[1];

第二行相当于:

int a = *(t + 1); // pointer addition

它本身相当于:

int a = *(int*)((char*)t + 1 * sizeof(int)); // integer addition

在编译器检查完类型后,它会丢弃强制转换,并且仅适用于地址、长度和整数加法。

于 2012-11-16T11:55:16.007 回答
3

是的。原始指针是 32 位数据(或 16 位或 64 位,具体取决于架构),并且不包含任何其他内容。是否int *, char *,struct sockaddr_in *只是编译器的信息,知道递增时实际添加的数字是多少,以及取消引用时它将具有的类型。

于 2012-11-16T11:55:15.990 回答
3

您的假设是正确的:要查看如何处理不同类型的指针,请尝试运行此程序:

int main()
{
    char * pc = 0;
    int * pi = 0;

    printf("%p\n", pc + 1);
    printf("%p\n", pi + 1);

    return 0;
}

您会注意到,向 char* 添加 1 会将其数值增加 1,同时对 int* 增加 4(这是我机器上的 int 的大小)执行相同操作。

于 2012-11-16T11:59:18.780 回答
2

正如您最后所说的那样-C 中的类型只是一个编译时概念,它告诉编译器如何为您可以对变量执行的各种操作生成代码。

最后指针归结为它们指向的地址,一旦代码编译,语义信息就不再存在。

于 2012-11-16T11:56:11.963 回答
1

递增 int* 指针与递增 char* 不同,仅仅是因为指针变量被声明为 int*。您可以将 int* 转换为 char* ,然后它将增加 1 个字节。

所以,是的,这一切都只是语法糖。它使某些类型的数组处理更容易,并使 void* 用户感到困惑。

于 2012-11-16T11:54:51.157 回答