概括:主要说明双向链表的基本概念和具体操作以及源代码。

一、基本概念

1.有了单链表以后我们可以把内存中小块的空间联系在一起,并且把每一个小块都存储上我们想要存储的数值。但是单链表只有一个next,我们每一次都要从头开始遍历整个链表,这样的话如果我们对单链表进行逆序访问那么将是一项很耗时的操作。

2.双向链表解决了上面的问题,我们不单对每一个数据节点都设置一个next,同时还有一个pre指针,这样我们可以完成对链表的双向查找。

3.双向链表的结构示意图如下所示:

二、把单链表更改成为双向链表

1.首先更改链表中的header,链表中的header对应的是一个结构体,如下所示:

struct _tag_DLinkListNode
{
DLinkListNode* next;
DLinkListNode* pre;
};

在这里相对于单链表增加了一个pre指针,作前驱。

2.在create函数中初始化将header.pre以及clear函数中的header.next都设置为NULL。

ret->header.pre = NULL;

3.其他地方的操作还要注意在插入一个节点和删除一个节点函数中的改变。

三、双向链表的具体操作

1.双向链表的创建

DLinkList* DLinkList_Create()
{
TDLinkList* ret = (TDLinkList*)malloc(sizeof (TDLinkList));
if (ret != NULL)
{
ret->length = 0;
ret->slider = NULL;
ret->header.next = NULL;
ret->header.pre = NULL;
} return ret;
}

这里的TDLinkList* ret = (TDLinkList*)malloc(sizeof (TDLinkList));是为链表头获取空间,链表中其他数据节点的空间是在主函数中定义的,也就是插入链表时候由 结构体进行定义。

2.双向链表的清除、销毁、以及获取长度

这一部分要注意返回值的定义和返回值的类型。

3.双向链表的插入操作

在链表插入函数中,一共有四点需要注意。

(1)普通的插入方式,不是头插法也不是尾插法,只是单纯的把一个数据节点插入到链表中。基本操作示意图如下:

这样我们基本上使用四步操作就可以解决:

current->next = node;
node->pre = current;
next->pre = node;
node->pre = current;

(2)如果插入的节点是采用头插法,而且链表中已经存在其他元素,那么我们要进行判定,因为这个时候不可以再单纯的令current = node->pre,这个时候的node是第一个元素,所以它的pre应该指向NULL。

代码实现:

if (current == (DLinkListNode*)slist)
{
node->pre = NULL;
}

(3)当我们插入的节点是链表中的第0个位置,并且链表中没有其他元素,也就是链表的长度为0的时候,这个时候我们插入的元素的node->pre指向的current是头结点,显然不应该这样,也应该令node->pre = NULL,虽然老唐的判定是slist->length = 0但是我自己的判定current == (DLinkListNode*)slist已经包含了老唐的情况。

这种情况的代码实现:

if (current == (DLinkListNode*)slist)
{
node->pre = NULL;
}

(4)插入节点的第三种特殊情况:当我们从链表的尾部插入元素,这个时候current是倒数第一个节点,next是NULL,我们要插入的节点node在插入完成以后,node->next应该指向空。

这种情况下的代码实现:

if (next != NULL)
{
// node->next = next;
next->pre = node;
}

(5)对然第二种情况已经实现了slist->length == 0的情况,但是假如我们的slist->length == 0的时候,我们的游标并没有指向当前插入的元素,为了解决这个问题,代码实现如下:

if (slist->length == 0)
{
slist->slider = node;
}

插入函数的整体实现如下所示:

int DLinkList_Insert(DLinkList* list, DLinkListNode* node, int pos)
{
TDLinkList *slist = (TDLinkList*)list;
int ret = (slist != NULL);
ret = ret && (pos >= 0);
ret = ret && (node != NULL);
int i = 0;
if (ret)
{
DLinkListNode*current = (DLinkListNode*)slist;
DLinkListNode*next = NULL;
for (i = 0; (i < pos) && (current->next != NULL); i++)
{
current = current->next;
}
next = current->next; current->next = node; node->next = next;
if (next != NULL)
{
// node->next = next;
next->pre = node;
} node->pre = current;
if (current == (DLinkListNode*)slist)
{
node->pre = NULL;
} if (slist->length == 0)
{
slist->slider = node;
} slist->length++;
}
return ret;
}

4.双向链表的删除元素操作

