【C语言】文科普通211如何转行做开发

    技术2024-12-30  10

    关于链表的碎碎念

    【C语言】自力更生谢谢学习C语言第11天20200703-链表详解(一)学习C语言第12天@20200704-链表详解逐条分析(二)

    【C语言】自力更生谢谢

    文科生,本科与硕士都是商科211,前几个月还在券商实习(债券承做),现在接触了一个我以前没了解过的领域,C语言,然后深深被它吸引。在接触C语言之前,由于学术研究的需要,我也学习过Python与R语言的粗浅知识。但当时均没有感受到此时学习C语言的快乐。 所以我开始记录自己学习的一个路线图。因为相对于专业出身(比如计算机及相关专业)的朋友们而言,我这个属于盆地起步。但是,也许是年轻给了我一点勇气,让我开始不再顾及其他。 今天看了启舰老师的文章,https://blog.csdn.net/harvic880925/article/details/102774769 我觉得深有感悟。认为学校、经验都非常重要。然而我的专业(工商管理)与学校(上海某211)估计对我求职是不能帮什么忙。但是我不是一个人在奋斗,很多人都不是一开始就清楚自己喜欢干什么,能够为什么事物奉献青春,只要明白的那一刻开始行动,我就是对的,我认为这没问题。 在自学的过程中,我会努力坚持做博客。希望后面能认识一些朋友一起进步,比如一些像我一样不是计算机专业,但是后期意识到自己很喜欢计算机的人嘻嘻。

    学习C语言第11天20200703-链表详解(一)

    C,C++,计算机网络结构。我觉得要入门可以先系统学习这几门课,因为大学课程也都是浅浅掠过所以不用以大学模式来进行,按自己的进度来走。C语言学习第11天,完成指针、链表初步学习。

    先来说一下前几周学习C语言的感受: 因为我现在这么菜并没有真知灼见,就谈一下感受。有两个内容非常非常有意思,一是递归,二是链表。 以前学经管模型更适用于长期与模糊问题的研究,也就是一般理论的研究,我没有太大的兴趣。。。。反之编程就可以更灵活地模拟实际情况,模拟一些没那么可能看到的情况。递归就是很好的例子,可以从始至终地输出问题的结果,每一步跟着特定的参数,中间添加或删减变量。还可以模拟如永远无法得出结果的问题、以及极端情况。

    然后另外一个是链表。这个也是大把人写博客,我就总结一下自己的想法。 链表的作用主要在于节约内存,只要会用连接节点的方法,基本上可以搭建任何种类的链表,来代替已有的结构。

    附上MOOC北京理工大学C语言程序设计(下)的代码。

    #include<stdio.h> #include<stdlib.h> struct node { char name[20],address[20],phone[15]; struct node * link; /*定义node型结构指针 */ }; /* 定义结构 */ typedef node NODE; /* 定义结点类型 */ main() { NODE * head ; NODE * p; p = ( NODE * ) malloc ( sizeof (NODE) ) ; /* 开辟新存储区,申请表头节点 */ p->link = NULL; /* 将表头节点的link置为NULL*/ head = p; int create ( NODE * head , int n ); create ( head , 2 ); int insert_node ( NODE * head , NODE * p , int i ); p = ( NODE * ) malloc ( sizeof (NODE) ) ; gets(p->name); insert_node(head, p, 1); int output ( NODE * head ); output(head); int delete_node ( NODE * head , int i ); delete_node(head,2); output(head); getchar(); } int create ( NODE * head , int n ) { NODE * p; for ( ; n>0 ; n-- ) { p = ( NODE * ) malloc ( sizeof(NODE)) ; gets ( p->name); p->link = head->link; head->link = p; } return 0; } int output ( NODE * head ) { NODE * p; p = head->link; /* p 指向第一个数据节点 */ while ( p!=NULL ) { puts ( p->name ) ; /* 输出 p 所指向节点的数据 */ p = p->link ; /* p 指向下一个数据节点 */ } return 0; } int insert_node ( NODE * head , NODE * p , int i ) { NODE * q; int n=0; for ( q = head; n<i && q->link!=NULL; ++n ) q = q->link; /* ① 定位 */ p->link = q->link; /* ② 链接后面指针 */ q->link = p; /* ③ 链接前面指针 */ return 0; } int delete_node ( NODE * head , int i ) { NODE * q, * p; int n; for ( n=0, q = head; n<i-1 && q->link!=NULL; ++n ) q = q->link; /* ① 定位 */ if ( i>0 && q->link != NULL ) { p = q->link; /* p 指向被删除节点 */ q->link = p->link; /* ② 摘链 */ free ( p ); /* ③ 释放 p节点 */ } }

    (此代码来源于中国大学MOOC北京理工大学课程) (此图借鉴于中国大学MOOC北京理工大学课程)

    从上图可以看到节点中,p作为指针起到一个搭桥的作用。每次新创建一个节点都会有一个相应的指针p出现,创建完了这个p就没用了。与此同时留下的是一个NODE类型的结构,里面带有数据域(比如放一个name),以及指针域。在这个代码示例中指针域用 *link,有的地方用 *next,本质上都是一样的,都是用来指向下一个指针。

    如果还觉得理解不了,可以这么想。head和p分别指向一个结构体的地址,而head->link,其实就是head所指的这一个NODE结构体中的指针域,所指向的下一个结构体的地址。p->link指的就是指针p所指的NODE结构体中的指针域,所指向的下一个结构体的地址。

    学习C语言第12天@20200704-链表详解逐条分析(二)

    本人立志不肝,虽盆地起步,仍该睡就睡,今天继续完善链表的话题,我们来逐字逐句分析上面这串代码(初学者的苦,大佬们不懂),这个MOOC视频里说得非常简略,我听的时候是不懂的,所以才有必要这样分析。我后面分享的话题都会按照自己一下子听不懂的来走。

    (1)创建结构

    struct node { char name[20],address[20],phone[15]; struct node * link; /*定义node型结构指针 */ }; /* 定义结构 */ typedef node NODE; /* 定义结点类型 */

    这个简单,插入一个名为node的结构,其中指针域名为link,指向node型结构,以后建立的节点的数据域都是按这个格式来的。然后用typedef来命名node结构为NODE,可能是为了配合其他语言吧,因为不这么做也可以。

    (2)创建不含数据的空链表

    NODE * head ; NODE * p; p = ( NODE * ) malloc ( sizeof (NODE) ) ; /* 开辟新存储区,申请表头节点 */ p->link = NULL; /* 将表头节点的link置为NULL*/ head = p;

    这个就是我给的结构图上面说的不含数据的空链表,是一个创建链表的初始环节。p是指向初始节点地址的指针,p->link=NULL说明这个结构体中的指针域没有下家。head=p是说head所指的地址和p是一样的。

    (3)创建链表框架

    int create ( NODE * head , int n ); create ( head , 2 );

    这里需要和我们自己定义的create函数的代码结合起来看。这个函数是用来建立这个单链链表的框架的,输入为头指针以及需要的节点的个数。这里函数也可以定义成void格式,如果是void就要把return 0删掉。

    int create ( NODE * head , int n ) { NODE * p; for ( ; n>0 ; n-- ) { p = ( NODE * ) malloc ( sizeof(NODE)) ; gets ( p->name); p->link = head->link; head->link = p; } return 0; }

    这个就是定义create函数的代码。建立一个在函数内部用的p指针,前面提到过,这个p只是用来搭桥的,所以可以在函数内部定义,函数运行完就扔。 然后来一个for循环,这样你想建几个节点就建几个。p = ( NODE * ) malloc ( sizeof(NODE))是用来创建节点的。*malloc是在<stdlib.h>下的一个,指向既定大小内存空间的指针,用来创建一个节点。内存大小是malloc函数的变量,sizeof(NODE)就是根据我们定义的NODE结构需要的内存。前面( NODE*)就是将malloc创建的指针转化成一个指向NODE结构的指针(就是经常被提到的强制转化)。 合起来理解为,p = ( NODE * ) malloc ( sizeof(NODE))让我们有了一个指向有NODE结构空间的指针。(我是不是有点啰嗦?) 然后就可以在这个空间里增加数据。比如这里很简单第加了条name数据,从(1)可以知道name是一个长度为20的字符串,所以我们按顺序输入字符串就好了。与此同时也可以加两条语句,比如gets(p->address); gets(p->phone); 或者修改(1)中的结构,我们就能够拥有存储了更丰富数据的链表。

    然后**p->link=head->link;**这个其实很不好理解,会者不难难者不会,老师就讲三句话我真滴不懂。不过其实自己再思索一下还是可以理解的。使p指向的结构体中的指针域的指向,与head指向的结构体中的指针域的指向相同。意思就是让新建立的节点的指针指向head所指的结构体的指针指向的那个结构体(头部节点指向的节点)。下面head->link = p修改了head所指的结构体的指针的指向,使其指向指针p。按照我下面这个步骤对照图来理解,我图上画斜线和叉叉的部分也是有意义的(除了左上角画错了)。

    第一步:p = ( NODE * ) malloc ( sizeof(NODE));创建第一个节点 第二步:gets ( p->name);输入数据 第三步:p->link = NULL ( head->link = NULL) ;第一个节点链接到无 第四步:head->link = p; 表头节点链接到新节点 第五步:p = ( NODE * ) malloc ( sizeof(NODE)); 创建第二个节点 第六步:gets ( p->name);输入数据 第七步:p->link = head->link; 第二个节点链接到表头节点后面那个节点 第八步:head->link = p; 表头节点链接得到新节点 …

    从此以后,你想设置n多大都行,这个程序会逐步在表头后面建立新的节点,放入你输入的数据。但是有可能输出的时候就是你最后输入的最先输出,后进先出。有具体需求再改源代码把,我们先理解这个思想。

    (4)插入节点

    int insert_node ( NODE * head , NODE * p , int i ); p = ( NODE * ) malloc ( sizeof (NODE) ) ; gets(p->name); insert_node(head, p, 1);

    这一条说明了insert_ node这个函数类型及参数。上面那一段已经建立了一个链表的框架,这个函数用于在链表的特定位置插入一个节点,需要输入表头指针head,刚刚建立的节点的指针p,以及位置i。

    int insert_node ( NODE * head , NODE * p , int i ) { NODE * q; int n=0; for ( q = head; n<i && q->link!=NULL; ++n ) q = q->link; /* ① 定位 */ p->link = q->link; /* ② 链接后面指针 */ q->link = p; /* ③ 链接前面指针 */ return 0; }

    首先再定义一个用于搭桥的指针q,以及一个用于循环的整数n。以head指针为起点,在还没有到达位置i之前,并且这个链表没有结束之前,持续一个for循环。(注意,这里的for,只针对后面第一行代码循环)。最终后面两行代码将会在最终定位到的节点后面插入一个新的节点。如下图所示: (5)输出链表

    int output ( NODE * head ); output(head);

    现在说明一个整型的函数output来输出链表,输入变量为表头指针。

    int output ( NODE * head ) { NODE * p; p = head->link; /* p 指向第一个数据节点 */ while ( p!=NULL ) { puts ( p->name ) ; /* 输出 p 所指向节点的数据 */ p = p->link ; /* p 指向下一个数据节点 */ } return 0; }

    定义这个函数,p为搭桥的指针,先让p指向第一个数据节点(这一步看不懂再看回前文,真的很清楚辽)。然后在后面的while循环里面,首先puts ( p->name )输出第一个数据节点的内容,这里可以根据所定义的结构来调整。然后让p指向下一个数据节点,判断下一个节点是不是NULL(只有在puts出了最后一个节点数据之后,p才会指向NULL)。循环这个结构直到输出了所有的节点,结束。

    (6)删除节点

    int delete_node ( NODE * head , int i ); delete_node(head,2);

    说明一个删除节点的函数,输入表头指针head以及位置i。

    int delete_node ( NODE * head , int i ) { NODE * q, * p; int n; for ( n=0, q = head; n<i-1 && q->link!=NULL; ++n ) q = q->link; /* ① 定位 */ if ( i>0 && q->link != NULL ) { p = q->link; /* p 指向被删除节点 */ q->link = p->link; /* ② 摘链 */ free ( p ); /* ③ 释放 p节点 */ }

    定义两个桥指针,q以及p。首先q指向需要摘链的前一个节点,这个很好理解。 然后p指向需要摘链的节点(这个看不懂,同样可以看一下前面(4)插入节点)。 q->link = p->link; 表示,q所指向的节点,直接跳过p所指向的节点,指向p的下一个节点。 然后p就free,free和malloc是一组打包操作,通过malloc建立的内存只有通过free来释放。

    关于我的起步,还有链表一些基础的内容就到这里。今天继续弄其他的

    Processed: 0.009, SQL: 9