【问题标题】:Declaring type of pointers?声明指针的类型?
【发布时间】:2013-12-05 18:24:52
【问题描述】:

我刚刚读到我们需要在用 C(或 C++)声明指针时给出指针的类型,即:

int *point ;

据我所知,指针存储变量的地址,无论类型是什么,地址都占用相同的内存量。 那么,为什么我们需要声明它的类型呢?

【问题讨论】:

  • C 确实有 'void *' 这是一个指向任何东西的指针。
  • 首先,您的理解完全错误;不要求每个地址都具有相同的大小。其次,尝试命名所有可以对指针执行的基本操作。这不是一个很长的清单,所以它不应该花你很长时间。你需要知道其中多少个被指向变量的类型?

标签: c++ c pointers


【解决方案1】:

类型安全。如果你不知道p 应该指向什么,那么就没有什么可以防止像

这样的类别错误了
*p = "Nonsense";
int i = *p;

静态类型检查是防止此类错误的非常强大的工具。

C 和 C++ 还支持 指针算法,它仅在目标类型的大小已知时才有效。

无论我是什么类型,地址都占用相同数量的内存

对于当今的流行平台来说确实如此。但有些平台并非如此。例如,指向多字节字的指针可能小于指向单字节的指针,因为它不需要表示字节在字中的偏移量。

【讨论】:

  • 假设目标字符集是 ASCII 或 Unicode,并且 p 的类型与 char* 的赋值兼容,上述代码将完全合法地将 i 设置为 78。解引用指针的结果是与指针类型相同的左值。
  • @supercat:确实,在某些假设下,该代码可能是有效的。类型检查有助于检查这些假设,并确保代码有效。
  • @supercat:当然(在真正的 C 和 C++ 语言中,也许我们正在考虑的弱类型假设语言中)不会写字符 @ 987654326@ 在任何情况下都指向p 指向的字节。字符串文字将转换为指针,*p 处的任何内容都将从该指针值分配。
  • 糟糕,在第一个 p 之前我没有注意到 *。如果p**char,则第一个语句有效,而第二个则无效。如果它只是单间接的,那么第一个语句将是无效的(除非前面的标记是一种类型,使得该语句变成类似于char *p = "Nonsense";)。
【解决方案2】:

因为:

  1. 不同类型的地址不需要具有相同的大小。该标准明确规定(C 2011 standard(在线草案),6.2.5/28)。
  2. 类型安全:这允许编译器检测您何时提供指向函数的不兼容指针,或在赋值中。这反过来又可以防止您弄乱函数的参数顺序的丑陋情况。
  3. 当指针被取消引用时,编译器需要知道类型。
  4. 要进行指针运算,需要知道所指向对象的大小,从而知道它的类型。

最后两点不适用于void 指针,这就是为什么它们不能被取消引用并且不能对它们进行指针运算的原因。该标准规定void 指针必须足够大以容纳任何类型的指针(函数指针除外,它们完全不同),并且可以在没有强制转换的情况下对void 指针进行分配(至少在 C 中,在 C++ 中总是需要强制转换)

【讨论】:

  • @John Bode 感谢您的编辑,我看到在 C99 标准中这是在 §6.2.5.27 下。
【解决方案3】:

一个原因是指针运算。你不能做p+1,除非你知道p指向的元素的大小——这就是p指向的类型的大小。如果您在void *p 上尝试p+1,您可能会得到一个错误的答案(这与在char * 上进行的操作相同,但也许您不希望这样做;它被@987654330 捕获@ 作为警告,-pedantic-errors 作为错误)。

另一个原因是类型安全。如果函数接收int * 作为参数,则不能将指针传递给char(字符串)。您会收到警告(-Werror / -pedantic-errors 出现错误)。考虑这个(虚拟)代码:

void test(int *x)
{
}

int main()
{
    char *x = "xyz";
    test(x);
    return 0;
}

编译(使用gcc (GCC) 4.8.2 20131017 (Red Hat 4.8.2-1))给出:

1.c: In function ‘main’:
1.c:8:2: warning: passing argument 1 of ‘test’ from incompatible pointer type [enabled by default]
  test(x);
  ^
1.c:1:6: note: expected ‘int *’ but argument is of type ‘char *’
 void test(int *x)
      ^