删除操作除了常规的操作以外也存在一些特殊的情况。

(1)常规的删除链表中的一个元素,也就是在这个双向链表中的不是第一个元素,也不是最后元素,而且这个时候的双向链表已经有了一定的长度。如下图所示:

具体的代码实现如下:

current->next = next;
next->node = current;

(2)删除链表中的元素的第二种情况,我们要删除链表中的第0个元素,这个时候我们头结点的next被赋值为next(第1个节点),但是这个时候第一个节点应该变为第0个节点,而第0个节点指向的为header,所以这个时候next->pre = NULL。


代码实现:

current->next = next;
next->pre= NULL;

同时这种情况下要对next节点是否是为NULL 进行判定。

(3)删除了第0个元素以后,链表中不再有其他元素,也就是我们删除的元素是链表中的唯一节点,这个时候我们只需要将current->next = next,而并不需要进行next->pre =current,因为这个时候根本就不存在next->pre的情况了。

这里不再需要next->pre = current,具体的代码实现如下:

if (next != NULL)

我们这种情况下next = = NULL,所以我们不再指向if下面的代码。

(4)如果删除的链表中的最后一个节点,如果这个节点为空,那么只执行current->next = next,这个判定if (next != NULL)已经完成,不再执行有关next->pre的操作。

具体代码实现如下:

if (next != NULL)

删除函数的具体实现如下:

(5)如果我们要删除的节点恰好是游标现在所指向的元素,那么我们需要将游标指向next。

具体代码实现如下:

if (slist->slider == ret)
{
slist->slider = next;
}

删除函数的具体实现代码如下:

DLinkListNode* DLinkList_Delete(DLinkList* list, int pos)
{
TDLinkList* slist = (TDLinkList*)list;
DLinkListNode * ret = NULL;
int i = 0; if ((slist != NULL) && (pos >=0) && (pos < slist->length))
{
DLinkListNode* current = (DLinkListNode*)(slist);
DLinkListNode*next = NULL;
for (i = 0; i < pos; i++)
{
current = current->next;
}
ret = current->next; next->pre = current;*/
next = ret->next;
current->next = next; if (next != NULL)
{
next->pre = current;
if (current == (DLinkListNode*)slist)
{
current->next = NULL;
next->pre = NULL;
} } if (slist->slider == ret)
{
slist->slider = next;
} slist->length--; }
return ret;
}

四、测试程序以及游标

1.测试程序

(1)我们的插入方式如下:

 DLinkList_Insert(list, (DLinkListNode*)&v1, DLinkList_Length(list));
DLinkList_Insert(list, (DLinkListNode*)&v2, DLinkList_Length(list));
DLinkList_Insert(list, (DLinkListNode*)&v3, DLinkList_Length(list));
DLinkList_Insert(list, (DLinkListNode*)&v4, DLinkList_Length(list));
DLinkList_Insert(list, (DLinkListNode*)&v5, DLinkList_Length(list));

我们采用的尾插法,就是每一次插入一个元素都是从链表的尾部插入。

(2)我们在操作游标之前,不必要对游标进行复位,如果不对游标进行复位,那么我们采用尾插法将会把元素的游标挤到第一个位置,那么我们就可以正常操作游标了。

(3)如果我们采用头插法插入元素,插入方式如下:

DLinkList_Insert(list, (DLinkListNode*)&v1, 0);
DLinkList_Insert(list, (DLinkListNode*)&v2, 0);
DLinkList_Insert(list, (DLinkListNode*)&v3, 0);
DLinkList_Insert(list, (DLinkListNode*)&v4, 0);
DLinkList_Insert(list, (DLinkListNode*)&v5, 0);

(4)在我们进行游标操作之前,我们要对游标进行复位,因为头插法将会把游标顺序的挤到最后一个位置,这个时候如果我们朦胧的将游标再向后移动一个将会导致程序的崩溃,但是这个时候向前移动并不会出错。

五、源代码

1.双向链表实现.c文件

