可以用malloc给结构体指针变量分配动态内存空间_指针——为你的C语言注入灵魂...

2023-05-16

0x00

众所周知,指针是C语言的核心,没有搞懂指针就相当于没有学过C语言。今天我们就来仔细盘一下指针这个玩意。

本文正确食用方法:

1.没了解过指针的可以学习指针     

2.学习过指针但忘的差不多的可以用以回顾       

3.等未来忘了后用以温习指针(笔者写本文目的(ง •_•)ง)

0x01

什么是指针

指针是指向另一数据对象的变量(加粗),指针变量储存了它所指数据对象的地址信息。

0x02如何声明指针:

int *p;        // 声明一个 int 类型的指针 p

char *p        // 声明一个 char 类型的指针p

int *arr[6]   // 声明一个指针数组,该数组有6个元素,其中每个元素都是一个指向 int 类型对象的指针

*p          //访问p所指向的对象

&a           //这个就不用说了,取地址符

卖了半天关子,指针到底有什么作用呢?

--------------------------指针用法-------------------------

0x03

对函数调用的变量进行修改

我们知道,调用函数时,函数调用的参数其实是一种值传递,并没有对该变量产生影响。那么如果我们恰好就是要对该变量进行操作,那么就需要使用指针来实现

假设我们现在要使用函数来实现交换两个变量的值:

void swap(int a,int b){        int temp;        temp = a;        a = b;        b = temp;}

显然,这样是行不通的,因为函数调用时,只会将变量的值传入函数,对该变量本身并不起作用。所以最终两个变量的值并没有交换。

此时,我们就要使用指针来进行值的交换:

void swap(int *a,int *b){        int temp;        temp = *a;        *a = *b;        *b = temp;}

这样,就成功完成了指针的值的变换。在以上代码中,虽然变量的值本身并没有发生改变,但是指针和指针所指变量的指向关系改变了,如下图:

0b6be58c52def97f86c8c5beb4416db0.png

原本,地址和地址所指的值如上图所示

1970ba82d07e241dec362cca959cc29a.png

后来,地址和地址所指的值的指向关系发生了改变,调用函数使得粉红色的箭头发生了改变。

0x04

一个函数需要返回多个值

在C语言中,一个函数最多只能返回1个值,那么如果我们需要用一个函数返回多个值时就需要使用指针来实现。

如:已知两个数a,b。用一个函数求a+b和a-b

我们可以提前定义变量,然后通过函数,将数值“装”进变量当中

代码如下:

void fx(int a,int b,int* c,int* d){       *c=a + b;       *d=a - b;}

容易发现,虽然函数没有返回值,但是真正的运算结果已经“装”在了*c、*d当中,以此我们就间接的实现了一个函数返回多个值。

在编写函数来进行计算时,我们经常会需要异常处理机制,比如对某一个数开根号时,我们需要被开方的数必须大于0,如果输入的数字小于0时,让该函数返回-1来表示计算不成功,代码如下:

double fx (double a){       if(a<0)           return -1;       a = sqrt(a);       return a;}

但是有的时候,我们会遇到计算结果包含了所有的数的情况,比如在计算两个数相除的时候,结果包含了全体实数,此时再使用return -1这种伎俩就已经没用了。这个时候,我们就可以让答案使用指针带回,计算是否正常使用函数的返回值带回,如:

int fx(double a,double b,double* result){       flag= 1;       if(b == 0)           flag= 0;       else              *result = a/b;       return flag;}

 容易看出,若被除数为0,则返回0来表示运算出错,若返回1,则表示本次计算有效。

0x05 

关于指针与数组

我们可以使用指针来操作数组。那么指针和数组到底有什么关系呢?让我们先来看一段代码:

int a[3] = {1,2,3};printf(“%d”,*a);

上述代码的运行结果为“1”,这揭示了一个重要的事情:

重点:当我们直接把数组当成指针来用时,这个指针指向的是数组的第一个单元,即:a[0]

实际上,数组和指针还有更密切的关系,比如在函数的参数表中,我们可以完全将指针和数组进行替换:

void fx(int a[]);
void fx(*a);

以上两条声明完全等价,甚至在访问数组的某一个单元时,都可以使用指针来进行。如:

a[0] 与 *a 等价

a[1] 与 *(a+1)

a[2] 与 *(a+2)

a[x] 与 *(a+x)

除此之外,指针的许多运算都可以有效操作数组,如:++、--、+=、-=、+、-都可以用来进行同上的指针运算,他们都用来对数组的某一个单元进行访问。

其中,还有一个特殊的运算:两个指针相减,它用来计算所访问的两个单元之间相差多少个单元,如:

int a[3]={1,2,3};int *p1 = &a[0];int *p2 = &a[2];printf("%p",p2-p1); 

上述代码的执行结果为2,即:p2和p1之间相差两个单元

0x06 

关于*p++

*p++ 也是个普通的语句,跟上面的指针运算差不多,为啥要单独拿出来说呢?

因为它有一个很特殊的性质,让我们来慢慢解开它神秘的面纱