【讨论】:

  • 应该,但这是一个警告 (pointer to incompatible type)
  • Clang error, GCC error。当然是 4.8.1,但为什么会改变呢?
  • 哦,除了这是一件好事,因为我相当确定这是标准的错误。根据我的经验,迂腐的警告是关于更好的标准一致性。
  • 除了对 void 指针的算术运算与 char 指针的行为相同之外,一切都很好。根本不是错误。
  • @Guido this answer 表示 void * 上的指针运算char * 相同且非法(在 C 和 C++ 中)。
【解决方案4】:

那么,为什么我们需要声明它的类型呢?

你想知道指针的类型,这样你就可以static type checking

我们还需要知道类型以便pointer arithmetic 工作,例如,当我们索引不同大小类型的数组(相当于指针算法)时,指针将是按类型相关的金额进行调整。如果我们查看 C99 标准部分草案 6.5.6加法运算符 说(强调我的):

对于加法,要么两个操作数都应具有算术类型,要么一个操作数应为 指向对象类型的指针 [...]

所以指针需要是一个对象类型,意思是不完整或无效。

你也说过:

无论类型是什么,地址都占用相同数量的内存。那么,为什么我们需要声明它的类型呢?

C++ 中并非总是如此,指向成员函数的指针的大小可能会根据类类型而改变,其中一篇很好的文章是 Pointers to member functions are very strange animals

此外,我们可以看到 C99 草案标准部分部分 6.2.5 Types 段落 27 说:

[...] 指向其他类型的指针不需要具有相同的表示或对齐要求。

和草案 C++ 标准部分 3.9.2复合类型 段落 3 说:

[...] 指针类型的值表示是实现定义的。指向布局兼容类型的 cv-qualified 和 cv-unqualified 版本 (3.9.3) 的指针应具有相同的值表示和对齐要求 (3.11)。 [...]

不要求指针具有相同的表示,除非在特定情况下。

