关于指针大小的混淆

时间:2017-11-29 13:57:05

标签: c windows pointers 64-bit

这可能是一个菜鸟问题..但这真让我感到困惑..

以下是一些示例代码

void main() {

    int a = 300;
    char *ptr = &a;
    int *intptr = &a;

    printf("%d\n %d\n", *ptr, *intptr);

}

输出:
44个
300

根据我的理解,为什么解除引用*ptr打印44是因为char指针是一个字节,因此它只从int的地址读取8位...

但是这question:What is the size of a pointer?个州Regardless of what data type they are pointing to, they have fixed size

我错过了一些东西。为什么解除引用字符指针打印44,如果指针大小相同?

7 个答案:

答案 0 :(得分:5)

首先,您的代码不是有效的C,它还会调用未定义的行为,因此任何事情都可能发生。如果您的编译器没有向您显示任何诊断消息,则需要将其卸载并改为工作。您有以下错误:

  • void main()仅适用于专门允许这种形式的主要版本的独立实现。
  • char *ptr = &a;不是C中有效的赋值形式。指针不兼容(1)
  • 使用%d格式说明符打印字符会调用未定义的行为。

修复这些错误后,我们得到:

#include <stdio.h>

int main (void)
{
  int a = 300;
  char *ptr = (char*)&a;
  int *intptr = &a;

  printf("%d\n %d\n", (int)*ptr, *intptr);
}

这些都不会打印指针的大小。您打印int a中的第一个字节的内容,您将其转换为char,其中可能会签名也可能不会签名,并且可能会给出错误的结果(此部分取决于endianess)。然后打印整个int a的内容。

您似乎想要的是打印指针本身的大小:

printf("%zu %zu\n", sizeof ptr, sizeof intptr);

(1) C11 6.5.16.1强调我的:

  

<强>约束
  以下其中一项应持有:
  / - /
   - 左操作数具有原子,限定或非限定指针类型,(考虑   左值操作数在左值转换后的类型)两个操作数都是   指向 兼容类型的限定或非限定版本以及指向的类型   左边的所有限定符都是右边所指示的类型;

答案 1 :(得分:5)

对象指针(例如指向函数之外的任何指针)在您可能遇到的大多数系统上通常具有相同的大小,但是不能保证这一点。话虽如此,即使指针可能大小相同,但它们指向的类型也不是。

例如,在64位Windows系统上,指针的大小通常为8个字节。在您的示例中,您有char *int *,它们很可能都是8个字节。这里的区别在于,取消引用char *将读取/写入1个字节,而取消引用int *将读取/写入4个字节(假设int为32位)。

假设小端字节排序,a在内存中看起来像这样:

  ------------------
a | 44 | 1 | 0 | 0 |
  ------------------

ptrintptr都包含a的地址。解除引用类型为ptr的{​​{1}}时,它只会查看第一个字节。相反,当解除引用类型char *的{​​{1}}时,它会查看所有4个字节。

答案 2 :(得分:3)

在本声明中

printf("%d\n %d\n", *ptr, *intptr);

输出的不是指针本身,而是它们指向的数据。

例如,300可以表示为256 + 44。因此44可以存储在一个字节中,而256可以存储在另一个字节中。此表达式*ptr给出了存储在指针ptr指向的字节中的值44。

另一方面,指针intptr指向类型int的整个对象,表达式*intptr给出值300。

如果要输出存储在指针中的地址,则应编写

printf("%p\n %p\n", ( void * )ptr, ( void * )intptr);

输出你可以编写的指针大小

printf("%zu\n %zu\n", sizeof( ptr ), sizeof( intptr ));

考虑到根据C标准,没有参数的函数main应声明为

int main( void )

同样在此声明中,您应该使用显式转换

char *ptr = ( char * )&a;

这是一个示范程序

#include <stdio.h>

int main(void) 
{
    int a = 300;
    char *ptr = ( char * )&a;
    int *intptr = &a;

    printf( "*ptr = %d, *intptr = %d\n", *ptr, *intptr );
    printf( "ptr = %p, intptr = %p\n", ( void * )ptr, ( void * )intptr );
    printf( "sizeof( ptr ) = %zu, sizeof( intptr ) = %zu\n", 
        sizeof( ptr ), sizeof( intptr ) );

    return 0;
}

它的输出可能看起来像

*ptr = 44, *intptr = 300
ptr = 0x7ffe5972613c, intptr = 0x7ffe5972613c
sizeof( ptr ) = 8, sizeof( intptr ) = 8