#include <stdio.h>
#include <stdlib.h>
#include "1.h" /************************************************************************
*这个结构体里定义的是链表头的信息,我们的链表操作和链表遍历都离不开链表头
************************************************************************/
typedef struct student
{
DLinkListNode header;
DLinkListNode *slider; //游标
int length;
}TDLinkList; /***********************************************************************************************
*函数名: DLinkList_Create
*参数:void
*返回值:DLinkList*类型,是一个void*类型,然后再由接收函数进行强制类型转换
*功能:创建链表,并返回链表头
***********************************************************************************************/
DLinkList* DLinkList_Create()
{
/*
为链表头获得空间,链表中其他数据节点的空间是在主函数中定义的,也就是插入链表时候由
结构体进行定义。
*/
TDLinkList* ret = (TDLinkList*)malloc(sizeof (TDLinkList));
if (ret != NULL)
{
ret->length = 0;
ret->slider = NULL;
ret->header.next = NULL;
ret->header.pre = NULL;
} return ret;
}
/***********************************************************************************************
*函数名: DLinkList_Destroy
*参数:DLinkList* list 传进来的是链表头
*返回值:void
*功能:销毁链表头
***********************************************************************************************/
void DLinkList_Destroy(DLinkList* list)
{
free(list);
}
/***********************************************************************************************
*函数名: DLinkList_Clear
*参数:DLinkList* list 传进来的是链表头
*返回值:void
*功能:清空链表,并把链表头信息清空
***********************************************************************************************/
void DLinkList_Clear(DLinkList* list)
{
TDLinkList *slist = (TDLinkList*)list;
if (slist != NULL)
{
slist->length = 0;
slist->header.next = NULL;
slist->header.pre = NULL;
slist->slider = NULL;
}
}
/***********************************************************************************************
*函数名: DLinkList_Length
*参数:DLinkList* list 传进来的是链表头
*返回值:int类型的整数
*功能:获得链表长度,并将链表的长度返回
***********************************************************************************************/
int DLinkList_Length(DLinkList* list)
{
/*首先给返回值赋初值,如果函数的返回值为-1,则证明链表并不存在*/
int ret = -1;
TDLinkList *slist = (TDLinkList*)list;
if (slist != NULL)
{
ret = slist->length;
}
return ret;
}
/***********************************************************************************************
*函数名: DLinkList_Insert
*参数:DLinkList* list 传进来的是链表头 DLinkListNode* node 要插入的数据节点,其实是我们
*实际要插入的数据节点的指针 int pos 要插入链表中的位置(注意这个是从0开始算起的)
*返回值:int类型的整数
*功能:如果插入元素成功返回1,否则返回其他。
***********************************************************************************************/
int DLinkList_Insert(DLinkList* list, DLinkListNode* node, int pos)
{
TDLinkList *slist = (TDLinkList*)list;
int ret = (slist != NULL);
ret = ret && (pos >= 0);
ret = ret && (node != NULL);
int i = 0;
if (ret)
{
DLinkListNode*current = (DLinkListNode*)slist;
DLinkListNode*next = NULL;
for (i = 0; (i < pos) && (current->next != NULL); i++)
{
current = current->next;
}
next = current->next; current->next = node; node->next = next;
if (next != NULL)
{
// node->next = next;
next->pre = node;
} node->pre = current;
if (current == (DLinkListNode*)slist)
{
node->pre = NULL;
} if (slist->length == 0)
{
slist->slider = node;
} slist->length++;
}
return ret;
} /***********************************************************************************************
*函数名: DLinkList_Get
*参数:DLinkList* list 传进来的是链表头 int pos 要插入链表中的位置(注意这个是从0开始算起的)
*返回值:DLinkListNode*类型 也就是返回的是一个链表的节点结构体指针
*功能:通过传进来的链表指针和位置,可以获得这个位置上的数据节点信息。
***********************************************************************************************/
DLinkListNode* DLinkList_Get(DLinkList* list, int pos)
{
TDLinkList* slist = (TDLinkList*)list;
DLinkListNode* ret = NULL;
int i = 0;
if ((slist != NULL)&& (pos >= 0) && (pos < slist->length))
{
DLinkListNode*current = (DLinkListNode*)slist;
//DLinkListNode*next = NULL;
for (i = 0; i < pos; i++)
{
current = current->next;
}
/*current永远都是我们要找的节点的前一个节点*/
ret = current->next;
}
return ret;
}
/***********************************************************************************************
*函数名: DLinkList_Delete
*参数:DLinkList* list 传进来的是链表头 int pos 要插入链表中的位置(注意这个是从0开始算起的)
*返回值:DLinkListNode*类型 也就是返回的是一个链表的节点结构体指针
*功能:通过传进来的链表指针和位置,可以获取删除指定位置上的元素,并对指定位置上的元素进行删除。
***********************************************************************************************/
DLinkListNode* DLinkList_Delete(DLinkList* list, int pos)
{
TDLinkList* slist = (TDLinkList*)list;
DLinkListNode * ret = NULL;
int i = 0; if ((slist != NULL) && (pos >=0) && (pos < slist->length))
{
DLinkListNode* current = (DLinkListNode*)(slist);
DLinkListNode*next = NULL;
for (i = 0; i < pos; i++)
{
current = current->next;
}
ret = current->next; next->pre = current;*/
next = ret->next;
current->next = next; if (next != NULL)
{
next->pre = current;
if (current == (DLinkListNode*)slist)
{
current->next = NULL;
next->pre = NULL;
} } if (slist->slider == ret)
{
slist->slider = next;
} slist->length--; }
return ret;
}
/***********************************************************************************************
*函数名: DLinkList_DeleteNode
*参数:DLinkList* list 传进来的是链表头 int pos 要插入链表中的位置(注意这个是从0开始算起的)
*返回值:DLinkListNode*类型 也就是返回的是一个链表的节点结构体指针
*功能:通过传进来的链表指针和位置,通过游标指向我们要删除的元素,然后调用DLinkList_Delete函数
进行删除。
***********************************************************************************************/
DLinkListNode* DLinkList_DeleteNode(DLinkList* list, DLinkListNode* node)
{
TDLinkList* slist = (TDLinkList*)list;
DLinkListNode * ret = NULL;
int i = 0;
if (slist != NULL)
{
DLinkListNode* current = (DLinkListNode*)(slist);
for (i = 0; i < slist->length; i++)
{
if (current->next == node)
{
ret = current->next;
break;
}
current = current->next;
} if (current != NULL)
{
DLinkList_Delete (list, i);
}
}
return ret;
} /***********************************************************************************************
*函数名: DLinkList_Reset
*参数:DLinkList* list 传进来的是链表头
*返回值:DLinkListNode*类型 也就是返回的是一个链表的节点结构体指针
*功能:通过传进来的链表指针将游标重新指向头结点所指向的下一个元素的位置,也就是所谓的游标复位。
进行删除。
***********************************************************************************************/
DLinkListNode* DLinkList_Reset(DLinkList* list)
{
TDLinkList* slist = (TDLinkList*)list;
DLinkListNode* ret = NULL;
if (slist != NULL)
{
slist->slider = slist->header.next;
ret = slist->slider;
}
return ret;
}
/***********************************************************************************************
*函数名: DLinkList_Current
*参数:DLinkList* list 传进来的是链表头
*返回值:DLinkListNode*类型 也就是返回的是一个链表的节点结构体指针
*功能:通过传进来的指针,找到游标当前指向的元素,并将这个当前元素返回。
***********************************************************************************************/
DLinkListNode* DLinkList_Current(DLinkList* list)
{
TDLinkList* slist = (TDLinkList*)list;
DLinkListNode* ret = NULL;
if (slist != NULL)
{
ret = slist->slider;
}
return ret;
} /***********************************************************************************************
*函数名: DLinkList_Next
*参数:DLinkList* list 传进来的是链表头
*返回值:DLinkListNode*类型 也就是返回的是一个链表的节点结构体指针
*功能:通过传进来的指针,找到游标指向前一个元素,并将这个前一个元素返回。
***********************************************************************************************/
DLinkListNode* DLinkList_Next(DLinkList* list)
{
TDLinkList* slist = (TDLinkList*)list;
DLinkListNode* ret = NULL; if( (slist != NULL) && (slist->slider != NULL) )
{
ret = slist->slider;
slist->slider = ret->next;
} return ret;
}
/***********************************************************************************************
*函数名: DLinkList_Pre
*参数:DLinkList* list 传进来的是链表头
*返回值:DLinkListNode*类型 也就是返回的是一个链表的节点结构体指针
*功能:通过传进来的指针,找到游标指向前一个元素,并将这个前一个元素返回。
***********************************************************************************************/
DLinkListNode* DLinkList_Pre(DLinkList* list)
{
TDLinkList* slist = (TDLinkList*)list;
DLinkListNode* ret = NULL;
if (slist != NULL && slist->slider != NULL)
{
slist->slider = slist->slider->pre;
ret = slist->slider;
}
return ret;
}

