如果生活说生活是由各种规则构造,那么在规则之下还觉得生活有趣的无法就是每个有趣的灵魂,编程亦是如此!
#include <stdio.h>
int main() {
char you_qu = 0;
printf("你觉得生活有趣吗?(Y/N):");
you_qu = getchar();
if (you_qu == 'Y') {
printf("你应该有一个有趣的灵魂吧!!\n");
} else if (you_qu == 'N') {
printf("你灵魂缺少滋养,几乎干渴了!!\n");
} else {
printf("你多半没有灵魂了!!\n");
}
return 0;
}
你觉得生活有趣吗?(Y/N):Y
你应该有一个有趣的灵魂吧!!
你觉得生活有趣吗?(Y/N):N
你灵魂缺少滋养,几乎干渴了!!
你觉得生活有趣吗?(Y/N):X
你多半没有灵魂了!!
01 数据结构课程
这个课程是在学习了C语言后,接下这个学期要学习的内容。在知道数据结构这门课程后,我才知道,为什么之前学习的C语言像是没有灵魂,必须通过一些死记硬背的方式去记住很多规则,这也是很多同学放弃的理由吧。
我能够坚持下来不是因为我有多么的喜欢死记硬背,相反我是非常不擅长死记硬背的那种人,记得小学、初中因为背课文经常被放学后留下来的悲惨经历,苦不堪言。
只是因为我对其他课程感到无趣,那么我上大学的目的是什么呢?我需要一个上大学的借口,不然我很难再坚持下去了,编程对于我来说唯一的吸引并非编程本身,我和现在的大部分编程小白一样,只是单纯的想做一个黑客,给人一种深沉的神秘感,同时也是一种局部存在感的表达。
为此,我还特意买了一些书《七剑下天山》、《我的黑客女友》等一些看上去与黑客有关的书籍,还非常认真的通读了全书,确实学到了一些东西,但不是编程相关的东西,而只是一些工具的使用。虽然这些书籍,在专业人士的眼里只不过是一些外行人用来装点门面的技艺,没有实质性技术价值,对于初学者的我还是如获至宝。
那么真正的编程又是什么呢?我突然想起,一个月前wtea给出的那道题“堆和栈的区别”,让我无地自容的题目,同时也是让我从自以为的得意时刻跌落深渊的时刻,我将铭记这个时刻,永远不要太高调,因为你所见到的可能只是你的认知范围内的知识。
超出认知,你不知,不是你的错误的,但是过于自满那肯定是要吃亏的。
这是大二第一学期,开始学习数据结构课程后,我终于明白了什么叫井底之蛙了,山外有山,人外有人。如果把编程比喻成乾坤大挪移神功,我目前的水平还在第一重,总共有七重。
数据结构就是第二重神功,从最开始C语言的招式修改修炼上升到对低阶心法的领悟上。
我举个数据结构中比较有意思的一个结构:链表。
假设人的生命是循环的:
我们如何使用链表来表示呢?
假设每个阶段都是一个节点,这个节点有一个名字,就是我们定义的阶段名字,为了方便表示,我们给每个阶段对应一个字母a->n,毕竟使用中文写代码不是很美观的,接下来我们需要先定义这个节点的数据结构。
#define MAX_SIZE 32
typedef struct _Node {
char code; // 字母代号
char name[MAX_SIZE]; // 阶段名称
struct _Node* next; // 指向下一个节点
} Node;
我们定义完成用来表示一个阶段的数据结构,如上代码应该是一目了然的了,我需要特别说明的next,这个变量是一个指针,其实在计算机里面就是一个内存地址的存在,用来指向下一个阶段的内存地址,他就相当于连接两个阶段之间的那条线,如果没有这条线,我们是没有办法把前后阶段按照一定顺序串联起来的。
其中出现了几个关键字:
-
struct : 结构体类型关键字,表示下面定义的是一个结构体类型,结构体可以把一系列相关的属性放在一个集合中表示,主要解决的是编程中的一个重要问题:内聚,这种代码可以提高代码的可读性,同时减少代码维护难度和成本。
-
typedef: 是定义别名的意思,在本例中,是吧struct _Node重命名为Node,在C语言中如果要定义一个结构体(struct)变量,需要使用struct修饰变量,如果我们重命名后,就只需要使用Node代表了,可以减少代码的冗余度。这种是否方法在C语言中是非常常见的。
-
define : 这表示一个宏定义,在C语言中也叫预编译宏,为啥叫预编译呢?因为他是在编译器执行之前就进行了替换工作,我们看到的代码中的 MAX_SIZE 其实在编译阶段是不存在的,他不是一个变量,连常量都算不上,在本例中只是32的一个代名词,方面代码中进行全局替换,同时减少代码中到处使用硬编码的数字,这样也是为了代码可维护性考虑。也是在C语言中非常常用的编程方式。
需要注意的是C语言都是需要使用“;”分号表示语句的结束的,无论是单行的普通语句,还是定义结构体这种比较长的多行语句,对于编译器来讲,他以分号分割为标记进行语法解析,所以如果缺少分号,编译器会报语法错误,这也是新人最容易犯的错误。我同样在这个错误上重复犯错,最后才能形成条件反射。
接下来,我们需要把一个一个的节点通过代码把他们串联起来,我们把上面的代码放到一起,方便观看。
#include <stdio.h>
#include <malloc.h>
#include <string.h>
#define MAX_SIZE 32
typedef struct _Node {
char code; // 字母代号
char name[MAX_SIZE]; // 阶段名称
struct _Node* next; // 指向下一个节点
} Node;
// 初始化链表
Node* init_list() {
char codes[] = { 'a', 'b', 'c', 'd', 'e' };
const char *names[5] = { "出生", "中年危机", "退休", "晚年", "投胎" };
Node* header = NULL;
Node* p = NULL;
int i = 0;
for (i = 0; i < sizeof(codes); i++) {
if (header == NULL) {
header = p = (Node*)malloc(sizeof(Node));
} else {
p->next = (Node*)malloc(sizeof(Node));
p = p->next;
}
p->code = codes[i];
strcpy(p->name, names[i]);
p->next = NULL;
}
p->next = header;
return header;
}
// 输出链表
void output(Node* header, int count) {
Node* p = header;
while (p != NULL && count > 0) {
printf("%c:%s->", p->code, p->name);
p = p->next;
count--;
}
}
int main() {
// 首先我们需要定义一个变量来表示这个循环链表的头
// NULL在C语言中表示空,这个空其实在内存里面就是0,这个特殊单词,
// 仅仅是为指针类型准备的,其他类型最好不要使用。
Node* header = NULL;
// 接下来,我们要初始节点,并同时把他们串联起来,我们定义一个新的函数来初始化。
header = init_list();
// 我们安装顺序输出
output(header, 10);
return 0;
}
我们在输出函数ouput中第二个参数,表示输出节点的个数,设置输出10个节点,我们来看看执行结果:
a:出生->b:中年危机->c:退休->d:晚年->e:投胎->a:出生->b:中年危机->c:退休->d:晚年->e:投胎->
可以看到他是一个死循环在执行的。
这就是链表实现的一个生命循环,是不是特别有意思。
02 编程之外的往事
在学习数据结构课程的过程中有一次忘记了去上课,这个教授数据结构的老师是一个中老年老师,非常的严格,缺一次课,就扣10分。
我忘记了上课,当我想起来赶到教室门口时,已经开始点名了,我不敢进去,因为害怕这个老师,同时这么多人看着确实不好进,我在教室门口犹豫很久,最后还是放弃了。
虽然我的上机实验课和课程设计都是满分,最后本课程的分数也只有80分而已,后来我开始反思,上课到底是否重要,我总结的结论是,如果你有自主学习的能力,上课已经不重要了。
但是如果你是要通过考试或者考高分在一些不重要的非专业课上,你就必须的听老师的课,老师会给你划重点,这也是学校惯例,甚至有些课程太复杂,导致大部分人无法及格,可能会重考甚至开卷考试。
这种考试的意义却是没有了,只是一种形式而已,对于一些普通的大学,毕业率可能远远大于学生是否学到东西。
这件事情也奠定后来我厌弃上课的基础情绪,我觉得上课的方式是满足大部分的学习进度的方式,不适合天才,或者不适合在某个方面要求更大进步的人。后来总结发现编程几乎是靠自己的努力,老师的作用几乎没有太大作用,当然那些想通过老师的课程拿高分的同学,虽然没有真材实料,但是确实可以考到高分。与我,与成为优秀的程序员并没有直接关系。
我不是鼓励大家翘课,而是想说明一个问题,自觉性的作用远远大于跟什么老师学习,大学生靠的是自己的意愿,所谓兴趣那只是针对业余选手的乐趣,专业的程序员,需要的是坚持。
这种坚持是日复一日的刻意练习,在学习数据结构的课程结束后,我对C语言的掌握可以说是随心所欲的程度了。
这个课程后,我的C语言代码量有之前预估的4W行,增长到7W行左右。
但是我没有意识到一个问题,那就是重复性问题,重复劳动带来的不是质的变化,让我停留在一个自以为可以自己控制的世界里洋洋得意,对于常见的课程设计都可以通过同样的套路,复制粘贴代码,快速形成一个课程设计,在这个过程,我成了课程设计生成机器,解决本班级一半人的课程设计难题。
中间发生了一些有趣事情,请看下回分解。