C语言指针详述Word文件下载.docx
- 文档编号:4790351
- 上传时间:2023-05-04
- 格式:DOCX
- 页数:31
- 大小:30.46KB
C语言指针详述Word文件下载.docx
《C语言指针详述Word文件下载.docx》由会员分享,可在线阅读,更多相关《C语言指针详述Word文件下载.docx(31页珍藏版)》请在冰点文库上搜索。
int*()[4]
sizeof(int*(*)[4])=16
Sizeof(int*(*)[4])=4
1指针的类型
从语法的角度看,你只要把指针声明语句里的指针名字去掉,剩下的部分就是这个指针的类型。
这是指针本身所具有的类型。
让我们看看例一中各个指针的类型:
2指针所指向的类型
当你通过指针来访问指针所指向的内存区时,指针所指向的类型决定了编译器将把那片内存区里的内容当做什么来看待。
从语法上看,你只须把指针声明语句中的指针名字和名字左边的指针声明符*去掉,剩下的就是指针所指向的类型。
例如:
在指针的算术运算中,指针所指向的类型有很大的作用。
指针的类型(即指针本身的类型)和指针所指向的类型是两个概念。
当你对C越来越熟悉时,你会发现,把与指针搅和在一起的"
类型"
这个概念分成"
指针的类型"
和"
指针所指向的类型"
两个概念,是精通指针的关键点之一。
3指针的值,或者叫指针所指向的内存区或地址
指针的值是指针本身存储的数值,这个值将被编译器当作一个地址,而不是一个一般的数值。
在32位程序里,所有类型的指针的值都是一个32位整数,因为32位程序里内存地址全都是32位长。
指针所指向的内存区就是从指针的值所代表的那个内存地址开始,长度为sizeof(指针所指向的类型)的一片内存区。
以后,我们说一个指针的值是XX,就相当于说该指针指向了以XX为首地址的一片内存区域;
我们说一个指针指向了某块内存区域,就相当于说该指针的值是这块内存区域的首地址。
指针所指向的内存区和指针所指向的类型是两个完全不同的概念。
在例一中,指针所指向的类型已经有了,但由于指针还未初始化,所以它所指向的内存区是不存在的,或者说是无意义的。
以后,每遇到一个指针,都应该问问:
这个指针的类型是什么?
指针指的类型是什么?
该指针指向了哪里?
4指针本身所占据的内存区
指针本身占了多大的内存?
你只要用函数sizeof(指针的类型)测一下就知道了。
在32位平台里,指针本身占据了4个字节的长度。
指针本身占据的内存这个概念在判断一个指针表达式是否是左值时很有用。
第二章运算符和表达式
这里&
是取地址运算符,*是...书上叫做"
间接运算符"
。
&
a的运算结果是一个指针,指针的类型是a的类型加个*,指针所指向的类型是a的类型,指针所指向的地址嘛,那就是a的地址。
*p的运算结果就五花八门了。
总之*p的结果是p所指向的东西,这个东西有这些特点:
它的类型是p指向的类型,它所占用的地址是p所指向的地址。
例五:
inta=12;
intb;
int*p;
p=&
a;
//&
a的结果是一个指针,类型是int*,指向的类型是int,指向的地址是a的地址。
*p=24;
//*p的结果,在这里它的类型是int,它所占用的地址是p所指向的地址,显然,*p就是变量a。
ptr=&
p;
p的结果是个指针,该指针的类型是p的类型加个*,在这里是int**。
该指针所指向的类型是p的类型,这里是int*。
该指针所指向的地址就是指针p自己的地址。
*ptr=&
b;
//*ptr是个指针,&
b的结果也是个指针,且这两个指针的类型和所指向的类型是一样的,所以用&
b来给*ptr赋值就是毫无问题的了。
**ptr=34;
//*ptr的结果是ptr所指向的东西,在这里是一个指针,对这个指针再做一次*运算,结果就是一个int类型的变量。
一个表达式的最后结果如果是一个指针,那么这个表达式就叫指针表达式。
下面是一些指针表达式的例子:
例六:
inta,b;
intarray[10];
int*pa;
pa=&
a是一个指针表达式。
int**ptr=&
pa;
pa也是一个指针表达式。
//*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当然也有了自己的位置。
第三章指针的算术运算
指针可以加上或减去一个整数。
指针的这种运算的意义和通常的数值的加减运算的意义是不一样的。
例二:
1。
Chara[20];
2。
int*ptr=(int*)a;
...
3。
ptr++;
printf("
%c\n"
*(ptr+1));
//会打印出a[4]的值,编译器是这样处理的:
它把指针ptr的值加上了sizeof(int),
printf(“%d\n”*(ptr+1));
//打印a[4],a[5],a[6],a[7]各二进制位组成的int型值
4.ptr+=5;
//编译器是这样处理的:
将指针ptr的值加上5乘sizeof(int),
5.printf(“%d\n”*ptr);
//指针访问了a[20]之后的内存,结果未知,若是写操作,会有危险
在上例中,指针ptr的类型是int*,它指向的类型是int,它被初始化为指向整形变量a。
接下来的第3句中,指针ptr被加了1,编译器是这样处理的:
它把指针ptr的值加上了sizeof(int),在32位程序中,是被加上了4。
由于地址是用字节做单位的,故ptr所指向的地址由原来的变量a的地址向高地址方向增加了4个字节。
由于char类型的长度是一个字节,所以,原来ptr是指向数组a的第0号单元开始的四个字节,此时指向了数组a中从第4号单元开始的四个字节。
总结一下,一个指针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所指向的类型)个字节。
第四章指针类型转换
赋值运算符=成立的条件是:
1左边的值必须为可修改的左值(l-value)
2=两边的表达式结果类型必须一致
如果=两边是指针表达式,它还必须符合第三个条件
3=两边的指针表达式所指向的类型必须一致。
当我们初始化一个指针或给一个指针赋值时,赋值号的左边是一个指针,赋值号的右边是一个指针表达式。
在我们前面所举的例子中,绝大多数情况下,指针的类型和指针表达式的类型是一样的,指针所指向的类型和指针表达式所指向的类型是一样的。
例十四:
1
floatf=12.3;
2
float*fptr=&
f;
3
int*p;
在上面的例子中,假如我们想让指针p指向实数f,应该怎么搞?
是用下面的语句吗?
不对。
因为指针p的类型是int*,它指向的类型是int。
表达式&
f的结果是一个指针,指针的类型是float*,它指向的类型是float。
两者不一致,直接赋值的方法是不行的。
至少在我的MSVC++6.0上,对指针的赋值语句要求赋值号两边的类型一致,所指向的类型也一致,其它的编译器上我没试过,大家可以试试。
为了实现我们的目的,需要进行"
强制类型转换"
:
p=(int*)&
如果有一个指针p,我们需要把它的类型和所指向的类型改为TYEP*TYPE,
那么语法格式是:
(TYPE*)p;
这样强制类型转换的结果是一个新指针,该新指针的类型是TYPE*,它指向的类型是TYPE,它指向的地址就是原指针指向的地址。
而原来的指针p的一切属性都没有被修改。
一个函数如果使用了指针作为形参,那么在函数调用语句的实参和形参的结合过程中,也会发生指针类型的转换。
例十五:
voidfun(char*);
inta=125,b;
fun((char*)&
a);
...
voidfun(char*s)
{
charc;
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位整数。
那可不可以把一个整数当作指针的值直接赋给指针呢?
就象下面的语句:
unsignedinta;
TYPE*ptr;
//TYPE是int,char或结构类型等等类型。
a=20345686;
ptr=20345686;
//我们的目的是要使指针ptr指向地址20345686(十进制
)
ptr=a;
//我们的目的是要使指针ptr指向地址20345686(十进制)
编译一下吧。
结果发现后面两条语句全是错的。
那么我们的目的就不能达到了吗?
不,还有办法:
unsignedinta;
a=某个数,这个数必须代表一个合法的地址;
ptr=(TYPE*)a;
//呵呵,这就可以了。
严格说来这里的(TYPE*)和指针类型转换中的(TYPE*)还不一样。
这里的(TYPE*)的意思是把无符号整数a的值当作一个地址来看待。
上面强调了a的值必须代表一个合法的地址,否则的话,在你使用ptr的时候,就会出现非法操作错误。
unsigned
char*s_cfque=(unsignedchar*)0x800000;
想想能不能反过来,把指针指向的地址即指针的值当作一个整数取出来。
完全可以。
下面的例子演示了把一个指针的值当作一个整数取出来,然后再把这个整数当作一个地址赋给一个指针:
例十六:
inta=123,b;
int*ptr=&
b=(int)ptr;
//把指针ptr的值当作一个整数取出来。
str=(char*)b;
//把这个整数的值当作一个地址赋给指针str。
好了,现在我们已经知道了,可以把指针的值当作一个整数取出来,也可以把一个整数值当作地址赋给一个指针。
C语言指针详述
(2):
数组与指针
第六章数组和指针的关系
1.指针数组:
一个数组里存放的都是同一个类型的指针,通常我们把他叫做指针数
组。
int*a[10];
它里边放了10个int*型变量,由于它是一个数组,已经在栈区分配了1
0个(int*)的空
间,也就是32位机上是40个byte,每个空间都可以存放一个int型变量的地址,这个
时候你可以为这
个数组的每一个元素初始化,或者单独做个循环去初始化它。
一个指针数组中的指针可以为任何同一类型,例如可以为结构指针、函数指针等
structa
inta;
intb;
}
typedefstructaSTRUCTA
STRUCTA*b[4];
//一个存放了4个STRUCTA*型指针的数组,其大小为4*sizeof
(STRUCTA*)=16
typedefvoid(*FUNC)();
FUNCfunarr[4];
//定义了一个函数指针数组
2.结构数组
}a[12];
//定义了一个结构数组,大小为12*sizeof(structa)=48;
3.数组指针 :
一个指向一维或者多维数组的指针;
int(*b2)[20];
//二级指针;
b2指向一个大小为20*sizeof(int)的int型数组
int(*b3)[30][20];
//三级指针――>
指向三维数组的指针;
4.数组的数组名具备一些指针的用法,近似于指针,
intarray[12][31];
//数组的嵌套定义,array是一个含有12个数组类型元素的数
组,其中的每一个
元素是一个int数组,array的类型是int[12][31],其指向的类型是int[31],可以理解为
array是指向数组
的指针。
array[1]是一个含有31个int型元素的数组,它的类型是int[31],它指向的类型是int;
intI;
I=array[4][7]等同于I=*(array[4]+7)
等同于I=*(*(array+4)+7)
int*p;
int**q;
int(*ptr1)[31];
int(*ptr2)[41];
p=array[2];
//正确,p指向数组array[2]中下标为0的元素
p=array;
//错误,array是二维数组,其类型为“数组的数组”,类型不匹配
q=array;
//错误,array不是指向指针的指针
ptr1=array;
//正确,同是指向数组int[31]的指针。
ptr2=array;
//错误,array是指向int[31]的指针,ptr2是指向int[41]的指针。
例九:
char*str[3]={
"
Hello,thisisasample!
Hi,goodmorning."
Helloworld"
};
chars[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,它指向的地址是字符
串"
的第一个字符的地址,即'
H'
的地址。
str+1也是一个指针,它
指向数组的第1号
单元,它的类型是char**,它指向的类型是char*。
*(str+1)也是一个指针,它的类型是char*,它所指向的类型是char,它指向
的第一
个字符'
,等等。
5.下面总结一下数组的数组名的问题。
声明了一个数组TYPEarray[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(*ptr)[10];
array;
上例中ptr是一个指针,它的类型是int(*)[10],他指向的类型是int[10],我们用整个数
组的首地址来初
始化它。
在语句ptr=&
array中,array代表数组本身。
本节中提到了函数sizeof(),那么我来问一问,sizeof(指针名称)测出的究竟是指针自身类型的大小呢还
是指针所指向的类型的大小?
答案是前者。
则在32位程序中,有:
sizeof(int(*)[10])==4
sizeof(int[10])==40
sizeof(ptr)==4
实际上,sizeof(对象)测出的都是对象自身的类型的大小,而不是别的什么类型的大小。
6数组与指针的不同
引例:
文件1:
intmango[100];
文件2:
externint*mango;
.............
//一些引用mango[i]的代码
错误:
把数组的定义等同于指针的外部声明。
6.1声明与定义
搞清楚这个问题之前,澄清一些重要概念:
C语言中的对象必须有且只有一个定义,但它可以有多个extern声明。
概念
区别
定义
只能出现在一个地方,确定对象的类型并分配内存,用于创建新的对象。
inta[12]
相当于特殊声明,它为对象分配内存。
声明
可多次出现,描述对对象的类型,用于指代其他地方定义的对象。
externinta[]
相当于普通声明,它所说明的并非自身,而是描述其他地方创建的对象。
由于extern声明不为对象分配内存,所以不必提供关于数组长度的信息。
对于多维数组,需要提供除最左边一维之外其他维的长度——给编译器足够的信息产生相应代码。
6.2输组与指针的访问过程
chara[9]=“abckefg”;
c=a[i];
编译器符号表具有一个地址9980
运行时步骤1:
取i的值,将它与9980相加;
运行时步骤2:
取地址[9980+i]的内容。
Char*p;
c=*p;
编译器符号表有一个符号p,它的地址是4624
取地址4624的内容,就是’5081’;
取地址5081的内容。
6.3定义为指针,但以数组方式引用的过程
char*p=“abchdefs”c=p[i]
编译器符号表有个p,地址为4624
取地址4624的内容,即’5081’;
取得I的值,并将它与5081相加;
运行时步骤3:
取地址[5081+i]的内容。
如果将p声明为指针,那么不管p原来是定义为指针还是数组,都会按照上面的三个步骤操作。
如:
char*p[10];
另一个文件中externchar*p;
当用p[I]提取这个声明中的内容时,实际上得到的是一个字符,而编译器却把它当作一个指针……
6.4指针和数组的其他区别
指针
数组
保存数
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 语言 指针 详述