关于C语言指针的一些学习心得(一)

来源:互联网 发布:安广网络池州分公司 编辑:程序博客网 时间:2024/06/09 23:04

1.首先理解一些内存。

就跟要去电影院看电影一样,电影院里面有座位,座位编号。人才能坐。内存也类似于电影院里的座位,值不够内存是一个存放数据的空间。我们在内存中存放各种各样的数据。当然我们 要知道我们的这些数据存放在什么位置吧!所以内存也要象座位一样进行编号了,这就是我们所说的内 存编址。座位可以是按一个座位一个号码的从一号开始编号,内存则是按一个字节一个字节进行编址, 每个字节都有个编号,一个字节代表八位。关于各种数据类型占有的字节数在其他篇幅中有说明。内存空间的布局类似于下图所示。注意是按照一个字节一个字节来编址的。



2.C语言中变量的声明。

int a;

char b;

每次我们要使用某变量时都要事先这样申明它,它其实是内存中申请了一个名为i的整型变量宽 度的空间(4字节),和一个名为a的字符型变量宽度的空间(1字节)。比如说,a申请的内存从6开始,则6,7,8,9都将被a预订,用于存放a的数据。而10将被b预订,用于存放b的数据。此时a和b只是个变量,并没有确切的值。但是内存已经分配了。只要声明就会分配。如下图所示。



3.变量赋值。

若进行赋值

a=10;

b='t';

则内存空间变成这样。


当然具体存放的可能是二进制的,并不是10 和‘t'。


这里需要注意的是,一个局部变量在声明的时候就已经开辟了内存空间。如果没有赋值的话,它的值是不确定的。如果有下面几行代码。得出的结果是这样的。

#include "stdio.h"int main(){int i;long a;char c;char d;printf("%d,%d,%d\n",i,&i,sizeof(i));printf("%d,%d,%d\n",a,&a,sizeof(a));        printf("%d,%d,%d\n",c,&c,sizeof(c));printf("%d,%d,%d",d,&d,sizeof(d));    return 1;}
其结果是:



观察结果可以知道,i和 char的值都是不确定的。但是i的开始地址是1244996,而a的开始地址是1244992。因为i和a的内存空间都是在栈中开辟的,在栈中的内存空间是从大往小走的。

可能有人会疑惑为什么d的内存开始的时候是84,它只占一个字节,按理说c应该从89开始,c同样占用一个字节,则a应该从90开始。事实上却不是,不管是c,还是d内存都给他们分配了4个字节。这是字节对齐的原因,关于字节对齐,会另开贴讨论。现在只需知道,对于32位的机器,默认为4字节对齐。

4.指针

指针其实也是一个变量,可以这样说,它跟其他变量其实没啥区别。关键的是,这个变量所代表的内容是什么?对了,这个变量所表示的内容是一个地址。但是说实在的,地址其实就是类似与一个int型的数。所以指针这种变量都是占4个字节的。

#include "stdio.h"int main(){int i;//long a;//char c;//char d[6];int *p=&i;printf("%d,%d,%d\n",i,&i,sizeof(i));//printf("%d,%d,%d\n",a,&a,sizeof(a));    //printf("%d,%d,%d\n",c,&c,sizeof(c));//printf("%d,%d,%d\n",d[0],d,sizeof(d));   printf("%d,%d,%d,%d",*p,p,&p,sizeof(p));    return 1;}

我们运行上述代码,则可以很清楚的得到下面结果。


说明这个指针的值就是i变量的地址,(就如同int a=3, a的值是3.只不过这时候变量不是i,是一个指针p。)这里就需要注意了:p是一个变量,p的值是一个地址,(相当于说i是一个变量,i的值是3),而p的地址则是另外一个值了(就是&p)。而p的值(这个地址),里面还装着一些东西。(本例中就是i的值。)这才是我们研究的关键。所以,指针p是一个地址,它指向了一个变量。这个变量可以是任何类型的变量。但是指针的值只能是一个地址。

归纳起来就是,指针也是一个变量,这个变量的值是一个地址,这个地址是另外一个变量的地址(也就是该指针所指向的变量的地址)。

5.指针类型

从语法的角度看,只要把指针声明语句里的指针名字去掉,剩下的部分就是这个指针的类型了。如:

int *ptr;//去掉ptr,指针类型是int*

char*ptr;//去掉ptr,指针的类型是char*

int**ptr;//去掉ptr,指针的类型是int**

int(*ptr)[3];//去掉ptr,指针的类型是int(*)[3];

int*(*ptr)[4];//去掉ptr,指针的类型是int*(*)[4]


这个int*ptr[3],ptr并不是一个指针,而是一个数组,因为【】的优先级大于*所以ptr[3]是一个数组,然后与int*结合,说明这个数组里面的元素是3个指针。这3个指针指向int型变量。

6.指针所指向的类型。

当你通过指针来访问指针所指向的内存区时,

指针所指向的类型决定了编译器将把那

片内存区里的内容当做什么来看待。  

 

 

从语法上看,你只须

把指针声明语句中的指针名字和名字左边的指针声明符*去掉,

剩下的就是指针所指向的类型。

例如: 

当你通过指针来访问指针所指向的内存区时,指针所指向的类型决定了编译器将把那片内存区里的内容当做什么来看待。  
  从语法上看,你只须把指针声明语句中的指针名字和名字左边的指针声明符*去掉,剩下的就是指针所指向的类型。例如: 

int*ptr;  //去掉*ptr,指针所指向的类型为int.

char*ptr; //去掉*ptr,指针所指向的类型是char.

int**ptr;//去掉*ptr,指针所指向的是一个int*的指针,设为p,则该p指针指向的是一个int的类型