首先,++的优先级大于*的优先级,应该先进行++的运算

但是,++作为一个后置运算,是需要等到整条语句执行完毕后,再执行的

所以,它的实际作用效果是:将p所指向的那个数据取出来,然后将指针指向下一个位置

在计算机当中,*p++会直接被翻译成一句汇编指令,因此若能巧妙运用*p++,可以有效提高程序的运行效率

比方说,我们在遍历字符数组时,可以使用这种代码

char p[5] = {a,b,c,d,e};while(*p !=/0){       xxxxxxxxx;     //某个操作       *p++;}

这样就不必使用for语句了,省时又省力!

0x07 

关于指针与结构体

指针在结构体中的应用其实跟数组差不多,如果指针指向的是结构体数组,那么就把它当作数组,没有什么特殊的,指针运算那些都能照常使用。

至于指向结构体的指针,则在链表、树等数据结构中广泛运用,也没有什么特别的。

笔者主要是想在这里区分两个运算符:  .和->  的区别

(这里当时坑了笔者好久ヽ(*。>Д

许多人容易把.和->混用,网上有些文章甚至直接说.和->毫无区别,可以随便替换

.运算是成员访问运算符,用于访问结构体中的某一个成员

而->是指向成员运算符,虽然也是访问结构体中的某一个运算符,但两者有差异

下面我们用两张图来理解两者之间的差异

26388dd5ec5f30b3f1f2ca07e13d56d5.png.运算符时,直接访问结构体中的某一个成员如:

 stu.a

0e6d5bfdd8ca7801ddad2cb4ecab8f5e.png

->运算符时,是通过指针p来间接访问结构体中的某一个单元,如:

p->a

如果没有区别开这两种运算符,在编译时会报错

区分以上两个运算符后,容易得出:(*p).a和p->a是等价的

注:由于.运算符的优先级大于*运算符的优先级,所以(*p).a中必须要加上括号

0x08 

动态内存分配

有的时候,我们会碰上数组大小不够的问题,除了提前定义更大的数组,还可以给它“扩容”,手动给它分配内存,这种行为被称为“动态内存分配”。

本文主要介绍malloc() 和 free()   函数原型在头文件:stdlib.h中,所以使用前记得在头部加上

#include

malloc() 内存分配,它会返回无类型的指针变量(即:void*)比如:

int *p;p = (int *)malloc(400);

 执行上述语句后相当于分配了400个字节的内存空间并返回了void*型的指针,所以我们用(int *)进行强制类型转换,将其转换为int *类型的指针再赋给p。

但是,申请了内存后系统并不会自动回收这一块内存空间,需要手动再使用free将其释放。只需执行:

free(p);

这时,我们先前所申请的空间就会被释放。

随着程序越来越大,系统资源也会消耗殆尽,所以free空间一定要及时

一个编程好习惯:有借有还。只要向系统申请了内存空间,一定要在用完后及时将它归还。

0x09 

关于指针的安全性

C语言的指针是一把双刃剑,许多人选择C语言是因为它强大的指针,同样也有很多人不选择C语言是因为它危险的指针。接下来让我们看看C语言在程序中的潜在危险性。

1.指针指向数组时越界

当我们对数组外的单元进行操作时,就构成了数组越界。在使用指针操作数组时,由于使用者的操作不当和C语言本身的特性,非常容易造成这种指针指向的数组越界的情况。这种情况又称为缓冲区溢出

小知识:缓冲区溢出:缓冲区溢出攻击是利用缓冲区溢出漏洞所进行的攻击行动。缓冲区溢出是一种非常普遍、非常危险的漏洞,在各种操作系统、应用软件中广泛存在。利用缓冲区溢出攻击,可以导致程序运行失败、系统关机、重新启动等后果。第一个缓冲区溢出攻击就是有名的蠕虫病毒,发生在二十年前,它曾造成了全世界6000多台网络服务器瘫痪。

2.没有对象的野指针

所谓野指针,就是指针指向了一块随机的空间,不受程序的直接控制。

产生原因:

①在指针最初定义的时候,没有给他进行初始化(比如指直接int *p后,不让它指向任何的对象)。

②free或delete了指针后,实际上仅仅释放了它所指向的空间的内存,并没有销毁指针本身,所以产生了野指针。

③返回了已被内存已被回收的对象时。比如一个函数在作用完后内存被回收,但却仍然有某个指向该函数的指针,此时该指针沦为野指针。

野指针的问题在于,它平时存在的时候没有什么影响,但随着程序越来越复杂,某一天这个指针被某个程序调用时,就会产生未知的结果,严重时甚至会导致程序崩溃。这种问题一般很难被找出来,需要耗费大量精力来寻找。

0x0a 

后话

指针是C语言的精髓,希望大家能妥善的使用指针,让C语言最大化的为自己所用!

非常感谢你的阅读,喜欢记得加关注噢(●'◡'●)            D区

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

可以用malloc给结构体指针变量分配动态内存空间_指针——为你的C语言注入灵魂... 的相关文章

随机推荐