C语言指针与动态内存分配复习

一、引入

最近在学习数据结构,使用C语言来实现,经常用到指针相关的操作,而且还要频繁的动态分配内存,所以对指针以及动态分配内存的内容进行一下简单复习,方便后续数据结构的学习。

二、对指针的理解

指针其实也是一种变量,是一种特殊类型的变量,它存储的是变量的地址在32位系统中占用4个字节,在64位系统重占用8个字节。

三、指针的使用

定义一个指针

指针与其他基本数据类型的定义相似,也是需要指定类型,只有类型相同,指针才能操作它指向的地址。

例如:

1
2
3
4
int a;
double b;
int *p = &a; // 可以
int *p = &b; // 不可以

从上边的程序可以看出,定义指针,其实就是在变量名前加一个*

使用指针

使用指针,需要用到*运算符,来获取指针指向的内存上的数据。

1
2
3
int a = 10;
int *p = &a;
cout << *p; // 输出10

上面这是对于基本数据类型指针的使用,对于结构体指针的使用则需要用到->运算符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct test
{
int a;
int b;
};

int main()
{
test t;
t.a = 10;
t.b = 20;
test *p = &t;
cout << p->a << " " << p->b;
return 0;
}

说到结构体,还经常会有一种写法

1
2
3
4
5
typedef struct test
{
int a;
int b;
} *t;

这种情况是使用typedeftest类型的结构体指针起了一个别名,这样以后再用这个指针的时候可以不加星号

1
2
tset te;
t p = &te

突然想到一些引用相关的知识:

1
2
3
4
5
void add(int a, int b, int &sum)
{
sum = a + b;
return;
}

比如这样的一个函数,当我形参sum前有一个&符号,这里的&符号并不是取地址的意思,而是C++中引用传递的意思,什么事引用传递呢。可以这么理解,C语言默认的是值传递,当调用函数的时候,知识将实参的值复制一份赋值给形参,在函数里是无法改变实参的值的,引用传递呢,就是给实参取了个别名,不是复制了一份,相当于我们在函数内其实就是在操作这个参数。

1
add(10, 20, sum); // 调用方式

又想到一个点:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
typedef struct test
{
int a;
int b;
} *tt;

void ddd(tt t) // 如果这样写:ddd(tt &t);也是会输出10
{
cout << t->a;
}

int main()
{
test t;
t.a = 10;
tt p = &t;
ddd(p);
return 0;
}

分析一下,第一种,tt其实是一种test指针类型,我们传递的参数p也是test指针,所以是完全没问题的,那么为什么加了&依然可以呢,因为引用,就是给变量起了个别名,也就是说我们这里的tp的别名,p的类型是test指针,所以我们的函数用tt类型的形参,所以直接t->a也是没问题的。

指针与函数

函数名其实也是个地址,所以可以用指针来指向这个函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
int maxValue(int a, int b)
{
if (a > b)
return a;
else
return b;
}

int test(int a, int b, int (*p)(int, int)) // 函数作为函数的参数,这里也可以写成 int p(int ,int).参数名可以写也可以不写,比如int p(int a,int b)
{
return p(a, b);
}

int main()
{
int (*p)(int, int) = maxValue; // 函数指针的类型就是函数返回值的类型
cout << p(10, 50);
cout << test(10, 50, p);

return 0;
}

四、内存分配

有四个函数:

1
int *p = (int *)malloc(size); // 申请一块连续可用的空间,空间大小是size个字节,并返回指向这块空间的指针,注意是空指针

返回的是空指针,所以我们要转换一下类型,然后去接收它;

1
void *calloc(size_t num,size_t size); // 为num个大小为size的元素开辟一块空间,并且把空间的每个字节初始化为0,返回值也是空指针

返回的是空指针,与malloc同样的处理方式

1
void free(void *memblock);

释放内存,参数是一个指针,释放掉这个指针指向的内存。注意,它只能释放动态分配出来的内存,即只能释放堆区,而不能释放栈区的。释放内存后,并不会把他重置成空指针,此时它为野指针,指向一块不知道的内存,要及时将他重置成空指针。

1
void *realloc(void *memblock,size_t size); // 为memblock指向的位置重新分配内存

这个是分情况的,如果memvlock指向的那块空间后还有足够的内存,那么就会在后面继续分配,返回值与memblock相同。

如果空间不够了,那么就会重新找一块足够的空间,先开辟出空间,再把原来的属于拷贝到新空间,然后释放原来的空间,同时返回这块新空间的地址。