【问题标题】:what is the difference between different kinds of pointer in CC中不同类型的指针有什么区别
【发布时间】:2014-11-21 02:02:18
【问题描述】:

在C语言中,我们可以定义不同种类的指针,例如:

  • int *p
  • char *p 它们都具有相同大小的 4 字节或 8 字节。它们都可以作为printf("%s",p)的参数,那么编译器是如何区分它们的呢?

【问题讨论】:

  • 昨天看到的类似问题,很确定在 SO 某处有重复。
  • 还有int *p[10]
  • cdecl 在中间管上:cdecl.org
  • 将除第二个(类型为char* - 指向char 的指针)以外的任何一个传递给printf("%s", p); 都会调用UB。幸运的是,任何体面的实现都会抱怨。

标签: c pointers


【解决方案1】:

你是对的。所有指针都具有相同的大小,并且它们拥有相同的数据类型(内存地址)。所以,你的问题是合法的。

编译器需要知道指针指向的数据类型,以便能够找出如何处理它。例如,数组基本上是指向(最好)分配的内存区域的指针。所以,a[1]*(a+1) 的简写。但是数组的下一个元素从哪里开始呢?除非您的指针具有类型,否则编译器无法知道这一点。例如,如果你告诉他 a 指向int(4 个字节),例如a = 0x100,他就会知道a+10x104,因为那是下一个元素的地址。

此外,了解指针指向的数据类型对于了解如何取消引用指针(解释数据)至关重要。

