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;
}