m0_73928344 2023-05-16 12:19 采纳率: 70%
浏览 20

c语言链表问题答疑解惑

img

#include<stdio.h>
#include<malloc.h>
struct linknode
{
    int a;
    struct linknode *next;
};
struct linknode *creatnode()
{
    struct linknode *p;
    p=(struct linknode *)malloc(sizeof(struct linknode));
    int a;
    scanf("%d",&a);
    p->a;
    p->next=NULL;
    return p;
}
void insertfirst(struct linknode *head,struct linknode *p)
{
    struct linknode *p1;
    if(head==NULL)
    {
        head=p;
    }
    else
    {
        p1=head;
        head=p;
        p->next=p1;
    }
}
void insertlast(struct linknode *head,struct linknode *p)
{
    struct linknode *p1,*p2;
    int a;
    p1=head;
    while(p1->next!=NULL)
    {
        p2=p1;
        p1=p2->next;
    }
    p1->next=p;
}
void displink(struct linknode *head)
{
    struct linknode *py;
    py=head;
    while(py!=NULL)
    {
        printf("%d ",py->a);
        py=py->next;
    }
}
void freelink(struct linknode *head)
{
    struct linknode *node;
    while(head!=NULL)
    {
        node=head->next;
        free(head);
        head=node;
    }
}
int main()
{
    int n;
    scanf("%d",&n);
    struct linknode *head,*p;
    head=creatnode();
    int i;
    for(i=2;i<=n;i++)
    {
        p=creatnode();
        if(i%2!=0)
        {
            insertfirst(head,p);
        }
        else
        {
            insertlast(head,p);
        }
    }
    displink(head);
    freelink(head);
    return 0;
}

请问这是个啥情况

  • 写回答