2.双向链表的头文件

#ifndef _1_H_
#define _1_H_ typedef void DLinkList;
typedef struct _tag_DLinkListNode DLinkListNode;
/*这个结构体是聊表头的一个成员*/
struct _tag_DLinkListNode
{
DLinkListNode* next;
DLinkListNode* pre;
}; DLinkList* DLinkList_Create(); void DLinkList_Destroy(DLinkList* list); void DLinkList_Clear(DLinkList* list); int DLinkList_Length(DLinkList* list); int DLinkList_Insert(DLinkList* list, DLinkListNode* node, int pos); DLinkListNode* DLinkList_Get(DLinkList* list, int pos); DLinkListNode* DLinkList_Delete(DLinkList* list, int pos); DLinkListNode* DLinkList_DeleteNode(DLinkList* list, DLinkListNode* node); DLinkListNode* DLinkList_Reset(DLinkList* list); DLinkListNode* DLinkList_Current(DLinkList* list); DLinkListNode* DLinkList_Next(DLinkList* list); DLinkListNode* DLinkList_Pre(DLinkList* list); #endif

3.测试程序

#include <stdio.h>
#include <stdlib.h>
#include "1.h"
/* run this program using the console pauser or add your own getch, system("pause") or input loop */ struct Value
{
DLinkListNode header;
int v;
}; int main(int argc, char *argv[])
{
int i = 0;
DLinkList* list = DLinkList_Create();
struct Value* pv = NULL;
struct Value v1;
struct Value v2;
struct Value v3;
struct Value v4;
struct Value v5; v1.v = 1;
v2.v = 2;
v3.v = 3;
v4.v = 4;
v5.v = 5; DLinkList_Insert(list, (DLinkListNode*)&v1, DLinkList_Length(list));
DLinkList_Insert(list, (DLinkListNode*)&v2, DLinkList_Length(list));
DLinkList_Insert(list, (DLinkListNode*)&v3, DLinkList_Length(list));
DLinkList_Insert(list, (DLinkListNode*)&v4, DLinkList_Length(list));
DLinkList_Insert(list, (DLinkListNode*)&v5, DLinkList_Length(list)); DLinkList_Insert(list, (DLinkListNode*)&v1, 0);
DLinkList_Insert(list, (DLinkListNode*)&v2, 0);
DLinkList_Insert(list, (DLinkListNode*)&v3, 0);
DLinkList_Insert(list, (DLinkListNode*)&v4, 0);
DLinkList_Insert(list, (DLinkListNode*)&v5, 0); for(i=0; i<DLinkList_Length(list); i++)
{
pv = (struct Value*)DLinkList_Get(list, i); printf("插入的元素为:%d\n", pv->v);
} printf("\n"); //DLinkList_Delete(list, 0);
//DLinkList_Delete (list)*/
for(i=0; i<DLinkList_Length(list); i++)
{
pv = (struct Value*)DLinkList_Next(list);
printf("%d\n", pv->v);
} printf("\n"); DLinkList_Reset(list);
DLinkList_Next(list); pv = (struct Value*)DLinkList_Current(list); printf("%d\n", pv->v); DLinkList_DeleteNode(list, (DLinkListNode*)pv); pv = (struct Value*)DLinkList_Current(list); printf("%d\n", pv->v); DLinkList_Pre(list); pv = (struct Value*)DLinkList_Current(list); printf("%d\n", pv->v); printf("Length: %d\n", DLinkList_Length(list)); DLinkList_Destroy(list); return 0;
}

