为
初学者服
务
。
这
是我的帖子的宗旨。我也是个初学者
(
强调
了无数遍了
),
我以我的理解把初学者
觉
得
难
懂的
东
西用浅
显
的
语
言写出来。由于小学
时语
文没学好
,
所以竭尽全力也未必能达到
这
个目的。尽力而
为
吧。
指
针
是
c
和
c++
中的
难
点和重点。我只精通
dos
下的
basic
。
c
语
言的其它各
种
特性
,
在
basic
中都有
类
似的
东
西。只有指
针
,
是
baisc
所不具
备
的。指
针
是
c
的灵魂。
我不想重
复
大多数
书
上
说
得很清楚的
东
西
,
我只是把我看
过
的
书
中
说
得不清楚或没有
说
,
而我又
觉
得我理解得有点道理的
东
西写出来。我的目的是
:
通
过
写
这
些
东
西
,
把我
脑
袋中
关
于
c
的模糊的知
识
清晰化。
第一章。指
针
的概念
指
针
是一个特殊的
变
量
,
它里面存
储
的数
值
被解
释
成
为
内存里的一个地址。
要搞清一个指
针
需要搞清指
针
的四方面的内容
:
指
针
的
类
型
,
指
针
所指向的
类
型
,
指
针
的
值
或者叫指
针
所指向的内存区
,
还
有指
针
本身所占据的内存区。
让
我
们
分
别说
明。
先声明几个指
针
放着做例子
:
例一
:
(1)int *ptr;
(2)char *ptr;
(3)int **ptr;
(4)int (*ptr)[3];
(5)int *(*ptr)[4];
1
。
指
针
的
类
型。
从
语
法的角度看
,
你只要把指
针
声明
语
句里的指
针
名字去掉
,
剩下的部分就是
这
个指
针
的
类
型。
这
是指
针
本身所具有的
类
型。
让
我
们
看看例一中各个指
针
的
类
型
:
(1)int *ptr; //
指
针
的
类
型是
int *
(2)char *ptr; //
指
针
的
类
型是
char *
(3)int **ptr; //
指
针
的
类
型是
int **
(4)int (*ptr)[3]; //
指
针
的
类
型
是
int(*)[3]
(5)int *(*ptr)[4]; //
指
针
的
类
型是
int *(*)[4]
怎
么样
?
找出指
针
的
类
型的方法是不是很
简单
?
2
。指
针
所指向的
类
型。
当你通
过
指
针
来
访问
指
针
所指向的内存区
时
,
指
针
所指向的
类
型决定了
编译
器将把那片内存区里的内容当做什
么
来看待。
从
语
法上看
,
你只
须
把指
针
声明
语
句中的指
针
名字和名字左
边
的指
针
声明符
*
去掉
,
剩下的就是指
针
所指向的
类
型。例如
:
(1)int *ptr; //
指
针
所指向的
类
型是
int
(2)char *ptr; //
指
针
所指向的的
类
型
是
char
(3)int **ptr; //
指
针
所指向的的
类
型是
int *
(4)int (*ptr)[3]; //
指
针
所指向的的
类
型是
int()[3]
(5)int *(*ptr)[4]; //
指
针
所指向的的
类
型是
int *()[4]
在指
针
的算
术
运算中
,
指
针
所指向的
类
型有很大的作用。
指
针
的
类
型
(
即指
针
本身的
类
型
)
和指
针
所指向的
类
型是两个概念。当你
对
C
越来越熟悉
时
,
你会
发现
,
把与指
针搅
和在一起的
"
类
型
"
这
个概念分成
"
指
针
的
类
型
"
和
"
指
针
所指向的
类
型
"
两个概念
,
是精通指
针
的
关键
点之一。我看
了不少
书
,
发现
有些写得差的
书
中
,
就把指
针
的
这
两个概念
搅
在一起了
,
所以看起
书
来前后矛盾
,
越看越糊涂。
3
。
指
针
的
值
,
或者叫指
针
所指向的内存区或地址。
指
针
的
值
是指
针
本身存
储
的数
值
,
这
个
值
将被
编译
器当作一个地址
,
而不是一个一般的数
值
。在
32
位程序里
,
所有
类
型的指
针
的
值
都是一个
32
位整数
,
因
为
32
位程序里内存地址全都是
32
位
长
。
指
针
所指向的内存区就是从指
针
的
值
所代表的那个内存地址
开
始
,
长
度
为
sizeof(
指
针
所指向的
类
型
)
的一片内存区。以后
,
我
们说
一个指
针
的
值
是
XX,
就相当于
说该
指
针
指向了以
XX
为
首地址的一片内存区域;我
们说
一个指
针
指向了某
块
内存区域
,
就相当于
说该
指
针
的
值
是
这块
内存区域的首地址。
指
针
所指向的内存区和指
针
所指向的
类
型是两个完全不同的概念。在例一中
,
指
针
所指向的
类
型已
经
有了
,
但由于指
针还
未初始化
,
所以它所指向的内存区是不存在的
,
或者
说
是无意
义
的。
以后
,
每
遇到一个指
针
,
都
应该问问
:
这
个指
针
的
类
型是什
么
?
指
针
指向的
类
型是什
么
?
该
指
针
指向了哪里
?
4
。
指
针
本身所占据的内存区。
指
针
本身占了多大的内存
?
你只要用函数
sizeof(
指
针
的
类
型
)
测
一下就知道了。在
32
位平台里
,
指
针
本身占据了
4
个字
节
的
长
度。
指
针
本身占据的内存
这
个概念在判断一个指
针
表达式是否是左
值时
很有用。
第二章。指
针
的算
术
运算
指
针
可以加上或减去一个整数。指
针
的
这种
运算的意
义
和通常的数
值
的加减运算的意
义
是不一
样
的。
例如
:
例二
:
1
。
char a[20];
2
。
int *ptr=a;
3
。
ptr++;
在上例中
,
指
针
ptr
的
类
型
是
int*,
它指向的
类
型是
int,
它被初始化
为
指向整形
变
量
a
。
接下来的第
3
句中
,
指
针
ptr
被加了
1,
编译
器是
这样处
理的
:
它把指
针
ptr
的
值
加上了
sizeof(int),
在
32
位程序中
,
是被加上了
4
。
由于地址是用字
节
做
单
位的
,
故
ptr
所指向的地址由原来的
变
量
a
的地址向高地址方向增加了
4
个字
节
。
由于
char
类
型的
长
度是一个字
节
,
所以
,
原来
ptr
是指向数
组
a
的第
0
号
单
元
开
始的四
个字
节
,
此
时
指向了数
组
a
中从第
4
号
单
元
开
始的四个字
节
。
我
们
可以用一个指
针
和一个循
环
来遍
历
一个数
组
,
看例子
:
例三
:
int array[20];
int *ptr=array; //
此
处
略去
为
整型数
组赋值
的代
码
。
for(i=0;i<20;i++)
{
(*ptr)++;
ptr++
;
}
这
个例子将整型数
组
中各个
单
元的
值
加
1
。由于
每
次循
环
都将指
针
ptr
加
1,
所以
每
次循
环
都能
访问
数
组
的下一个
单
元。再看例子
:
例四
:
1
。
char a[20];
2
。
int *ptr=a;
3
。
ptr+=5;
在
这
个例子中
,ptr
被加上了
5,
编译
器是
这样处
理的
:
将指
针
ptr
的
值
加上
5
乘
sizeof(int),
在
32
位程序中就是加上了
5
乘
4=20
。由于地址的
单
位是字
节
,
故
现
在的
ptr
所指向的地址比起加
5
后的
ptr
所指向的地址来
说
,
向高地址方向移
动
了
20
个字
节
。在
这
个例子中
,
没加
5
前的
ptr
指向数
组
a
的第
0
号
单
元
开
始的四个字
节
,
加
5
后
,ptr
已
经
指向了数
组
a
的合法范
围
之外了。
虽
然
这种
情况在
应
用上会出
问题
,
但在
语
法上却是可以的。
这
也体
现
出了指
针
的灵活性。
如果上例中
,ptr
是被减去
5,
那
么处
理
过
程大同小异
,
只不
过
ptr
的
值
是被减去
5
乘
sizeof(int),
新的
ptr
指向的地址将比原来的
ptr
所指向的地址向低地址方向移
动
了
20
个字
节
。
总结
一下
,
一个指
针
ptrold
加上一个整数
n
后
,
结
果是一个新的指
针
ptrnew,ptrnew
的
类
型和
ptrold
的
类
型相同
,ptrnew
所指向的
类
型和
ptrold
所指向的
类
型也相同。
ptrnew
的
值
将比
ptrold
的
值
增加了
n
乘
sizeof(ptrold
所指向的
类
型
)
个字
节
。
就是
说
,ptrnew
所指向的内存区将比
ptrold
所指向的内存区向高地址方向移
动
了
n
乘
sizeof(ptrold
所指向的
类
型
)
个字
节
。
一个指
针
ptrold
减去一个整数
n
后
,
结
果是一个新的指
针
ptrnew,ptrnew
的
类
型和
ptrold
的
类
型相同
,ptrnew
所指向的
类
型和
ptrold
所指向的
类
型也相同。
ptrnew
的
值
将比
ptrold
的
值
减少了
n
乘
sizeof(ptrold
所指向的
类
型
)
个字
节
,
就是
说
,ptrnew
所指向的内存区将比
ptrold
所指向的内存区向低地址方向移
动
了
n
乘
sizeof(ptrold
所指向的
类
型
)
个字
节
。
第三章。运算符
&
和
*
这
里
&
是取地址运算符
,*
是
...
书
上叫做
"
间
接运算符
"
。
&a
的运算
结
果是一个指
针
,
指
针
的
类
型是
a
的
类
型加个
*,
指
针
所指向的
类
型是
a
的
类
型
,
指
针
所指向的地址嘛
,
那就是
a
的地址。
*p
的运算
结
果就五花八
门
了。
总
之
*p
的
结
果是
p
所指向的
东
西
,
这
个
东
西有
这
些特点
:
它的
类
型是
p
指向的
类
型
,
它所占用的地址是
p
所指向的地址。
例五
:
int a=12;
int b;
int *p;
int **ptr;
p=&//&a
的
结
果是一个指
针
,
类
型是
int*,
指向的
类
型是
int,
指向的地址是
a
的地址。
*p=24;//*p
的
结
果
,
在
这
里它的
类
型是
int,
它所占用的地址是
p
所指向的地址
,
显
然
,*p
就是
变
量
a
。
ptr=&//&p
的
结
果是个指
针
,
该
指
针
的
类
型是
p
的
类
型加个
*,
在
这
里是
int**
。
该
指
针
所指向的
类
型是
p
的
类
型
,
这
里是
int*
。
该
指
针
所指向的地址就是指
针
p
自己的地址。
*ptr=&b//*ptr
是个指
针
,&b
的
结
果也是个指
针
,
且
这
两个指
针
的
类
型和所指向的
类
型是一
样
的
,
所以用
&b
来
给
*ptr
赋值
就是毫无
问题
的了。
**ptr=34;//*ptr
的
结
果是
ptr
所指向的
东
西
,
在
这
里是一个指
针
,
对这
个指
针
再做一次
*
运算
,
结
果就是一个
int
类
型的
变
量。
第四章。指
针
表达式。
一个表达式的最后
结
果如果是一个指
针
,
那
么这
个表达式就叫指
针
表达式。
下面是一些指
针
表达式的
例子
:
例六
:
int a,b;
int array[10];
int *pa;
pa=&//&a
是一个指
针
表达式。
int **ptr=&//&pa
也是一个指
针
表达式。
*ptr=&//*ptr
和
&b
都是指
针
表达式。
pa=array;
pa++;//
这
也是指
针
表达式。
例七
:
char *arr[20];
char **parr=arr;//
如果把
arr
看作指
针
的
话
,arr
也是指
针
表达式
char *str;
str=*parr;//*parr
是指
针
表达式
str=*(parr+1);//*(parr+1)
是指
针
表达式
str=*(parr+2);//*(parr+2)
是指
针
表达式
由于指
针
表达式的
结
果是一个指
针
,
所以指
针
表达式也具有指
针
所具有的四个要素
:
指
针
的
类
型
,
指
针
所指向的
类
型
,
指
针
指向的内存区
,
指
针
自身占据的内存。
好了
,
当一个指
针
表达式的
结
果指
针
已
经
明确地具有了指
针
自身占据的内存的
话
,
这
个指
针
表达式就是一个左
值
,
否
则
就不是一个左
值
。
在例七中
,&a
不是一个左
值
,
因
为
它
还
没有占据明确的内存。
*ptr
是一个左
值
,
因
为
*ptr
这
个指
针
已
经
占据了内存
,
其
实
*ptr
就是指
针
pa,
既然
pa
已
经
在内存中有了自己的位置
,
那
么
*ptr
当然也有了自己的位置。
第五章。数
组
和指
针
的
关
系
如果
对
声明数
组
的
语
句不太明白的
话
,
请
参
阅
我前段
时间贴
出的文章
<<
如何理解
c
和
c++
的
复杂类
型声明
>>
。
数
组
的数
组
名其
实
可以看作一个指
针
?
聪吕
?
例八
:
int array[10]={0,1,2,3,4,5,6,7,8,9},value;
value=array[0];//
也可写成
:value=*array;
value=array[3];//
也可写成
:value=*(array+3);
value=array[4];//
也可写成
:value=*(array+4);
上例中
,
一般而言数
组
名
array
代表数
组
本身
,
类
型是
int [10],
但如果把
array
看做指
针
的
话
,
它指向数
组
的第
0
个
单
元
,
类
型是
int *,
所指向的
类
型是数
组单
元的
类
型即
int
。因此
*array
等于
0
就一点也不奇怪了。同理
,array+3
是一个指向数
组
第
3
个
单
元的指
针
,
所以
*(array+3)
等于
3
。其它依此
类
推。
例九
:
char *str[3]={ "Hello,this is a sample!","Hi,good morning.","Hello world" };
char s[80]
;
strcpy(s,str[0]);//
也可写成
strcpy(s,*str);
strcpy(s,str[1]);//
也可写成
strcpy(s,*(str+1));
strcpy(s,str[2]);//
也可写成
strcpy(s,*(str+2));
上例中
,str
是一个三
单
元的数
组
,
该
数
组
的
每
个
单
元都是一个指
针
,
这
些指
针
各指向一个字符串。把指
针
数
组
名
str
当作一个指
针
的
话
,
它指向数
组
的第
0
号
单
元
,
它的
类
型是
char**,
它指向的
类
型是
char *
。
*str
也是一个指
针
,
它的
类
型是
char*,
它所指向的
类
型是
char,
它指向的地址是字符串
"Hello,this is a sample!"
的第一个字符的地址
,
即
'H'
的地址。
str+1
也是一个指
针
,
它指向数
组
的第
1
号
单
元
,
它的
类
型是
char**,
它指向的
类
型是
char *
。
*(str+1)
也是一个指
针
,
它的
类
型是
char*,
它所指向的
类
型是
char,
它指向
"Hi,good morning."
的第一个字符
'H',
等等。
下面
总结
一下数
组
的数
组
名的
问题
。声
明了一个数
组
TYPE array[n],
则
数
组
名称
array
就有了两重含
义
:
第一
,
它代表整个数
组
,
它的
类
型是
TYPE [n]
;第二
,
它是一个指
针
,
该
指
针
的
类
型是
TYPE*,
该
指
针
指向的
类
型是
TYPE,
也就是数
组单
元的
类
型
,
该
指
针
指向的内存区就是数
组
第
0
号
单
元
,
该
指
针
自己占有
单
独的内存区
,
注意它和数
组
第
0
号
单
元占据的内存区是不同的。
该
指
针
的
值
是不能修改的
,
即
类
似
array++
的表达式是
错误
的。
在不同的表达式中数
组
名
array
可以扮演不同的角色。
在表达式
sizeof(array)
中
,
数
组
名
array
代表数
组
本身
,
故
这时
sizeof
函数
测
出的是整个数
组
的大小。
在表达式
*array
中
,array
扮演的是指
针
,
因此
这
个表达式的
结
果就是数
组
第
0
号
单
元的
值
。
sizeof(*array)
测
出的是数
组单
元的大小。
表达式
array+n(
其中
n=0,1,2,....
。
)
中
,array
扮演的是指
针
,
故
array+n
的
结
果是一个指
针
,
它的
类
型是
TYPE*,
它指向的
类
型是
TYPE,
它指向数
组
第
n
号
单
元。故
sizeof(array+n)
测
出的是指
针类
型的大小。
例十
:
int array[10];
int (*ptr)[10];
ptr=&
上例中
ptr
是一个指
针
,
它的
类
型是
int (*)[10],
他指向的
类
型是
int [10],
我
们
用整个数
组
的首地址来初始化它。在
语
句
ptr=&array
中
,array
代表数
组
本身。
本
节
中提到了函数
sizeof(),
那
么
我来
问
一
问
,sizeof(
指
针
名称
)
测
出的究竟是指
针
自身
类
型的大小呢
还
是指
针
所指向的
类
型的大小
?
答案是前者。例如
:
int (*ptr)[10];
则
在
32
位程序中
,
有
:
sizeof(int(*)[10])==4
sizeof(int [10])==40
sizeof(ptr)==4
实际
上
,sizeof(
对
象
)
测
出的都是
对
象自身的
类
型的大小
,
而不是
别
的什
么类
型的大小。
第六章。指
针
和
结
构
类
型的
关
系
可以声明一个指向
结
构
类
型
对
象的指
针
。
例十一
:
struct MyStruct
{ int a;
int b;
int c;
}
MyStruct ss={20,30,40};//
声明了
结
构
对
象
ss,
并把
ss
的三个成
员
初始化
为
20,30
和
40
。
MyStruct *ptr=&//
声明了一个指向
结
构
对
象
ss
的指
针
。
它的
类
型是
MyStruct*,
它指向的
类
型是
MyStruct
。
int *pstr=(int*)&//
声明了一个指向
结
构
对
象
ss
的指
针
。但是它的
类
型和它指向的
类
型和
ptr
是不同的。
请问
怎
样
通
过
指
针
ptr
来
访问
ss
的三个成
员变
量
?
答案
: ptr->a; ptr->b; ptr->c;
又
请问
怎
样
通
过
指
针
pstr
来
访问
ss
的三个成
员变
量
?
答案
: *pstr
;
//
访问
了
ss
的成
员
a
。
*(pstr+1);//
访问
了
ss
的成
员
b
。
*(pstr+2)//
访问
了
ss
的成
员
c
。
呵呵
,
虽
然我在我的
MSVC++6.0
上
调
式
过
上述代
码
,
但是要知道
,
这样
使用
pstr
来
访问结
构成
员
是不正
规
的
,
为
了
说
明
为
什
么
不正
规
,
让
我
们
看看怎
样
通
过
指
针
来
访问
数
组
的各个
单
元
:
例十二
:
int array[3]={35,56,37};
int *pa=array;
通
过
指
针
pa
访问
数
组
array
的三个
单
元的方法是
:
*pa;//
访问
了第
0
号
单
元
*(pa+1);//
访问
了第
1
号
单
元
*(pa+2);//
访问
了第
2
号
单
元
从格式上看倒是与通
过
指
针访问结
构成
员
的不正
规
方法的格式一
样
。
所有的
C/C++
编译
器在排列数
组
的
单
元
时
,
总
是把各个数
组单
元存放在
连续
的存
储
区里
,
单
元和
单
元之
间
没有空隙。但在存放
结
构
对
象的各个成
员时
,
在某
种编译环
境下
,
可能会需要字
对齐
或双字
对齐
或者是
别
的什
么对齐
,
需要在相
邻
两个成
员
之
间
加若干
"
填充字
节
",
这
就
导
致各个成
员
之
间
可能会有若干个字
节
的空隙。
所以
,
在例十二中
,
即使
*pstr
访问
到了
结
构
对
象
ss
的第一个成
员变
量
a,
也不能保
证
*(pstr+1)
就一定能
访问
到
结
构成
员
b
。因
为
成
员
a
和成
员
b
之
间
可能会有若干填充字
节
,
说
不定
*(pstr+1)
就正好
访问
到了
这
些填充字
节
呢。
这
也
证
明了指
针
的灵活性。要是你的目
的就是想看看各个
结
构成
员
之
间
到底有没有填充字
节
,
嘿
,
这
倒是个不
错
的方法。
通
过
指
针访问结
构成
员
的正确方法
应该
是象例十二中使用指
针
ptr
的方法。
第七章。指
针
和函数的
关
系
可以把一个指
针
声明成
为
一个指向函数的指
针
。
int fun1(char*,int);
int (*pfun1)(char*,int);
pfun1=fun1;
int a=(*pfun1)("abcdefg",7);//
通
过
函数指
针
饔煤
?
可以把指
针
作
为
函数的形参。在函数
调
用
语
句中
,
可以用指
针
表达式来作
为
实
参。
例十三
:
int fun(char*);
int a;
char str[]="abcdefghijklmn";
a=fun(str);
int fun(char*s)
{ int num=0;
for(int i=0;i
{ num+=*s;s++; }
return num;
)
这
个例子中的函数
fun
统计
一个字符串中各个字符的
ASCII
码值
之和。前面
说
了
,
数
组
的名字也是一个指
针
。在函数
调
用中
,
当把
str
作
为实
参
传递给
形参
s
后
,
实际
是把
str
的
值传递给
了
s,s
所指向的地址就和
str
所指向的地址一致
,
但是
str
和
s
各自占用各自的存
储
空
间
。在函数体内
对
s
进
行自加
1
运算
,
并不意味着同
时对
str
进
行了自加
1
运算。
第八章。指
针类
型
转换
当我
们
初始化一个指
针
或
给
一个指
针赋值时
,
赋值
号的左
边
是一个指
针
,
赋值
号的右
边
是一个指
针
表达式。在我
们
前面所
举
的例子中
,
绝
大多数情况下
,
指
针
的
类
型和指
针
表达式的
类
型是一
样
的
,
指
针
所指向的
类
型和指
针
表达式所指向的
类
型是一
样
的。
例十四
:
1
。
float f=12.3;
2
。
float *fptr=&
3
。
int *p;
在上面的例子中
,
假如我
们
想
让
指
针
p
指向
实
数
f,
应该
怎
么
搞
?
是用下面的
语
句
吗
?
p=&
不
对
。因
为
指
针
p
的
类
型是
int*,
它指向的
类
型是
int
。表达式
&f
的
结
果是一个指
针
,
指
针
的
类
型是
float*,
它指向的
类
型是
float
。两者不一致
,
直接
赋值
的方法是不行的。至少在我的
MSVC++6.0
上
,
对
指
针
的
赋值语
句要求
赋值
号两
边
的
类
型一致
,
所指向的
类
型也一致
,
其它的
编译
器上我没
试过
,
大家可以
试试
。
为
了
实现
我
们
的目的
,
需要
进
行
"
强
制
类
型
转换
":
p=(int*)&
如果有一个指
针
p,
我
们
需要把它的
类
型和所指向的
类
型改
为
TYEP*
和
TYPE,
那
么语
法格式是
: (TYPE*)p
;
这样强
制
类
型
转换
的
结
果是一个新指
针
,
该
新指
针
的
类
型是
TYPE*,
它指向的
类
型是
TYPE,
它指向的地址就是原指
针
指向的地址。而原来的指
针
p
的一切属性都没有被修改。
一个函数如果使用了指
针
作
为
形参
,
那
么
在函数
调
用
语
句的
实
参和形参的
结
合
过
程中
,
也会
发
生指
针类
型的
转换
。
例十五
:
void fun(char*);
int a=125,b;
fun((char*)&a);
void fun(char*s)
{ char c;
c=*(s+3);*(s+3)=*(s+0);*(s+0)=c;
c=*(s+2);*(s+2)=*(s+1);*(s+1)=c;
}
}
注意
这
是一个
32
位程序
,
故
int
类
型占了四个字
节
,char
类
型占一个字
节
。函数
fun
的作用是把一个整数的四个字
节
的
顺
序来个
颠
倒。注意到了
吗
?
在函数
调
用
语
句中
,
实
参
&a
的
结
果是一个指
针
,
它的
类
型是
int *,
它指向的
类
型是
int
。形参
这
个指
针
的
类
型是
char*,
它指向的
类
型是
char
。
这样
,
在
实
参和形参的
结
合
过
程中
,
我
们
必
须进
行一次从
int*
类
型到
char*
类
型的
转换
。
结
合
这
个例子
,
我
们
可以
这样
来想象
编译
器
进
行
转换
的
过
程
:
编译
器先构造一个
临时
指
针
char*temp,
然后
执
行
temp=(char*)&a,
最后再把
temp
的
值传递给
s
。所以最后的
结
果是
:s
的
类
型是
char*,
它指向的
类
型是
char,
它指向的地址就是
a
的首地址。
我
们
已
经
知道
,
指
针
的
值
就是指
针
指向的地址
,
在
32
位程序中
,
指
针
的
值
其
实
是一个
32
位整数。那可不可以把一个整数当作指
针
的
值
直接
赋给
指
针
呢
?
就象下面的
语
句
:
unsigned int a;
TYPE *ptr;//TYPE
是
int,char
或
结
构
类
型等等
类
型。
a=20345686;
ptr=20345686;//
我
们
的目的是要使指
针
ptr
指向地址
20345686(
十
进
制
)
ptr=a;//
我
们
的目的是要使指
针
ptr
指向地址
20345686(
十
进
制
)
编译
一下吧。
结
果
发现
后面两条
语
句全是
错
的。那
么
我
们
的目的就不能达到了
吗
?
不
,
还
有
办
法
:
unsigned int a;
TYPE *ptr;//TYPE
是
int,char
或
结
构
类
型等等
类
型。
a=
某个数
,
这
个数必
须
代表一个合法的地址;
ptr=(TYPE*)a
;
//
呵呵
,
这
就可以了。
严
格
说
来
这
里的
(TYPE*)
和指
针类
型
转换
中的
(TYPE*)
还
不一
样
。
这
里的
(TYPE*)
的意思是把无符号整数
a
的
值
当作一个地址来看待。
上面
强调
了
a
的
值
必
须
代表一个合法的地址
,
否
则
的
话
,
在你使用
ptr
的
时
候
,
就会出
现
非法操作
错误
。
想想能不能反
过
来
,
把指
针
指向的地址即指
针
的
值
当作一个整数取出来。完全可以。下面的例子演示了把一个指
针
的
值
当作一个整数取出来
,
然后再把
这
个整数当作一个地址
赋给
一个指
针
:
例十六
:
int a=123,b;
int *ptr=&
char *str;
b=(int)ptr;//
把指
针
ptr
的
值
当作一个整数取出来。
str=(char*)b;//
把
这
个整数的
值
当作一个地址
赋给
指
针
str
。
好了
,
现
在我
们
已
经
知道了
,
可以把指
针
的
值
当作一个整数取出来
,
也可以把一个整数
值
当作地址
赋给
一个指
针
。
第九章。指
针
的安全
问题
看下面的例子
:
例十七
:
char s='a';
int *ptr;
ptr=(int*)&
*ptr=1298
;
指
针
ptr
是一个
int*
类
型的指
针
,
它指向的
类
型是
int
。
它指向的地址就是
s
的首地址。
在
32
位程序中
,s
占一个字
节
,int
类
型占四个字
节
。最后一
条
语
句不但改
变
了
s
所占的一个字
节
,
还
把和
s
相
临
的高地址方向的三个字
节
也改
变
了。
这
三个字
节
是干什
么
的
?
只有
编译
程序知道
,
而写程序的人是不太可能知道的。也
许这
三个字
节
里存
储
了非常重要的数据
,
也
许这
三个字
节
里正好是程序的一条代
码
,
而由于你
对
指
针
的
马
虎
应
用
,
这
三个字
节
的
值
被改
变
了
,
这
会造成崩
溃
性的
错误
。
让
我
们
再来看一例
:
例十八
:
1
。
char a;
2
。
int *ptr=&
3
。
ptr++;
4
。
*ptr=115;
该
例子完全可以通
过编译
,
并能
执
行。但是看到没有
?
第
3
句
对
指
针
ptr
进
行自加
1
运算后
,ptr
指向了和整形
变
量
a
相
邻
的高地址方向的一
块
存
储
区。
这块
存
储
区里是什
么
?
我
们
不知道。有可能它是一个非常重要的数据
,
甚至可能是一条代
码
。而第
4
句竟然往
这
片存
储
区里写入一个数据
,
这
是
严
重的
错误
。所以在使用指
针时
,
程序
员
心里必
须
非常清楚
:
我的指
针
究竟指向了哪里。
在用指
针访问
数
组
的
时
候
,
也要注意不要超出数
组
的低端和高端界限
,
否
则
也会造成
类
似的
错误
。
在指
针
的
强
制
类
型
转换
:ptr1=(TYPE*)ptr2
中
,
如果
sizeof(ptr2
的
类
型
)
大于
sizeof(ptr1
的
类
型
),
那
么
在使用指
针
ptr1
来
访问
ptr2
所指向的存
储
区
时
是安全的。如果
sizeof(ptr2
的
类
型
)
小于
sizeof(ptr1
的
类
型
),
那
么
在使用指
针
ptr1
来
访问
ptr2
所指向的存
储
区
时
是不安全的。
至于
为
什
么
,
读
者
结
合例十七来想一想
,
应该
会明白的
。