int(*ptr)[3]//指针指向的是int()[3](其实就是int[3])是一个数组,而且这个数组的元素有3个,元素时整型的。

int*(*ptr)[4];//指针所指向的类型是int*()[4],这是一个数组,数组里面的元素是4个指针,而且4个指针指向的类型是整型。

7.指针的值--也就是指针所指向的地址

指针的值是指针本身存储的数值,这个值将被编译器当作一个地址,而不是一个一般的数值。在32位程序里,所有类型的指针的值都是一个32位整数,因为32位程序里内存地址全都是32位长。指针所指向的内存区就是从指针的值所代表的那个内存地址开始,长度为sizeof(指针所指向的类型)的一片内存区以后,我们说一个指针的值是XX,就相当于说该指针指向了以XX为首地址的一片内存区域;我们说一个指针指向了某块内存区域,就相当于说该指针的值是这块内存区域的首地址。 
指针所指向的内存区和指针所指向的类型是两个完全不同的概念。在例一中,指针所指向的类型已经有了,但由于指针还未初始化,所以它所指向的内存区是不存在的,或者说是无意义的。  
以后,每遇到一个指针,都应该问问:这个指针的类型是什么?指针指的类型是什么?该指针指向了哪里?(重点注意)

8指针本身所占有的区域。

上面已经讲过,指针占有4个字节。只要用seizeof(指针的类型)或者sizeof(指针)即可以。

9.指针的算术运算

一句话:

一个指针ptrold加(减)一个整数n后,结果是一个新的指针ptrnew,ptrnew的类型和ptrold的类型相同,ptrnew所指向的类型和ptrold所指向的类型也相同。ptrnew的值将比ptrold的值增加(减少)了n乘sizeof(ptrold所指向的类型)个字节。就是说,ptrnew所指向的内存区将比ptrold所指向的内存区向高(低)地址方向移动了n乘sizeof(ptrold所指向的类型)个字节。  
比如以下例子:

#include "stdio.h"int main(){ char a[20]="You_are_a_girl";  char *p=a; char **ptr=&p;  printf("%d,%d\n",a,&a); printf("p=%d\n",p);  printf("&d=%d\n",&d);  printf("ptr=%d\n",ptr);    printf("*ptr=%d\n",*ptr); printf("**ptr=%c\n",**ptr); ptr++;  printf("ptr=%d\n",ptr);  printf("*ptr=%d\n",*ptr);  printf("**ptr=%c\n",**ptr); return 1;}

打印出来的结果是:



从上述的结果可以看出,ptr是一个指向指针P 的指针,是一个二级指针,ptr的类型是char*,当ptr+1的时候,偏移量应该是sizeof(char*);应该是4.这样以后,ptr所指向的位置是不一定的。所以这时候的**ptr也是一个不确定的值。


10.一些需要注意点

一个例子

#include"stdio.h"int main(){char *a[4]={"China","French","American","Japan"};      char**p=a;   printf("%d,%d,%d,%d,%d,%d\n",a,&a,&a[0],&a[1],&a[2],&a[3]); printf("sizeof(a)=%d\n",sizeof(a)); printf("%d,%d,%d,%d\n",a,a+1,a+2,a+3);  printf("%d,%d\n",&a,&a+1);     printf("%d,%d,%d,%d\n",*a,*(a+1),*(a+2),*(a+3)); printf("%d,%d,%d,%d\n",a[0],a[1],a[2],a[3]); printf("%s,%s,%s,%s\n",a[0],a[1],a[2],a[3]); printf("%s,%s,%s,%s\n",*a,*(a+1),*(a+2),*(a+3)); printf("%c,%c,%c,%c\n",*a[0],*a[1],*a[2],*a[3]); printf("%s,%s,%s,%s\n",p[0],p[1],p[2],p[3]);  printf("%s,%s,%s,%s\n",*p,*(p+1),*(p+2),*(p+3)); return 1;  }

这个例子的结果是


通过这个例子,我们对二级指针,char*a[4]这种类型应该有更加深入的了解。

这是一个数组,数组的元素是4个char*指针,char*指针是指向一个数组的(或者说指向一个char*串),所以其实这是一个二维数组。

第一个问题:a的size是多少?

因为a是一个数组,所以,他的大小应该等于数组的维数4乘以元素的大小,而数组的元素如上所说,是char*的指针,所以大小应该是4乘以4=16.

第二个问题:a,a+1,a+2,a+3.

因为a,是一个数组,所以数组名a代表的是第一个元素的地址,也就是&a[0],同样的a+1是指向第二个元素的地址。他们之间的偏移量应该是4.同样的,&a[0],&a[1]与a,a+1是一样的。注意,这是元素的地址,并不是元素的值。

第三个问题:*a,*(a+1)是什么。

a是首元素的地址,那么*a就是第一个元素的值,同样的*(a+1)是第二个元素的值。以此类推。元素的值,还是一个地址,只不过这四个地址没有必然的连续关系。a[0]与*a,a[1]与*(a+1)是一样的。

第四个问题,如何打印一个字符串。

在C语言中,其实没有字符串这个概念。可以用两种方法来表示字符串,一种是使用数组如char a[]=”china",这时候打印字符串只需要printf("%s",a),因为a是一个指针。或者另外一种方法,定义一个c串,如char*a1="china“.打印方法是一样的printf("%s",a1)。不过需要注意的sizeof(a)和sizeof(a1)是不一样的,第一个是数组的长度,而第二个是一个指针的长度。此处还要注意,字符串数组的结束符是占有直接大小的。他占了一位。所以sizeof(a)应该是为6。


0 0