1条回答 默认 最新

  • CSDN-Ada助手 CSDN-AI 官方账号 2023-05-16 14:17
    关注
    • 这个问题的回答你可以参考下: https://ask.csdn.net/questions/7546373
    • 我还给你找了一篇非常好的博客,你可以看看是否有帮助,链接:c语言使用递归思想解决扩散问题实例
    • 同时,你还可以查看手册:c语言-声明 中的内容
    • 除此之外, 这篇博客: C语言进阶第七篇【动态存储和柔性数组】中的 部分也许能够解决你的问题, 你可以仔细阅读以下内容或跳转源博客中阅读:
    • 如果我们直接使用realloc函数,那么才开始起始地址为NULL;后面40就是开辟空间的大小!效果不就和malloc直接开辟40个字节的大小一样?所以这里的功能就类似于malloc,就是在堆区开辟40个字节!

      我们开辟一个很大的空间是有可能开辟失败的!在开辟后并没有进行判空处理,直接使用,这就有可能因为开辟空间失败(返回空指针NULL)造成对空指针的解应用操作! 

      我们动态开辟空间是以字节为单位的,开辟的是40个字节,实际上就是10个整型;使用时用40个字节就会造成越界访问异常! 

      我们静态创建的数组,实际上就是在栈区上创建的;不需要free来释放,栈区间里的变量除了这个函数就会被销毁!只有malloc等动态创建,在堆区间上创建的,才需要free!

      释放内存的一部分,什么意思呢?我们发现*p++说明指针一直往后走的 ;p指向的就不是起始地址,此时释放p就是释放p的一部分,并没有把p的地址完全释放掉!

      这个问题一般会出现在写项目里,我们已经在函数里释放过了,结果出了函数又释放一次;这就造成了多次释放,也会使编译器出现问题!但是如果我们释放过后,立马进行置空处理(p = NULL)再二次释放就不会有问题!

      这种错误就很严重了!我们总是申请不释放:就会造成内存泄漏,导致死机卡顿 ;比如一个常见的现象:我们的服务器一般是不关机的,一直在跑;如果我们在跑的东西一直在申请空间确没有释放,过段时间就会使服务器宕机;然后重启后就会好了!

      补充:动态开辟的空间,2种回收方式:(1)主动free        (2)程序结束

      问题1: 

      str传给GetMemory函数的时候是值传递,所以GetMemory函数的形参p是str的一份临时拷贝,在GetMemory函数内部动态申请空间的地址,存放在p中,不会影响外边str,所以当GetMemory函数返回之后,str依然是NULL。所以strcpy拷贝会失败。

      问题2: 

      没有free释放,当GetMemory函数返回之后,形参p销毁,使得动态开辟的100个字节存在内存泄漏。

      我们就改成传址调用,利用二级指针接收!这样对形参的改变就会影响实参,此时把动态申请的空间交给指针p,其实就是给str!最后用完还要进行释放!

      不用传址调用怎么办呢?这就需要利用return指针返回来,这时我们还是用一级指针接收!因为是动态开辟的空间实际上是在堆区上开辟的,需要手动才销毁;我们把这个地址利用return带回来,然后在利用一个指针就接收就可以了!

      比如:这种方法就可以应用在带头循环双链表中给头结点开辟空间,就可以利用return返回值的方式,避免使用二级指针!

      问题: 

      同样是利用return返回值的方式,这里为什么就不行了呢?GetMemory函数内部创建的数组是在栈区上创建的,出了函数,p数组的空间就还给了操作系统,返回的地址是没有实际意义的,如果通过返回的地址,去访问内存就是非法访问内存!

       问题:

      看着好像是没什么问题,是传址调用,二级指针接收;主要就是,没有释放空间!free(str),str = NULL!

      问题:

      str已经释放了,下面就不能在访问,在访问就属于非法访问内存;为了避免这种错误发生:free过后一定要手动置空 str=NULL

      问题: 

      (1)x是局部变量,在栈区上创建,出了这个范围,就把内存释放了,return(&x)没有实际意义!
      (2)指针ptr才开始没有初始化,是野指针,里面放的随机值!

      解析: 

      (1)对于#define定义符号,相当于替换,所以是int*a,b===>*说明a是指针,类型是整型;对于b只能说明是整型!所以如果我们要是一下子定义两个指针int*a,*b要写成这种形式。
      (2)typedef是类型重定义,代表着一种类型;把int*起个别名叫int_ptr;所以c,d都是指针类型

      C/C++程序内存分配的几个区域:
      1. 栈区(stack):在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。 栈区主要存放运行函数而分配的局部变量、函数参数、返回数据、返回地址等。
      2. 堆区(heap):一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。分配方式类似于链表。
      3. 数据段(静态区)(static)存放全局变量、静态数据。程序结束后由系统释放。
      4. 代码段:存放函数体(类成员函数和全局函数)的二进制代码。
      补充:static关键字修饰局部变量的例子解释
      实际上普通的局部变量是在栈区分配空间的,栈区的特点是在上面创建的变量出了作用域就销毁。但是被static修饰的变量存放在数据段(静态区),数据段的特点是在上面创建的变量,直到程序结束才销毁所以生命周期变长1

      C99 中,结构中的最后一个元素允许是未知大小的数组,这就叫做『柔性数组』成员。

      (1)结构中的柔性数组成员前面必须至少一个其他成员

      (2)sizeof 返回的这种结构大小不包括柔性数组的内存
      (3)包含柔性数组成员的结构用malloc ()函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小。

      (4)柔型数组开辟的空间都是在堆上开辟的!

       (1)对于柔型数组的动态开辟空间,是用结构体指针(struct S* ps)进行接收;对于大小主要包括两部分:

              一是n的大小创建,我们就用sizeof(struct S),这算的就是除柔性数组以外的大小;

              二是柔型数组的动态开辟;其实就是开辟10个整型,我们就用10*sizeof(int);

      (2)成功开辟后就使用,通过指针ps来查看内存发现已经被赋值;

      (3)用完了,我们就用realloc进行扩容,这也就体现了柔性数组的特点,大小是可变的;我们用一个新的指针ptr进行接收,扩容成功后;把新指针ptr赋值给旧指针ps;

      (4)最后在进行ps指针的释放,free(ps),ps = NULL ;

      为了保证和柔性数组一样的性质,都是在堆上开辟的!我们所有的都需要用malloc开辟空间;柔型数组是一个malloc整体一块开辟的;用指针代替柔性数组,就要分开开辟:一个开辟除指针以外的大小的而空间;一个开辟指针所需大小的空间!

       (1)利用指针代替柔型数组;实际上就是从一步到两步的过程;柔性数组只需要一次malloc;而指针形式就需要两次malloc;两次malloc就需要两次free;

      (2)第一次malloc是动态创建除指针以外所需的空间;第二次malloc是动态创建指针所需大小的空间;

      (3)成功开辟后就使用,通过指针ps->arr来查看内存发现已经被赋值;

      (4)用完了,我们就用realloc进行扩容,我们就只需要对指针部分进行扩容;我们用一个新的指针ptr进行接收,扩容成功后;把新指针ptr赋值给旧指针ps->arr;

      (5)最后在进行ps->arr指针和ps指针的释放,free(ps->arr),ps->arr = NULL ;free(ps),ps = NULL

    • 您还可以看一下 朱有鹏老师的C语言位操作视频精讲-C语言专题第二部分课程中的 4.2.3.如何用位运算构建特定二进制数小节, 巩固相关知识点
    评论

报告相同问题?

问题事件

  • 已采纳回答 5月16日
  • 创建了问题 5月16日

悬赏问题

  • ¥15 代码在keil5里变成了这样怎么办啊,文件图像也变了,
  • ¥20 Ue4.26打包win64bit报错,如何解决?(语言-c++)
  • ¥15 clousx6整点报时指令怎么写
  • ¥30 远程帮我安装软件及库文件
  • ¥15 关于#自动化#的问题:如何通过电脑控制多相机同步拍照或摄影(相机或者摄影模组数量大于60),并将所有采集的照片或视频以一定编码规则存放至规定电脑文件夹内
  • ¥20 深信服vpn-2050这台设备如何配置才能成功联网?
  • ¥15 Arduino的wifi连接,如何关闭低功耗模式?
  • ¥15 Android studio 无法定位adb是什么问题?
  • ¥15 C#连接不上服务器,
  • ¥15 angular项目错误