本文为我的读书笔记,仅供记录学习过程当中遇到的往后须要留意的问题,若有相关版权问题请及时通知做者。
指针
指针的内存布局
一个基本的数据类型,包括结构体等自定义类型加上 * 号就构成了一个指针类型,这个类型的大小是必定的,与 * 号前面的数据类型无关。* 号前面的数据类型只是说明指针所指向的内存里存储的数据类型。因此,在32位系统下(64位测试相同结果),无论什么样的指针类型,其大小都为4byte。
int *p=NULL与*p=NULL
int *p=NULL;
定义一个指针变量p,其指向的内存里面保存的是int类型的数据;在定义变量p的同时把p的值设置为0x00000000,而不是把*p的值设置为0x000000.这个过程叫作初始化,是编译时进行的。(我的理解:也就说把指针位置初始化,而不是把指针指向的内容清空)
int *p;
*p=NULL;
定义一个指针变量p,其指向的内存里保存的是int类型的数据;此时p自己的值不肯定,多是一个非法的地址。第二行给*p赋值为NULL,即给P指向的内存赋值为NULL;可是因为p指向的内存多是非法的,因此调试的时候,编译器可能会报告一个内存访问错误。
NULL就是NULL,被宏定义为0,但和0不一样。要注意大小写。
如何将数值存储到指定的内存地址
int *p=(int*)0x12ff7c
*p=0x100;
为了取一个合法的地址,能够创建变量编译时查看地址,目的是避免使用了不合法的地址。
*(int*)0x12ff7c=0x100;同理
数组
数组的内存布局
int a[5];
sizeof(a)的值为sizeof(int)*5 =20
sizeof(a[0])的值为sizeof(int)=4
sizeof(a[5])的值为4,没有出错。由于sizeof是关键字,不是函数。函数求值是在运行的时候,而关键字sizeof求值是在编译的时候。虽然并不存在a[5]这个元素,可是这里也没有真正访问a[5],而是仅仅根据数组元素的类型来肯定其值。
sizeof(&a[0]) 值为4。取元素a[0]的首地址。
sizeof(&a)值为4 。取数组a的首地址 。但VC++6.0的结果是20.
数组名做为左值与右值的区别
x=y;
左值:在这个上下文环境中,编译器认为x的含义是x所表明的地址。由编译器管理。
右值:在这个上下文环境中,编译器认为y的含义是y所表明的地址里面的内容。
C语言规定,出如今赋值符左边的符号所表明的低智商的内容必定是能够被修改的。也就是说,只能给非只读变量赋值。
a做为右值时等价于&a[0],表明的是数组首元素的首地址,而不是数组的首地址。但这仅仅是表明,编译器并无为数组a分配一块内存来存其地址。
a不能做左值。只能访问数组的某个元素而没法把数组当一个整体进行访问。
指针与数组的关系。
指针与数组没有关系。
指针变量在32位系统下,永远占4个byte,其值为某一个内存的地址,指针能够指向任何地方,但不是任何地方都能经过指针变量访问到。
数组的大小与元素的类型和个数有关。定义数组时必须指定元素类型和个数。数组能够存任何类型的数据,但不能存函数。
以指针的形式和如下标的形式访问指针。
对于 char *p=“abcdef”;
若是要取字符e,使用*(p+4)与p[4]均可以实现。编译器老是把如下表的形式的操做解析为以指针的形式的操做。
如下标的形式访问在本质上与以指针的形式访问没有区别,只是写法不一样。
以指针的形式和如下标的形式访问数组
数组自己在栈上。对数组的访问必须现根据数组的名字找到数组首元素的首地址,而后根据偏移量找到相应的值。这是一种典型的“具名+匿名”的访问。
指针和数组均可以 以指针形式 或 如下标形式进行访问。但指针是彻底的匿名访问,数组是典型的具名+匿名访问。
a和&a的区别
main()
{
int a[5]={1,2,3,4,5};
int *ptr=(int *)(&a+1);
printf("%d,%d",*(a+1),*(ptr-1));
}
对指针进行加1操做,获得的是下一个元素的地址。一个类型为T的指针的移动,以sizeof(T)为单位。
&a是数组的首地址,所以&a+1至关于&a+5*sizeof(a),即&a+5*sizeof(int),也就是下一个数组的首地址。
a是数组首元素的首地址,也就是a[0]的首地址,a+1就是数组下一元素的地址,即a[1]的地址。
指针和数组的定义与声明
定义为数组,声明为指针
文件1:char a[100];
文件2:extern char *a;
是错的。当声明extern char *a;时,编译器认为a是一个指针变量,占4个byte。这4个byte里存放了一个地址,这个地址上存的是字符类型数据。
定义为指针,声明为数组
一样错误。
指针和数组的对比。
在一个地方定义为指针,在别的地方也只能声明为指针;在一个地方定义为数组,在别的地方也只能声明为数组。
指针 |
数组 |
保存数据的地址,任何存入指针变量的数据都会被当作地址来处理。指针自己的地址由编译器另外存储,存储位置未知 |
保存数据,数组名a表明数组首元素的首地址。&a表明整个数组的首地址。a自己的地址由编译器另外存储,存储位置未知 |
间接访问数据,首先取得指针变量的内容,把它做为地址,而后从这个地址提取数据或向这个地址写入数据。指针能够以指针或下标的形式访问。但本质上都是先取p的内容而后再加偏移量*sizeof(类型)个byte |
直接访问数据,数组名a是整个数组的名字,数组内每一个元素没有名字。只能经过具名+匿名的方式来访问某个元素。数组能够以指针的形式或下表的形式访问。但本质上都是a所表明的数组首元素的首地址加上偏移量*sizeof(类型)个byte |
一般用于动态数据结构 |
一般用于存储固定数目且数据类型相同的元素 |
相关函数 malloc free |
隐式分配和删除 |
一般指向匿名数据,也能够指向具名数据 |
自身即为数组名 |
指针数组和数组指针
指针数组:是一个数组,数组的元素都是指针。数组占多少个字节由数组自己决定。全称是 存储指针的数组
数组指针:是一个指针,指向一个数组。32位系统下永远是4个字节,指向的数组占多少个字节未知。全称是 指向数组的指针。
int *p1[10];
int (*p2)[10];
[ ]的优先级高于*,因此p1先与[ ]结合,构成一个数组的定义,因此它是一个名为p1的数组,数组元素是int *。也就是指针数组。
()的优先级比[ ]高,因此p2与*结合构成指针的定义,指针变量名为p2,int修饰的是数组的内容。数组在这里没有名字,是个匿名数组。因此p2是一个指针,指向一个包含10个int类型数据的数组,即数组指针。
数组指针本来的定义形式为:int (*)[10] p2;
由于数组指针是指向数组的,因此赋值应当=&a 而不是 =a
地址的强制转换
指针变量与一个证书相加减并非用指针变量里的地址直接加减这个整数。这个整数的单位是元素个数。
多维数组与多级指针
二维数组初始化是花括号嵌套花括号
二级指针
二级指针保存的是一级指针的地址
数组参数与指针参数
一维数组参数
没法向函数传递一个数组。
C语言中,当一维数组做为函数参数的时候,编译器老是把它解析成一个指向其首元素首地址的指针。
函数自己是没有类型的,右值函数的返回值才有类型。
函数的返回值也不能是一个数组,而只能是指针。
实际传递的数组大小与函数形参指定的数组大小没有关系。
一级指针参数
没法把指针变量自己传递给一个函数,传递过去的是实参的一个备份。若是须要在函数中操做指针,如malloc,那么须要在函数中把指针变量return回来。
二维数组参数与二维指针参数
数组参数 |
等效的指针参数 |
数组的数组:char a[3][4] |
数组的指针:char (*p) |
指针数组:char *a[5] |
指针的指针:char **P |
C语言中,当一维数组做为函数参数的时候,编译器老是把它解析成一个指向其首元素首地址的指针。这条规则并非递归的,也就是说只有一维数组才是如此,当数组超过一维时,将第一维改写为指向数组首元素首地址的指针后,后面的维不再可改写。
函数指针
函数指针就是函数的指针。是一个指针,指向一个函数
VC++6.0里,给函数指针赋值时,能够用&fun或直接用函数名fun。这是由于函数名被编译以后就是一个地址。因此这两种用法没有本质的差异。
函数指针数组
char * (*pf)(char *p)定义的是一个函数指针pf。既然pf是一个指针,那就能够存储在一个数组里。
char *(*pf[3])(char *p);
这是定义一个函数指针数组。它是一个数组,名为pf,数组内存储了3个指向函数的指针。这些指针指向一些返回值类型为指向字符的指针、参数为一个纸箱字符的指针的函数。
函数指针数组的指针
char *(*(*pf)[3])(char *p);
这里的pf是指针,指向一个包含了3个元素的数组;这个数组里存的是指向函数的指针;这些指针指向一些返回值类型为指向字符的指针、参数为一个纸箱字符的指针的函数。
,,