伤脑筋的指针初探

说起指针,这是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。。。。。。。。。。。。。。

又是愉快的一天吧。。。。。。。。。。。

原文地址:https://www.cnblogs.com/969059506-java/p/3675830.html