C语言数据结构----双向链表的更多相关文章

  1. C++语言实现双向链表

    这篇文章是关于利用C++模板的方式实现的双向链表以及双向链表的基本操作,在之前的博文C语言实现双向链表中,已经给大家分析了双向链表的结构,并以图示的方式给大家解释了双向链表的基本操作.本篇文章利用C+ ...

  2. C语言实现双向链表

    目前我们所学到的链表,无论是动态链表还是静态链表,表中各节点中都只包含一个指针(游标),且都统一指向直接后继节点,通常称这类链表为单向链表(或单链表). 虽然使用单链表能 100% 解决逻辑关系为 & ...

  3. 用C语言把双向链表中的两个结点交换位置,考虑各种边界问题。

    用C语言把双向链表中的两个结点交换位置,考虑各种边界问题. [参考] http://blog.csdn.net/silangquan/article/details/18051675

  4. (js描述的)数据结构[双向链表](5)

    (js描述的)数据结构[双向链表](5) 一.单向链表的缺点 1.只能按顺序查找,即从上一个到下一个,不能反过来. 二.双向链表的优点 1.可以双向查找 三.双向链表的缺点 1.结构较单向链表复杂. ...

  5. 数据结构 双向链表 C语言实现

    dlist.h #ifndef __dList_H #define __dlist_H typedef int Item; typedef struct Node *PNode; typedef PN ...

  6. c语言数据结构复习

    1)线性表 //顺序存储下线性表的操作实现 #include <stdio.h> #include <stdlib.h> typedef int ElemType; /*线性表 ...

  7. 读谭浩强C语言数据结构有感(1)

    1.什么是数据结构? 数据结构,就是我们计算机内部的运算,编程语言的基础工作模式吧,个人总结的 = = !! 数据:说简单一点,就是计算机二进制机器码,然后通过一些复杂的操作,变为复杂的语言. 数据元 ...

  8. C语言数据结构----栈与递归

    本节主要说程序中的栈函数栈的关系以及栈和递归算法的关系. 一.函数调用时的栈 1.程序调用时的栈是也就是平时所说的函数栈是数据结构的一种应用,函数调用栈一般是从搞地质向低地址增长的,栈顶为内存的低地址 ...

  9. 第二章 R语言数据结构

    R语言存储数据的结构包括:标量.向量.矩阵.数组.数据框和列表:可以处理的数据类型包括:数值型.字符型.逻辑型.复数型和原生型. 数据结构 向量 向量是用来存储数值型.字符型或逻辑型数据的一维数组.单 ...

