在C语言中,我们可以定义不同类型的指针,例如:
int *p
char *p
它们都具有相同的大小4个字节或8个字节。所有这些都可以用作printf的参数("%s",p),那么编译器如何区分它们呢?答案 0 :(得分:2)
你是对的。所有指针都具有相同的大小,并且它们具有相同的数据类型(内存地址)。所以,你的问题是合法的。
编译器需要知道指针指向的数据类型,以便能够找到如何处理它。例如,数组基本上是指向(最好)分配的存储区域的指针。因此,a[1]
是*(a+1)
的简写。但是数组的下一个元素从哪里开始呢?除非你的指针有一个类型,否则编译器无法知道这一点。例如,如果你告诉他指向int
(4个字节),例如a = 0x100
,他会知道a+1
是0x104
,因为那是下一个元素的地址。
此外,了解指针指向的数据类型对于了解如何取消引用指针(解释数据)至关重要。
答案 1 :(得分: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;
}
重要:使用-Wall
(gcc -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个指针都被初始化为相同的值,但同样的操作会产生完全不同的结果。
答案 2 :(得分:1)
把它想象成生活空间的不同。每个住所都有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
*/
答案 3 :(得分:-2)
我认为指针只存储了变量的地址。所以所有指针都具有相同的大小(4字节或8字节),因为地址总线是32位或64位。你可以像这样声明指针
void * p = null;
int* p2 = (int*)p;
float* p3 = (float*)p;
p
,p2
,p3
指向同一地址。我们在声明指针时使用的数据类型告诉编译如何处理该地址中的数据。
例如,*p2
将被处理为4字节整数,*p3
将被处理为8字节浮点数。