链表所遇问题

这个板块只要针对个人所遇到的一些问题进行总结和梳理.   如果遇到相同困惑的朋友也可以看下~



Question 1   结构类型放在.h文件与.c文件的区别

(PS: 是指结构类型的定义, 而非声明--- 具体区别见: http://jingyan.baidu.com/article/020278118e51e01bcd9ce576.html)

情形1 :  将结构类型(struct) 放在.h文件中

头文件 structtst.h

#ifndef _Stest_H_
#define _Stest_H_

int a;
int b;
struct test
{
    int a;
    int b;
};

#endif

测试主函数

#include<stdio.h>
#include "structtst.h"

int main()
{
    struct test tst;
    tst.a=10;
    tst.b=20;
    a=30;
    b=40;
    printf("tst.a=%d, tst.b=%d\n",tst.a,tst.b);
    printf("a=%d, b=%d\n",a,b);
}

运行结果如下

从而可见:  当将结构类型放在头文件中, 主函数包含该头文件则可以直接试用结构类型(即可以进行声明和定义).

情形2: 将结构类型放在另一个.c文件而非主函数中

头文件 structtst.h

#ifndef _Stest_H_
#define _Stest_H_

int a;
int b;
struct test;
typedef struct test *tst;
struct test *GetStruct();

#endif

非主函数的.c文件

#include<stdio.h>
#include<stdlib.h>

struct test
{
    int a;
    int b;
};

struct test *GetStruct()
{
    struct test *tst;
    tst=malloc(sizeof(struct test));
    tst->a=10;
    tst->b=20;
    return tst;
}

主函数测试

测试1 :

#include<stdio.h>
#include "structtst.h"

int main()
{
    struct test t;
}

提示错误如下: 

测试2:

#include<stdio.h>
#include "structtst.h"

int main()
{
    tst t;
    t=GetStruct();
    t->a=10;
}

提示错误如下:

(PS:  当去掉 t->a=10 这句则正确)

总结:  从上述几种错误可以看出这种方式 将失去对  struct的"主导权", 只能将与 struct内的内容全部在定义其的.c中显示,在返回---即实际权利全都在此.c文件!

(总感觉此处还是存在挺多不正确之处,  发现望指出!   此外,可以参考看下 http://stackoverflow.com/questions/6316987/should-struct-definitions-go-in-h-or-c-file )

Question 2 malloc和free的使用

(此处主要来自: http://www.cnblogs.com/hanyonglu/archive/2011/04/28/2031271.html)

函数原型:

void *malloc(size_t size);  ----给函数分配size个字节,返回指向这块内存的指针. 若分配失败,则返回NULL

(ps : malloc 函数返回的是 void * 类型。对于C++,如果你写成:p = malloc (sizeof(int)); 则程序无法通过编译,报错:“不能将 void* 赋值给 int * 类型变量”。所以必须通过 (int *) 来将强制转换。而对于C,没有这个要求,但为了使C程序更方便的移植到C++中来,建议养成强制转换的习惯。)

void free(void *memblock); ---memblock: 释放之前分配的内存块, free 函数解除分配之前通过调用calloc、malloc或者 realloc 分配的存储区 (memblock)。

函数使用的注意事项:

A、申请了内存空间后,必须检查是否分配成功。

B、当不需要再使用申请的内存时,记得释放;释放后应该把指向这块内存的指针指向NULL,防止程序后面不小心使用了它。

C、这两个函数应该是配对。如果申请后不释放就是内存泄露;如果无故释放那就是什么也没有做。释放只能一次,如果释放两次及两次以上会出现错误(释放空指针例外,释放空指针其实也等于啥也没做,所以释放空指针释放多少次都没有问题)。

D、虽然malloc()函数的类型是(void *),任何类型的指针都可以转换成(void *),但是最好还是在前面进行强制类型转换,因为这样可以躲过一些编译器的检查。

几个关键问题

  • malloc()到 底从哪里得到了内存空间?

   答案是从堆里面获得空间。也就是说函数返回的指针是指向堆里面的一块内存。操作系统中有一个记录空闲内存地址的链表。当操作系统 收到程序的申请时,就会遍历该链表,然后就寻找第一个空间大于所申请空间的堆结点,然后就将该结点从空闲结点链表中删除,并将该结点的空间分配给程序。

  • 在使用malloc()分配内存空间后,一定要记得释放内存空间,否则就会出现内存泄漏
  • free()到底释放了什么

  free()释 放的是指针指向的内存!注意!释放的是内存,不是指针!指针并没有被释 放,指针仍然指向原来的存储空间。指针是一个变量,只有程序结束时才被销毁。释放了内存空间后,原来指向这块空间的指针还是存在!只不过现在指针指向的内 容的垃圾,是未定义的,所以说是垃圾。因此,释放内存后把指针指向NULL,防止指针在后面不小心又被解引用了。

两函数的机制

事实上,仔细看一下free()的函数原型,也许也会发现似乎很神奇,free()函数非常简单,只有一个参数,只要把指向申请空间的指针传递给free()中的参数就可以完成释放工作!这里要追踪到malloc()的申请问题了。申请的时候实际上占用的内存要比申请的大。因为超出的空间是用来记录对这块内存的管理信息。

大多数实现所分配的存储空间比所要求的要稍大一些,额外的空间用来记录管理信息——分配块的长度,指向下一个分配块的指针等等。这就意味着如果写过一个已分配区的尾端,则会改写后一块的管理信息。这种类型的错误是灾难性的,但是因为这种错误不会很快就暴露出来,所以也就很难发现。将指向分配块的指针向后移动也可能会改写本块的管理信息。

malloc()申请的空间实际就是分了两个不同性质的空间。一个就是用来记录管理信息的空间,另外一个就是可用空间了。而用来记录管理信息的实际上是一个结构体。在C语言中,经常用结构来记录信息!下面看看这个结构体的原型:

程序代码

struct mem_control_block
{
      int is_available;    //一般来说应该是一个可用空间的首地址,但这里英文单词却显示出空间是否可用的一个标记
      int size;            //这是实际空间的大小
};

所以,free()就是根据这个结构体的信息来释放malloc()申请的空间!而结构体的两个成员的大小我想应该是操作系统的事了,

下面看看free()的源代码

    void free(void *ptr)
    {
            struct mem_control_block *free;
            free = ptr - sizeof(struct mem_control_block);
            free->is_available = 1;
            return;
    }

malloc源码见:  http://blog.csdn.net/zhongjiekangping/article/details/6756907

时间: 2024-10-29 02:46:07

链表所遇问题的相关文章

从文件读数据插入到链表

前两周做了一个小作业,学生成绩管理系统,第一周实现了录入学生信息.删除学生信息.显示学生信息和按照学生平均成绩排序的功能,总体来说比较顺利,第二周只做了一件事就是读txt文件中的学生信息,将txt文件中的学生信息读到程序中插入到链表中,这一个看似简单的工作,花费了一周的时间. 我程序中用到的txt文件中的数据是学生成绩,其中有学生ID.学生姓名.成绩等,在链表中分别是int型,字符串和float型,如下所示. 因为之前学习了I/O文件的操作,没有学习基于流的I/O,所以一开始就是瞎撞,根本不知道

leetcode--Linked List Cycle--判断链表是否有环

Given a linked list, determine if it has a cycle in it. Follow up: Can you solve it without using extra space? 这个题目用快慢指针来做,重点在于代码怎么实现的简洁方便理解. 这里用快指针来判断链表是不是有NULL,没有NULL那再继续走,看是否能与慢指针遇上 class Solution { public: bool hasCycle(ListNode *head) { ListNode

深入浅出数据结构C语言版(4)——表与链表

在我们谈论本文具体内容之前,我们首先要说明一些事情.在现实生活中我们所说的"表"往往是二维的,比如课程表,就有行和列,成绩表也是有行和列.但是在数据结构,或者说我们本文讨论的范围内,我们所说的"表"是一维的,即所有"元素"都是前后排列的.就我个人而言,这样的"表"用"队列"来形容比较恰当.但是,数据结构中"队列"这个名词是被一种特殊的"表"给占用了的,所以我们没法再用

Linked List Cycle leetcode java (链表检测环)

题目: Given a linked list, determine if it has a cycle in it. Follow up: Can you solve it without using extra space? 题解: 这道题连带着II是很经典的,在看CC150时候,纠结这个问题纠结了很久.在读了很多网上资料还有书的讲解以及和别人讨论之后,对这个专题终于明白了. 这一问只需要判断链表是否有环. 当链表没有环时是很好判断的,让一个指针一直往后走,遇见null了自然就没有环. 而如

待字闺中之单链表和之恋

题目来源,待字闺中,原创@陈利人 ,欢迎大家继续关注微信公众账号"待字闺中" 原题:两个单链表(singly linked list),每一个节点里面一个0-9的数字,输入就相当于两个大数了.然后返回这两个数的和(一个新list).这两个输入的list长度相等. 要求是:不用递归:要求算法在最好的情况下,只遍历两个list一次 ,最差的情况下两遍 分析:如果链表表示的数是从低到高位表示的,则很简单:如果是从高到低,问题就复杂了,进位是万恶之源. 两个单链表,每个节点存储一个0-9的数字

单链表(增、删、查找)

     用结构体构建单链表,实现单链表的基本功能. //头文件 #pragma once #include<stdio.h> #include<assert.h> #include<malloc.h> typedef int DataType; typedef struct ListNode { DataType _data; struct ListNode *_next; }ListNode; //初始化 void InitList(ListNode **ppHea

寻找带环的链表的柄长

试题:给定一个带环的链表,找出环起点.比如:A -> B -> C -> D -> E -> C (C为环形起点)写一个程序找出环起点C. ListNode结构如下,请实现 ListNode* find_circle_beginning(ListNode* head);函数,返回环的起点. struct ListNode { char val; ListNode* next; }; 答案: 1.先用快指针(每次走两步)和慢指针(每次走一步),遍历链表,当两个指针相遇时,说明该

判断链表是否带环,以及环的入口

给出一个链表,先判断链表是否带环,如果带环,求出环的入口. 判断是否带环:用快慢指针.快指针每走两步,慢指针走一步,如果两者在某个点处相 遇,则链表带环. 下边给出函数的实现代码: typedef struct LinkNode { DataType data; struct LinkNode *next; }LinkNode,*pLinkNode; typedef struct LinkList { LinkNode *pHead; }LinkList,*pLinkList; pLinkNod

【数据类型】链表那些事儿

大家好(????)?",今天本蒟蒻要和大家讲讲链表的那些事儿~~~ 首先可以直接的告诉大家一件事情: 指针是程序设计中最TMD难理解也是最TMD有难度的部分,没有之一! 这使的指针和图论.数论成为了各位Oier肩上的三座大山. 而链表又是指针当中的精华,我们一起来了解一下 写在前面 本蒟蒻学习链表时内心受到了无限的伤害 QAQQAQ ,希望大家不要重蹈我的覆辙 qwqqwq 链表的基础知识 链表是一个动态的存储空间,在开始前无需考虑空间大小的问题,优点是删除或添加时方便,省内存,缺点是查找引用困