【讨论】:

    【解决方案2】:

    这都是关于静态类型检查和指针算法的。 也许最好通过一个具体的例子来说明。

    考虑一下:

    #include <stdio.h>
    
    int main( int argc, char *argv[] )
    {
       char x[10];
    
       char *p0       = &x[0];  /* ok */
       int  *p1       = &x[0];  /* <- type checking, warning #1 */
       char (*p2)[10] = &x;     /* ok */
       int  (*p3)[10] = &x;     /* <- type checking, warning #2 */
    
       (void)printf( "sizeof(char): %ld\n", sizeof( char ));
       (void)printf( "sizeof(int):  %ld\n", sizeof( int ));
    
       (void)printf( "p0: %p, p0+1: %p\n", (void*)p0, (void*)( p0+1 ));
       (void)printf( "p1: %p, p1+1: %p\n", (void*)p1, (void*)( p1+1 ));
       (void)printf( "p2: %p, p2+1: %p\n", (void*)p2, (void*)( p2+1 ));
       (void)printf( "p3: %p, p3+1: %p\n", (void*)p3, (void*)( p3+1 ));
    
       return 0;
    }
    

    重要:用-Wallgcc -Wall -o test test.c)编译

    程序将编译,但您会收到两个关于指针类型不兼容的警告,这是正确的。

    % gcc -Wall -o test test.c
    test.c: In function ‘main’:
    test.c:9:21: warning: initialization from incompatible pointer type [enabled by default]
        int  *p1       = &x[0];  /* <- type checking, warning #1 */
                         ^
    test.c:11:21: warning: initialization from incompatible pointer type [enabled by default]
        int  (*p3)[10] = &x;     /* <- type checking, warning #2 */
                         ^
    

    现在运行程序:

    % ./test 
    sizeof(char): 1
    sizeof(int):  4
    p0: 0x7fff9f6dc5c0, p0+1: 0x7fff9f6dc5c1  # + 1 char
    p1: 0x7fff9f6dc5c0, p1+1: 0x7fff9f6dc5c4  # + 1 int (4 bytes here)
    p2: 0x7fff9f6dc5c0, p2+1: 0x7fff9f6dc5ca  # + 10 chars
    p3: 0x7fff9f6dc5c0, p3+1: 0x7fff9f6dc5e8  # + 10 ints (40 bytes here)
    

    在这里您可以观察到对指针运算的影响:虽然所有 4 个指针都被初始化为相同的值,但相同的操作会产生完全不同的结果。

    【讨论】:

    • 您可以通过删除您发布的大约 75% 的代码行来获得更好的答案。一个编译器的输出绰绰有余。无需提供两个。
    • 笼统的答案和纠结的答案之间的平衡并不容易找到。我正在努力提供最好的答案,并努力提高我的信噪比。我刚刚删除了多余的编译器输出,请随意编辑剩余的材料。
    【解决方案3】:

    把它想象成生活空间的不同。每个住宅有1个地址,但住宅大小不同。

    /*
     _________________________________________
    /___________Studio Apartments_____________\
    |  _   _   _   _   _   _   _   _   _   _  |
    |_|0|_|1|_|2|_|3|_|4|_|5|_|6|_|7|_|8|_|9|_|
    
     _________________________________________
    /____________2 Bed Apartments_____________\
    |  _       _       _       _       _      |
    |_|0|_____|1|_____|2|_____|3|_____|4|_____|
    
    Note: different endianness may look like:
     _________________________________________
    /____________2 Bed Apartments_____________\
    |      _       _       _       _       _  |
    |_____|0|_____|1|_____|2|_____|3|_____|4|_|
    */
    
    typedef studio char; //tell the compiler what a "studio" is like
    typedef apt2br short; //tell it what a 2 bedroom apartment is like
    
    /*Now let's build our apartments at the first available address.*/
    studio mystudios[10] = /*the letter people live here :)*/
      {'A','B','E','C','I','D','O','F','U','G'};
    
    /*We just told our contractor to build somewhere - record locations for later.*/
    studio *LetterStudios=&mystudios;
    
    /* Let's say we want to print out all of our letter people residents
     * and no one has built an apartment complex next to them, so the data
     * following our last letter person is () a '0'
     */
    
    printf("%s\n", (char *)LetterStudios);
    
    /* if nothing is built we may get "ABECIDOFUG", but since we did not add our
     * own '\0' terminator then we get whatever happens to be next.  This is a
     * buffer overrun ... we may get "ABECIDOFUG<lots of garbage>" or worse
     */
    

    让我们看看 2 字节的情况:

    /* So let's give ourselves some boundaries and loop through it, but this
     * case has 1 byte elements, so lets look at a 2 byte array
     */
    
    apt2br myapts[5] = /*people with magic number names live here :)*/
      {0xFEFE, 0xB0B, 0xDADE, 0xABE, 0xBABE};
    apt2br *MagicApartments=&myapts;
    
    /* to get the number of units in an apartment complex we can always divide the
     * size of the whole complex by the size of a single unit
     */
    for(int i=0;i<sizeof(myapts)/sizeof(myapts[0]);i++){
      printf("%X\n",myapts[i]);
    }
    /* Note: the sizeof() is on the array, not the pointer to it, all pointers are
     * going to be either 4 (32 bit) or 8 (64 bit), which would have printed only
     * 2 or 4 elements because the size of the "apartment" at the address is 2
     */
    
     /* you can still use the pointer though */
     int apartments = sizeof(myapts)/sizeof(myapts[0]);
     while (apartments--)
       printf("%X\n",*MagicApartments++);
     /* This just gives the inspector a number of apartments to inspect and points
      * him to the first apartment to inspect.  Each time he prints off the data
      * pointed to, moves his pointer to the next apartment and decrements the number
      * of apartments to inspect till he gets to 0.
      * Note however that now his MagicApartments pointer is past the last apartment
      * If he were to inspect again (without resetting MagicApartments=&myapts;) ,
      * he would end up inspecting the apartment next door... another buffer overrun
      */
    

    【讨论】:

      【解决方案4】:

      我认为指针只是存储了变量的地址。所以所有指针都有相同的大小(4 字节或 8 字节),因为地址总线是 32 位或 64 位。你可以像这样声明指针

      void * p = null;  
      int* p2 = (int*)p;
      float* p3 = (float*)p;
      

      pp2p3 指向同一个地址。我们在声明指针时使用的数据类型告诉编译器如何处理该地址中的数据。

      例如,*p2 将被处理为 4 字节整数,*p3 将被处理为 8 字节浮点数。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-08-20
        • 2012-09-13
        • 2016-06-03
        • 1970-01-01
        • 2012-01-13
        • 2021-09-21
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多