【讨论】:

    【解决方案5】:

    您需要按照标准要求指定类型。 此外,这样当您尝试执行指针运算(如加法或减法)时不会出现问题。

    【讨论】:

      【解决方案6】:

      虽然处理器通常有不同的指令用于“从地址加载字节”、“从地址加载 16 位半字”和“从地址加载 32 位字”,“存储”同样如此在操作中,C 使用与加载任何其他大小值相同的语法从地址加载字节。鉴于声明:

      int n = *p;
      

      编译器可以生成代码,从 p 中的地址加载一个字节、半字或字并将其存储到 n 中;如果 p 是 *float,它可能会生成更复杂的代码序列来将浮点值加载到 c 中,截断它,转换为 int,并将转换后的值存储到 n 中。在不知道 p 的类型的情况下,编译器无法知道哪个操作是合适的。

      同样,p++ 语句可以将p 中的地址增加一、二、四或其他数字。地址增加的数量取决于 p 的声明类型。如果编译器不知道p的类型,就不知道如何调整地址。

      可以声明一个指针而不指定它所指向的事物的类型。这种指针的类型是void*。但是,必须先将 void* 转换为真正的指针类型,然后再对其进行任何有用的操作; void* 的主要用途在于,如果将指针转换为void*,它可能会通过不知道指针实际类型的代码作为void* 传递。如果指针最终被赋予确实知道其类型的代码,并且该代码将指针转换回该类型,则结果将与已转换为void*的指针相同。

      必须处理指向它一无所知的事物的指针的代码通常可以有效地使用 void* 来实现此类目的,但确实知道指针指向的事物的代码通常应该声明正确类型的指针。

      【讨论】:

        【解决方案7】:

        指针的类型在解引用和指针运算时发挥作用。 例如

        int x=10;     //Lets suppose the address of x=100
        int *ptr=&x;   //ptr has value 100
        printf("The value of x is %d", *ptr);
        ptr++;  // ptr has value 104(if int is 4bytes)
        

        在上面的示例中,指针类型是 int,因此编译器将从内存地址 100 开始查找存储在接下来的 4 个字节(如果 int 是 4 个字节)中的值。所以指针的类型告诉编译器有多少个取消引用时它应该寻找的字节。如果指针类型不存在,编译器如何知道取消引用时要查看多少字节。当我们执行 ptr++ 时,指针的类型告诉我们 ptr 应该增加多少。这里 ptr 增加了 4。

        char c='a';   //Lets suppose the address of c = 200
        char* ptr=&c;   //ptr has value 200
        ptr++;   //ptr has value 201(char assumed to be 1 byte) 
        

        指针类型告诉ptr增加1个字节。

        【讨论】:

          【解决方案8】:

          以便它可以执行算术和其他操作。 考虑以下两个例子:

          int* p; /* let the address of the memory location p pointing to be 1000*/
          p++;
          printf("%u",p); /* prints 1004 since it is an integer pointer*/
          
          
          char *p; /* let the address of the memory location p pointing to be 1000*/
          p++;
          printf("%u",p); /* prints 1001 since it is an char pointer*/
          

          希望对你有帮助!

          【讨论】:

            【解决方案9】:

            我们实际上并不需要(见下文)声明类型,但我们应该。指针存储有关对象位置的信息,而类型定义了它在内存中占用的空间。

            在各种情况下都需要存储在指定内存中的对象的大小 - 数组创建、分配、复制内存,最后 - 使用 new 创建对象。

            但是,如果您想(出于任何原因)隐藏类型,您仍然可以定义 void 指针:

            void* dontKnowWhatTypeIsHere;
            

            空指针被认为是通用指针。它可以指向任何对象,当我们想将它与类型一起使用时,我们只需使用reinterpret_cast

            【讨论】:

              【解决方案10】:

              上述说法很多,但 apan 是完全正确的。 现在你的问题 为什么要定义指针类型? 指针的第一个定义 一个指针,可以保存另一个变量的地址。 上述定义是片面的。确切的定义是 指针是一个可以保存变量地址的变量,如果我们取消引用(获取值)它,它将返回该地址上的当前值。如果指针在取消引用时无法返回值,则它不是指针。 您可以尝试即使在 gcc 编译器中,一个简单的变量也可以保存另一个变量的地址,但在取消引用时它会给您错误。 现在大小 无论数据类型如何,指针的大小始终等于该特定编译器上的整数大小。所以 gcc 编译器中指针的大小是 4bytes(整数大小),而在 turboc 中它的大小是 2bytes(整数大小)。 现在的问题是为什么等于整数的大小。 任何变量的地址是什么,它可能是 int、char、float 等,地址始终是整数,并且整数存储在 int 中。这就是为什么指针的大小等于 int 的大小,因为它还存储始终是纯整数数据的地址。 那么任何其他数据类型指针的 int 和 char 之间有什么区别。在检索时,您的编译器将根据您的数据类型获取字节数,否则您将得到错误或不是错误,但对您来说是一些不可预测的结果,但对我来说不是。 相同的规则适用于递增和递减指针,它总是根据指针数据类型递增和递减。 指针的大小不取决于数据类型,因此您的链接列表出现的原因是因为如果您尝试在同一个变量中声明结构,那么您将收到编译时错误,因为您的编译器的大小不是结构在其完整声明之前但允许相同结构的自引用指针为什么?唯一的答案是因为指针的大小不取决于数据类型的大小。如果您有任何疑问,请问我。 谢谢 asif aftab

              【讨论】:

              • “现在无论数据类型如何,指针的大小始终等于特定编译器上整数的大小。” 。虽然在许多情况下可能如此,但它既不是标准规定的,也不是总是正确的。想想 64 位平台,其中 int 可能是 4 个字节,但指针是 8 个字节。或使用非线性寻址方案的嵌入式/旧平台。
              • "确切的定义是指针是一个可以保存变量地址的变量" 不,指针是一个,而不是一个变量.
              • Kninnug 是绝对正确的;不要求任何两个不同类型的指针具有相同的大小,也不需要它们具有与 int 相同的大小。在分段架构上,“近”和“远”指针的大小也可能有所不同。
              • "这就是为什么指针的大小等于 int 的大小,因为它还存储了始终是纯整数数据的地址" 不,绝对不是!没有任何要求将指针实现为整数!也许你不记得在 16 位时代为 x86 机器编写代码是什么感觉,可能有 16 位整数,但 32 位指针由 16 位偏移量和 16 位段组成,只给你 20 位有效寻址.
              • 感谢 Eric 纠正我的回答。
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2014-12-01
              • 1970-01-01
              • 1970-01-01
              • 2011-09-14
              • 1970-01-01
              • 1970-01-01
              • 2018-02-27
              相关资源
              最近更新 更多