C中不同类型指针的区别是什么?

时间:2021-06-25 13:30:24

In C language,we can define different kinds of pointer such as:

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

  • int *p
  • int * p
  • char *p all of them have the same size 4 bytes or 8 bytes. All of them can be used as a parameter for the printf("%s",p),so how the complier distinguish between them?
  • 所有的字符都有相同大小的4字节或8字节。它们都可以作为printf(“%s”,p)的参数,那么编译器如何区分它们呢?

4 个解决方案

#1


2  

You are right. All the pointers have the same size and they hold the same data type (a memory address). So, your question is legit.

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

The compiler needs to know about the data type the pointer points to in order to be able to find out how to deal with it. For example, an array is basically a pointer to an (preferably) allocated memory region. So, a[1] is a shorthand for *(a+1). But where does the next element of the array start? The compiler has no way to know this, unless your pointer has a type. For example, if you tell him that a points to int (4 bytes) and, for example, a = 0x100, he will know that a+1 is 0x104, because that is the address of the next element.

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

Also, knowing the type of the data the pointer points to is essential for knowing how to dereference the pointer (interpreting the data).

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

#2


2  

It's all about static type checking and pointer arithmetics. Perhaps it's best illustrated by looking at a concrete example.

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

Consider this:

考虑一下:

#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;
}

Important: compile with -Wall (gcc -Wall -o test test.c)

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

The program will compile, but you'll get two warnings about incompatible pointer types, and rightly so.

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

% 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 */
                     ^

Now run the program:

现在运行程序:

% ./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)

Here you can observe the impact on pointer arithmetics: although all 4 pointers were initialized to the same value, the same operation yields completely different results.

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

#3


1  

Think of it like the different in living spaces. Each residence has 1 address, but the residences are different sizes.

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

/*
 _________________________________________
/___________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
 */

Lets look at the 2 byte case:

让我们看一下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


-2  

I think pointer just stored the address of variable. So All pointers have same size (4 bytes or 8 bytes) because the address bus is 32bits or 64bits. You can declare pointers like this

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

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

p, p2, p3 point to the same address. The data type we used when declare a pointer tells the compile how to process the data in this address.

p, p2, p3指向同一个地址。我们在声明指针时使用的数据类型告诉编译如何处理这个地址中的数据。

For example, *p2 would be processed as a 4bytes integer and *p3 as a 8bytes float.

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

#1


2  

You are right. All the pointers have the same size and they hold the same data type (a memory address). So, your question is legit.

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

The compiler needs to know about the data type the pointer points to in order to be able to find out how to deal with it. For example, an array is basically a pointer to an (preferably) allocated memory region. So, a[1] is a shorthand for *(a+1). But where does the next element of the array start? The compiler has no way to know this, unless your pointer has a type. For example, if you tell him that a points to int (4 bytes) and, for example, a = 0x100, he will know that a+1 is 0x104, because that is the address of the next element.

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

Also, knowing the type of the data the pointer points to is essential for knowing how to dereference the pointer (interpreting the data).

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

#2


2  

It's all about static type checking and pointer arithmetics. Perhaps it's best illustrated by looking at a concrete example.

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

Consider this:

考虑一下:

#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;
}

Important: compile with -Wall (gcc -Wall -o test test.c)

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

The program will compile, but you'll get two warnings about incompatible pointer types, and rightly so.

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

% 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 */
                     ^

Now run the program:

现在运行程序:

% ./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)

Here you can observe the impact on pointer arithmetics: although all 4 pointers were initialized to the same value, the same operation yields completely different results.

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

#3


1  

Think of it like the different in living spaces. Each residence has 1 address, but the residences are different sizes.

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

/*
 _________________________________________
/___________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
 */

Lets look at the 2 byte case:

让我们看一下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


-2  

I think pointer just stored the address of variable. So All pointers have same size (4 bytes or 8 bytes) because the address bus is 32bits or 64bits. You can declare pointers like this

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

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

p, p2, p3 point to the same address. The data type we used when declare a pointer tells the compile how to process the data in this address.

p, p2, p3指向同一个地址。我们在声明指针时使用的数据类型告诉编译如何处理这个地址中的数据。

For example, *p2 would be processed as a 4bytes integer and *p3 as a 8bytes float.

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