17章 指针的高级应用-《C语言程序设计现代方法》第二版修订版

1.动态存储分配

  • malloc函数-分配内存块,但是不对内存块进行初始化
  • calloc函数-分配内存块,并且对内存块进行清零
  • realoc函数-调整先前分配的内存块大小

通常用第一个,效率比较高(因为没初始化)

空指针

如果没有足够的内存空间,就会返回空,所以开空间后,判断一下是否为空比较好。

2.动态分配字符串

2.1 使用malloc函数为字符串分配内存

malloc函数具有如下原型:

 void *malloc(size_t size);

因为不知道打算在这个内存块存储什么类型的数据,所以返回的是void *类型的值,void *类型的值是“通用”指针,它本质上只是内存地址。(感觉自己需要什么数据,强制转换一下会更好)
例子

#include <bits/stdc++.h>
using namespace std;

int main()
{
    char *a;
    a=(char *)malloc(2);
    a[0]='a';
    cout<<a[0]<<endl;
    return 0;
}


3 动态分配数组

3.1 使用malloc函数为数组分配存储空间

使用malloc函数为数组分配存储空间。这种方法和用它为字符串分配空间非常像。
主要区别就是任意数组的元素不需要像字符串那样是1字节的长度。这样的结果是,我们需要使用sizeof运算符来计算出每个元素所需要的空间数量。如下

#include <bits/stdc++.h>
using namespace std;

int main()
{
    int *a;
    a=(int *)malloc(2*sizeof(int));
    a[0]=1;
    cout<<a[0]<<endl;
    return 0;
}

4 释放存储空间

4.1 free函数

如下图,如果不释放P一开始指向的空间,就会浪费,被称为垃圾,留有垃圾的程序存在内存泄露现象
请添加图片描述

4.2 悬空指针

调用free(p)函数会释放p指向的内存块,但是不会改变p本身。如果忘记了p不在指向有效内存块,混乱可能随之即来。

char *p=malloc(4);
...
fre(p);
...
strcpy(p,"abc");/*** Wrong ***/

5 链表

5.1 声明结点类型

struct node{
    int value;
    struct node *next;
};

现在已经声明node结构体了。还需要一个始终指向表中第一个结点的变量。这里把此变量命名为first

struct node *first = NULL;

这里引用一下这篇文章的图片
在这里插入图片描述

5.2 创建结点

创建结点并分配空间

    struct node *new_node;
    new_node =(struct node *) malloc(sizeof(struct node));

赋值,采用间接寻址运算符*,然后选在运算符 . ,在*new_node两边的圆括号是强制要求的,因为运算符.的优秀级高于运算符 *

(*new_node).value=10;

5.3 ->运算符

结构体中的变量可以用这个符号

new_node->value=10;
代替
(*new_node).value=10;

5.4 在链表的开始处插入结点

感觉容易迷惑的是new_node在第二次开辟空间的时候,就已经分配新的地址了,不要认为还是前一个。(while循环算是搜索链表吧)

#include <bits/stdc++.h>
using namespace std;
struct node{
    int value;
    struct node *next;
};
int main()
{
    struct node *first=NULL;
    /*插入第一个------*/
    struct node *new_node;
    new_node =(struct node *) malloc(sizeof(struct node));
    new_node->value=5;
    new_node->next=NULL;
    first=new_node;
    /*插入第一个------*/

    /*插入第二个------*/
    new_node =(struct node *) malloc(sizeof(struct node));//这是一个新空间,与上一个不同
    new_node->value=10;
    new_node->next=first;
    first=new_node;
    /*插入第二个------*/

    while(first!=NULL){
        cout<<first->value<<endl;
        first=first->next;
    }
    return 0;
}

6 指向指针的指针

“指向指针的指针”这一概念也频繁出现在链式数据结构中。实用的地方在于,当函数的实际参数是指针变量时,有时候会希望函数能通过让指针指向别处来改变此变量。这就需要指向指针的指针。

如果添加一个结点的函数是如下这样写,中间省略了。

struct node *add_to_list(struct node *list,int n){
    struct node *new_node;

    ...

    return new_node;
}

假设把最后一句修改为

list = new_node;

是无法影响first的指向的。

add_to_list(first,10);

如上传过去的不过是是first指针变量,函数内部新生成了一个list变量复制了first里面的值而已,如下图,虽然指向同一个值,但是修改list指向新的值没有用,根本不影响first的指向。
在这里插入图片描述
经过如下代码的修改即可,这样就一直在操作first,就使用了指向指针的指针。

void add_to_list(struct node **list,int n){
    struct node *new_node;

    ...

    *list = new_node;
}

7 指向函数的指针

指向函数的指针(函数指针)不像人们所想像的那样奇怪。毕竟函数占用内存单元,所以每个函数都有地址,就像每个变量都有地址一样。
感觉和指针区别不大,主要是定义方式需要注意一下,这篇文章讲的很清晰。
首先定义方式,例子如下

#include <bits/stdc++.h>
using namespace std;
int maxx(int a,int b)
{
    return a>b?a:b;
}
int main()
{
    int (*p)(int,int)=&maxx;
    //或者
    int (*p)(int,int);
    p=maxx;
    return 0;
}

  • 首先他是一个指针变量,所以要有一个 * ,即(*p);
  • 其次前面的int表示这个指针变量可以指向返回值类型为int型的函数
  • 后面括号中的两个int表示这个指针变量可以指向有两个参数且都是int型的函数。
    疑问:调用的时候,必须参数填全吗?后续验证,按理说是要填全的。

7.2 qsort函数

便于更深的理解

7.3 函数指针的其他用途

下面是基本调用

#include <bits/stdc++.h>
using namespace std;
int maxx(int a,int b)
{
    return a>b?a:b;
}
int main()
{
    int (*p)(int,int);
    p=maxx;
    //调用方法有两种
    int a=(*p)(1,2);
    int b=p(1,2);
    cout<<a<<" "<<b<<endl;
    return 0;
}

我们可以把函数的指针存储在数组中,我感觉下面的代码,pp才是对的,为什么结果是一样。。。
答:对函数取地址和不取地址,都是一样的效果,在编译器运行中,具体原因还没搜到好的理解。

#include <bits/stdc++.h>
using namespace std;
int maxx(int a,int b)
{
    return a>b?a:b;
}
int minn(int a,int b)
{
    return a<b?a:b;
}
int main()
{
    int (*p[])(int,int)={minn,maxx};
    int (*pp[])(int,int)={&minn,&maxx};

    int a=p[0](1,2);
    int b=p[1](1,2);
    cout<<a<<" "<<b<<endl;

    cout<<"\n"<<endl;
    int aa=pp[0](1,2);
    int bb=pp[1](1,2);

    cout<<aa<<" "<<bb<<endl;
    return 0;
}

8 受限指针

定义:用restrict声明的指针叫作受限指针。

#include <stdio.h>
#include <stdlib.h>
int main()
{
    int * restrict p;
    p = malloc(sizeof(int));
    int a=1;
    p=&a;
    printf("%d",*p);
    return 0;

}

博客1理解
博客2理解

9 弹性数组成员

博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值