说起指针,这是C语言的强大之处,但也是伤脑筋之处,就像那句没有使用过C语言和C++导致内存泄露而产生严重问题的人永远不能理解java设计的初衷,而指针正是导致内存泄露的罪魁祸首。
1.指针是什么?
就像宾馆里每一个房间都有一个门牌号一样,一个房间住一个客人,我们的内存也都有一个编号,每一块内存也都有一个值,这符合我们的生活。而这个编号就是地址,这个值就是内存内容,在一个程序中,程序经过编译,变量名直接转化为地址(PS猜想:这也可能是变量名不占内存空间的原因),然后通过这个编号(地址)见到里面的人(得到相应的内存内容)。那么有没有些想法呢?我们是不是可以直接把这个编号保存起来,然后用这个编号来调用这个值。这样指针与指针变量就可以说就合情合理了,我们把一个变量的地址叫做指针。
我们都知道 类型+*+变量名,这里其实不叫声明一个指针,这是得到一个指针变量,然后这个指针变量可以保存一个地址(就是这个变量的指针),那么这就相当于我们知道要去房间的门牌号。
注意:*在声明指针时,仅仅表示这是一个指针变量,就像%d在输出时表示整型一样。而除了声明之外,其他时候它都表示指向对象,就是指向那一段内存空间的首地址。
??这里有一个疑问,如果变量名代表的是首地址的话,那么赋值的时候为什么要是 (指针类型)变量名 = &变量名 ?无奈我只有去看看编译原理及实现了。
综上,其实指针就是一个变量的地址,而我们一般所说的指针是指针变量。
但是,其实有些书上,比如《C语言入门经典》上就说可以储存地址的变量叫指针。这个真的很混乱,仁者见仁,智者见智吧。
2.有点难的指针与数组。其实数组名就是首地址嘛,这个真没疑问,所以我们可以用一个指针变量将它保存起来,然后来调用数组中的值,比如如下代码:脑残的输入三个数,再输出来。
#include<stdio.h> void main(){ int a[3]; int *p; int i ; p=a; for(i=0;i<3;i++){ scanf("%d",&a[i]); } for(i=0;i<3;i++,p++){ printf("%d",*p); } }
然后就发现&a[i] 不可以用a++这样来代替,这是因为数组名虽然也是一个地址,但是地址也是有常量与变量之分的吧,数组名就是一个常量。
其实数组与指针最难的就是多维数组与指针了。其实得到地址与取值并不难,难的是多种多样的表示方法下,还保证内存不泄露,保证指针变量与数组的对应关系。
有两种感到奇葩的表达方式*p++和*(p++)竟然相同,而且竟然是先取值,再让p+1,其实不用大惊小怪,a++前有运算符都是这样的规则。
#include<stdio.h> void main(){ int a= 0; printf("%d",-a++); }
输出是0。
在这还有一条经验,那些大牛在用数组做参数时并不写数组,而是一个指针,这和C语言自带的函数吻合。
3,指针的两个应用,动态申请内存和字符串操作
其实字符串操作就是数组操作嘛,动态申请内存呢,解决了一个想了好久的问题,实现长度可变的数组,有没有java中ArrayList的感觉呢?和ArrayList相比,其实他还是需要输入申请多少内存的,就是下面n的值。
动态内存分配即分配内存大小在运行时才确定,一般在堆中分配
对于malloc函数:
根据不同的电脑使用状况,申请内存有可能失败,失败时返回NULL,因此,动态申请内存时,一定要判断结果是否为空。malloc()的返回值类型是“void *”,因此,不要忘记类型转换。(许多人都省略了。)
写一个例子:
#include <stdio.h> #include <stdlib.h> #include <string.h> int main(void) { char *p ; p = (char *)malloc(40 * sizeof(char)) ; if (p == NULL) { //这个判断是必须的 printf("内存分配出错!"); exit(1); } strcpy(p, "这是劝学网C语言教程。 "); //不要忘记给新内存赋值 printf("%s", p); free(p); //过河一定要拆桥 p = NULL ; //释放后的指针置空,这是非常好的习惯,防止野指针。 return 0; }
对于calloc函数:
int *p = calloc(n,size);
calloc可以分配n个size大小的内存空间,一般用于一组struct结构体的分配。
那么calloc和malloc有什么区别呢?抛开n参数不谈(malloc也可以将参数设置为n*size达到一样的效果),最关键的区别是malloc分配的内存是不保证初始化的,而calloc会将分配的内存都初始化为0.
对于realloc函数:
realloc函数将ptr指向的内存空间重新分配大小为size并返回新的内存首地址。具体的实现,函数首先会尝试直接在已经分配的内存后进行padding,如果空间足够那么还是返回原来的地址,如果不够,则会寻找新的空间并malloc size个字节,之后再将原先的内容“搬家”到新的内存地址,所以函数的返回值可能和原指针相同,也可能不同。
另外,size参数如果是0,则该函数和free效果相同。如果ptr是NULL,函数的效果和malloc相同~
对于free():
释放前三个函数申请的内存空间。关于free最经典的问题就是内存泄露(memory leak)。所以,使用前三个分配函数分配的内存一定要记得free掉。
有人对某一只在函数内使用的指针动态分配了内存,用完后不释放。其理由是:函数运行结束后,函数内的所有变量全部消亡。这是错误的。动态分配的内存是在“堆”里定义,并不随函数结束而消亡。
有人对某动态分配了内存的指针,用完后直接设置为NULL。其理由是:已经为NULL了,这就释放了。这也是错误的。指针可以任意赋值,而内存并没有释放;相反,内存释放后,指针也并不为NULL。
唉、、、、、学艺不精啊,编译原理和堆和栈的知识还的自己去学啊!fighting。。。。。。。。。。。。。。
又是愉快的一天吧。。。。。。。。。。。