指针

指针就是内存地址。通常我们说的指针是指:指针变量。

因此:指针变量存放指针,指针就是一个内存空间的地址!

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

posted on 2013-03-14 21:49  秋天里的红苹果  阅读(177)  评论(0编辑  收藏  举报