指针
指针就是内存地址。通常我们说的指针是指:指针变量。
因此:指针变量存放指针,指针就是一个内存空间的地址!
int a
int *p
比较:a标示了内存的一个空间,該空间能够存放一个整数。p是一个指针类型变量(通常称为指针),它也标示了内存的一个空间,該空间存放的是一个内存地址(内存地址在32位下为4字节)。
理解指针:我们需要弄清指针的三方面内容
1、指针的类型
2、指针所指空间存储数据的类型
3、指针的值及存储指针的空间大小
以下通过例子说明这三方面:
1、int *p;
2、char *p;
3、int **p;
4、int (*p)[3]
1、指针的类型:从语法的角度讲,只要将定义中指针的名字去掉,剩下的部分就是这个指针的类型。
1、int *p; int*
2、char *p; char*
3、int **p; int**
4、int (*p)[3] int(*)[3]
理解:一个基本的数据类型加上*号,就构成了指针的类型,这个类型定义的变量大小是一定的,与*号前面的数据类型无关。*号前面的数据类型只是说明了指针所指向的内存里存储数据的类型! 如:int* 就是个指针的类型,用該类型定义的变量p大小(32位的为4个字节),int说明該指针所指向的内存里存储的是int类型的数据!
2、指针所指空间存储数据的类型:语法上讲:只需把指针定义中的指针变量名和名字左边的指针声明符*去掉,剩下的就是指针所指空间存储数据的类型。
1、int *p; int
2、char *p; char
3、int **p; int*
4、int (*p)[3] int [3]
理解:指针所指空间的数据类型是我们所关心的,对于编译器来说,根据該类型来确定读写从指针地址开始的多少个字节空间的数据。
3、指针的值以及存储指针值的空间大小
指针的值是指针变量本身存储的数值,由于指针就是一个内存地址,所以指针的值也就是一个内存地址。在32位系统中,内存地址均为32位长,所以存储指针的空间为4个字节。可以用sizeof()来测试。
对比一般变量和指针变量的定义和含义:
int p; p是整型变量,用于标识内存的一个空间内能存放一个整数
int *p; p是指针类型变量,用于标识内存的一个内存空间内能存放一个地址,該地址处能存放一个整数
int p[5]; p是一个数组(一段连续的存储空间),数组中有5个元素,每个元素为一个整数,p为这段连续存储空间的首地址
int *p[5]; p是一个数组(一段连续的存储空间),数组中有5个元素,每个元素为一个地址(类型为int *),每个地址处能存放一个整数,p为这段连续存储空间的首 地址 (被成为指针数组,主体是数组,[ ]优先级高于*)
int (*p)[5]; p是一个指针类型的变量,p指向具有5个元素的数组(类型为int[5]),数组中的元素为整数。(被称为数组指针,主体是指针!)
int **p; p是指针类型的变量,用于标识内存的一个空间能存放一个地址(类型为int**),該地址处存放的存放的还是一个地址(类型为int *),这个地址处能存放一 个整数(类型为int)
int p(int); p是一个函数,函数有个整型参数,并且函数返回值为整型
int (*p)(int); p是一个指针,一个指向函数的指针,函数有一个整型参数,并且函数返回值为整型。
1 #include<stdio.h> 2 3 main() 4 { 5 int a=12,*p,**ptr; 6 ptr=&p; 7 p=&a; 8 **ptr=34; 9 printf("%d,%d,%d\n",a,*p,**ptr); 10 }
输出:34,34,34
指针和数组: (指针和数组是不同的,指针是内存单元的一个地址32位为4字节,数组其大小和元素类型和个数有关。
对指针和数组元素的访问,可以采用下标法,也可以采用指针法。貌似指针和数组有什么关系,事实上,它两没有关系。指针就是指针,数组就是数组。
在内存中的数据,一种方法是用数组名加下标的方式读写,另一种方法是通过指针间接的读写。
1.对数组元素的访问: int a[10]={0,1,2,3,4,5,6,7,8,9};
a为数组元素的首地址a[0],现在想读取整数5; (1)指针法: *(a+5) (2)数组法:a[5], 编译器把以下标形式的操作解析为以指针的形式的操作,a作为数组元素的首地址,再加上括号中5个元素的偏移量,a+5*sizeof(int)
2、对指针所指空间的访问: char *p=“abcde”;
p为一个指针变量,大小为4个字节(32位系统),p里面存储了一块内存的首地址,这块内存在静态区(还不明白),其空间大小为6个字节,这快内存空间没有名字,通过匿名访问。 读取字符d (1)指针法:*(p+3) (2)下标法 p[3] ,译法和上面相同
注意:指针加减偏移量时,不是代表指针移动多少个字节,而是代表移动多少个指针所指的元素。
指针和数组的声明和定义:
外部变量的引用问题:如果在文件1中定义了一个数组,在文件2中要引用这个数组,那么在这两个文件中怎样声明和定义呢?
在同一个文件中,特别是数组名作为函数参数时,实参和形参可以用数组,也可以用指针,两种任意组合没有问题(还没有试过)。但是在不同文件间引用数组时,作为外部变量的声明形式必须和另一个文件中的定义形式相同,否则会出错,切记!!!
例子: file1.c 中:
1 #include<stdio.h> 2 3 extern void func(void); 4 char *a={"ABCDE"}; 5 6 void main() 7 { 8 func(); 9 printf("%c,%c\n",a[1],a[2]); 10 }
file2.c中:
1 #include<stdio.h> 2 3 extern char *a; 4 void func() 5 { 6 printf("%c\n",a[1]); 7 }
gcc -c file1.c file2.c
gcc -o file file1.o file2.o
./file
输出: B
B ,C
若将 file2.c中 extern char *a;改为 extern char a[ ]; 则上面的输出第一行的变成乱数! why??
因为:在file2.c中将a声明为外部数组(注意这里声明是不分配空间的,所以无须说明数组有多少个元素,具体是多大可以在其它文件中定义)
这时编译器认为a是一个数组,其大小为4个字节(因为file1.c中定义了a为指针变量),因此a[1]就输出a这个指针变量中指针的第二个字节内容!是不确定的。。。
若将file1.c中 char *a={"ABCDE"}; 該为 char a[ ]={"ABCDE"} ; file2.c 中不变,则运行出现段错误。。。 why??
file1.c中定义数组a,并为其分配6个字符的空间,但在file2.c中,编译器并不知道a是个数组,认为a是个指针变量,而这时候a中存的值为数组{“ABCDE“},由于指针大小为4个字节,所以在file2.c 中a的值变为{”ABCD“},该值被作为指针(画图好理解),这时候输出a[1](相当于*(p+1)),这个地址中的值不确定。。可能这个地址都越界,出现段错误!
指针的算术运算和关系运算:
指针的算术运算:因为指针是一个内存地址,所以,其加减一个整数的意义是:指针从当前地址移动多少个元素,元素是指针所指空间存储的数据类型。
1 #include<stdio.h> 2 3 main() 4 { 5 char s[]={"ABCDEFGH"}; 6 int *p; 7 p=(int *)s; 8 p++;//p+sizeof(int)*1,p+4,指向E 9 printf("%c\n",*p); 10 p=p+5;//p+sizeof(int)*5,p+20,越界了,这里p指向的是int类型的数据 11 printf("%c\n",*p); 12 }
输出E和乱码
1 #include<stdio.h> 2 3 main() 4 { 5 char s[]={"ABCDEFG"}; 6 char *p,**ptr; 7 p=s; 8 ptr=&p; 9 printf("%c\n",**ptr); 10 ptr++; 11 printf("%c\n",**ptr); 12 }
输出A和段错误!
在第10行,ptr++,ptr指向的类型是char *,是个指针,大小为4字节,所以ptr=ptr+sizeof(char *)*1=ptr+4,这时候ptr的值越界了。。(画图好理解)
另外:关系运算,两个指针可以进行减法运算,一般是高地址减去低地址,两个指针不能进行加法运算是非法的!关系运算符也都适用。
数组的首地址和数组元素的首地址:
注:它两值是相同的,但是类型不同,数组首地址&a;;;数组元素首地址:a或a[0],等价。。。
例子:
1 #include<stdio.h> 2 3 /*該程序说明 数组首地址 和 数组首元素的地址 的区别 4 *数组首元素(a==a[0])和&a值是一样的,但是类型不同 5 *a/a[0]类型是:int * 6 *&a的类型是:int(*)[] 7 *也就是说下面程序中p1和p2的值是一样的 8 */ 9 10 11 main() 12 { 13 int a[10]={1,2,3,4,5,6,7,8,9,10}; 14 int *p1; 15 int (*p2)[10]; 16 p1=a; 17 p2=&a; 18 printf("%d\n",*p1); 19 printf("%d\n",*(int *)p2); 20 printf("%d\n",*(p1+1)); 21 printf("%d\n",*(int *)(p2+1)); 22 }
要是16行改为:p1=&a; 则编译出现:
ptr1.c:16: warning: assignment from incompatible pointer type
原程序输出:
1 1 2 -1078863728
21行输出乱码,因为p2+1, p2 的类型为 int [10](所指向数据的类型), 那sizeof(p2)=4*10 字节了, p2+1 =p2+ 4*10*1