指针*p:假设有语句:int a=1;其中变量a的值0x1234存放在地址0x1000的内存中:
变量 | 内存地址 | 值 |
---|---|---|
a | 0x1000 | 1 |
那么当初始化一个指针int *p的时候,CPU会另开辟一片内存单元给指针变量,例如0x1002则:
变量 | 内存地址 | 值 |
---|---|---|
a | 0x1000 | 1 |
p | 0x1002 | 随机 |
当进行赋值操作:p=&a;则p中存放的就是变量a的地址*p就是p中存放地址所存储的值,内存单元变化为:
变量 | 内存地址 | 值 |
---|---|---|
a | 0x1000 | 1 |
p | 0x1002 | 0x1000 |
*p | 0x1002 | *(0x1000)=1 |
空指针
C语言里面的指针可以指向任何有效的数据,也可以不指向任何东西;这后者即所谓的NULL指针。当指向有效数据的时候,对它使用* 做dereference操作就可以取出数据来了;但是对不指向任何数据的指针来进行*操作肯定就没什么意义了,对吧?所以对不指向任何数据的指针做提领其实是个bug。
在现代的操作系统设计中,为了消除程序里面的这种bug,故意分配一些专门的不能访问的内存页,然后将NULL指针安排在里面。这样当程序员不小心在自己的程序中写下对NULL指针的提领操作后,系统运行时就会报错。在windows和linux中,这些内存页被设计者安排在0地址开始对应的那几个页上。实际上,这些页面里面的任何地址都是不能被提领的。* ((char * )0) 不可以,((char )0x12)之类的地址也不可以。很多人认为NULL指针其实就是指向0地址的指针,实际上这是不准确的。虽然很少见,但也有处理器架构将NULL指针安排在非零地址起始的页面内。所以为了程序的可移植性,在需要使用NULL指针的时候,用NULL
宏而来不用0值来初始化指针变量。
强制类型转换
*(int *)0 = 0
C语言中(int * )表示将操作对象强制类型转换为int * 类,即整形指针,这里是将0转换为int * ,所以返回值还是0,不过类型发生了强制转换,成为int * 型。你可以这样想象,有一个指向NULL的int * 指针,和这个地方的(int * )0是等价的,因为当行ansi/iso-c99标准规定NULL指针为0x00000000,即内存中的“第0号地址”。
然后我们继续看外层的* ,这个* 表示对指针指向的内容写内容,写的是一个0,所以,结果是往0x00000000中写数据0,至于写多少个字节的0,需要根据你的平台的字长,以及操作系统等因素来决定。例如:* (int * )0 = 0则是把从内存中地址值为0的内存空间开始,往下涵盖sizeof(int)内存区域,将这段内存区域写入0,比如32内核 int为四个字节,那么上述语句操作结果是将地址值是0x00000000、0x00000001、0x00000002和0x00000003这个四个字节的内存里数据初始化为0。
另外:
UCOSII中 if(OSTCBPrioTbl[prio]==(OS_TCB * )0)指的是判断OSTCBPrioTbl[prio]是否是空指针(是否没有空的TCB位置了)
函数指针:
顾名思义,函数指针就是指向函数的指针变量。
格式为:返回值类型(*指针变量名)(【形参列表】)
形参可有可无,视情况而定
例如:
#include<stdio.h>
int max(int x,int y){return (x>y? x:y);}
int main()
{
int (*ptr)(int, int);
int a, b, c;
ptr = max;
scanf("%d%d", &a, &b);
c = (*ptr)(a,b);
printf("a=%d, b=%d, max=%d", a, b, c);
return 0;
}