答案 3 :(得分:2)

如果将对象的指针强制转换为与对象不同的类型,则引入未定义的行为,然后通过此指针访问它。

语句int a = 300引入了int类型的对象,char* ptr = &a引入了指向char类型对象的指针,但让它指向{{1}类型的对象}}。这本身不是问题,但是通过int取消引用此指针是未定义的行为。这与*ptr没有关系 - 像printf这样的语句也是UB。但是,像char x = *ptr这样的语句就可以了,因为ptr会被转换回它所指向的对象的原始类型。

关于指针的大小:指针的大小(即,存储指针指向的存储器地址所需的值)是固定大小;它通常在32位系统上为4个字节,在64位系统上为8个字节。表示内存地址所需的大小与驻留在指针所指向的特定内存地址的对象的大小无关。

以下示例演示了此行为:

int x = *((int *)ptr)

输出:

int main() {

    int a = 300;
    void *ptr = &a;
    int *intptr = &a;

    printf("%d %d\n", *((int*)ptr), *intptr);   
}

答案 4 :(得分:1)

实际上,在一个小端架构上,LSB首先出现在内存中,因此存储300,内存看起来像

44 , 1 , 0 , 0  (for a size 4 int)

printf("%d\n %d\n", *ptr, *intptr);

不打印指针大小(可能是相同的),它打印解除引用的指针,一个字节的值,一个char,然后是int值。

打印指针大小

printf ("%zu\n%zu\n", sizeof(ptr), sizeof(intptr));

答案 5 :(得分:0)

<强> 代码

#include <stdio.h>
#include <stdint.h>

unsigned int
trans(unsigned char c){
  if ('0' <=c && c <= '9') return c - '0';
  if ('A' <=c && c <= 'F') return c - 'A' + 0x0A;
  if ('a' <=c && c <= 'f') return c - 'a' + 0x0A;
  return 0;
}

uint16_t
hex_to_uint16(const char* s) {
  char *p = (char*) s;
  uint16_t v = 0;
  while (*p) {
    if (p > s) v = v << 4;
    v += trans(*p);
    p++;
  }
  return v;
}

int main (void)
{

  int n = 1;
    // little endian if true
    if(*(char *)&n == 1) {printf("little\n");}

  int a = 300;
  char *ptr =  (char*)&a;
  int *intptr = &a;

    printf("charSize:%zu intSize:%zu\n", sizeof (char), sizeof (int));
    printf("PcharSize:%zu PintSize:%zu\n", sizeof (ptr), sizeof (intptr));


  //printf("Hex: %x, Decimal: %d\n", 0x2c, (int)0x2c ); 

  //printf("int: %d\n", hex_to_uint16("2c"));
    //printf("300H: %x\n", (a));


    printf("PcharAddr: %p\nPintAddr: %p\n", ptr, intptr);


  printf("int: %d\n", *intptr);


  printf("LSB  |     |     |  MSB|\n");
  printf("  0  |  1  |  2  |  3  |\n");
  printf("------------------------\n");
  printf("Dec\n");
  printf(" %d     %d     %d     %d\n", *(ptr), *(ptr+1), *(ptr+2), *(ptr+3));
  printf("Hex\n");
  printf(" %x     %x     %x     %x\n", *(ptr), *(ptr+1), *(ptr+2), *(ptr+3));



    printf("To Big Endian:\n%.2x%x%x%x\n", (uint8_t)*(ptr+3), (uint8_t)*(ptr+2), (uint8_t)*(ptr+1), (uint8_t)*(ptr));

  ///https://stackoverflow.com/questions/19275955/convert-little-endian-to-big-endian
    /*
     * chux aswer
    uint32_t num = 300;
    uint8_t b[4];

    b[0] = (uint8_t) (num >>  0u);
    b[1] = (uint8_t) (num >>  8u);
    b[2] = (uint8_t) (num >> 16u);
    b[3] = (uint8_t) (num >> 24u);

  printf("%x%x%x%x\n", b[3], b[2], b[1], b[0]);

  */

    return 0;
}

编译,运行

gcc -Wall -Wextra te.c -o te && ./te

答案 6 :(得分:-1)

不,不,不是

  

根据我的理解,为什么解除引用* ptr print 44是因为char指针是一个字节,因此它只从int的地址读取8位...

char是一个字节,而不是char指针。如果你想打印指针,你应该

printf("%p\n %p\n", ptr, intptr);