随机推荐

  1. ROS开发文档

    http://docs.ros.org/indigo/api/roscpp/html/classros_1_1NodeHandle.html#af0bf7baa0ab2385275bb657cc358 ...

  2. codinglife主题小修改和有意思的博客挂件

    这个主题很漂亮,不过为了迎合自己的喜好ヽ(•̀ω•́ )ゝ,修改了字号.阴影之类的小细节.同时下面还有我博客里面的两个有意思的小挂件,请向右边看(๑و•̀ω•́)و 1.主题修改:复制下面的css代码 ...

  3. 优秀的 Android Studio 插件

    转自:http://www.codeceo.com/article/8-android-studio-plugins.html Android Studio是目前Google官方设计的用于原生Andr ...

  4. 新浪sae 项目之 git 配置

    新浪sae 项目现在支持git 配置了,但是有好多人配置不成功.下面对这个问题进行一个总结. 1. 在新浪云上面新建项目(该步骤省略) 2. 一般新建完毕后,会让你选择代码的管理工具,如下 注意这里, ...

  5. ASP.NET MVC 项目分离

    ASP.NET MVC 项目分离 说明: ZRT.Web 是前台网站,目录[D:\ZRT.Web\] ZRT.Admin 是后台管理,目录[D:\ZRT.Web\Applications\Admin\ ...

  6. QT显示如何减轻闪屏(双缓冲和NoErase)

    很多同志在些QT 程序后会遇见闪屏的问题, 有时速度非常快,但毕竟影响了显示效果,如何做到减轻屏幕抖动或闪屏呢?我曾试过如下的办法:1.使用双缓冲. 比如我们在一个Widget里面绘多个图的话, 先创 ...

  7. 多线程中Local Store Slot(本地存储槽)

    在Java中有一种ThreadLocal机制,为每一个使用该变量的线程都提供一个变量值的副本,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突.从线程的角度看,就好像每一个线程都完全 ...

  8. POJ 2758 Checking the Text(Hash+二分答案)

    [题目链接] http://poj.org/problem?id=2758 [题目大意] 给出一个字符串,支持两个操作,在任意位置插入一个字符串,或者查询两个位置往后的最长公共前缀,注意查询的时候是原 ...

  9. OOP中的多态

    尽管一直在说OOP,但说实话还不是真正的理解,面向对象的三个基本特性继承.封装.多态,前两个性质曾经 有接触听的比較多还好理解,以下主要介绍一下第三个特性--多态. 1. 定义     同一操作作用于 ...

  10. Objective-C中NSArray和NSMutableArray的基本用法

    /*---------------------NSArray---------------------------*/ //创建数组 NSArray *array1 = [NSArray arrayW ...