目前几乎所有理工科的学生都要学习C语言编程,有很多同学会感觉C语言很难入门。下面我将从我自己的学习经验出发,总结在C语言学习的过程中,怎么才能少走些弯路。 首先必须说一点,要想学好C语言,最好的办法是:上机!上机!上机!重要的事说三遍,离开编程实践,是没有办法学好一门语言的。像所有工具一样,最好的学习办法就是多用。上机多了,对编程的感觉自然而然会上来。 不管你的期末考试的形式是什么样的,哪怕就告诉你,考题就出复习材料中的原题,也千万不要试图去背答案。背答案永远是最差的解决方法。常言道授人以鱼不如授人以渔,学会一种解决问题的方法,学会一门编程语言,对所有理工科的学生来说,都是一种在日后不一定什么时候就能用到的技能。 下面进入正题。这里我不喜欢按照教材的顺序讲解,学一门语言要融汇贯通,要站在一个更高的视角反观正在学习的内容。 先说基本的程序结构,只有涉及到控制流(分支、循环等)和函数调用时,才有程序结构这一概念。 不要按照解决这个问题我需要用哪个语法来想问题,语法或者代码本身永远是最后最后才考虑的问题。我带过一个学生,我带他做题时,我都会先问题他:这个问题有思路吗?结果他上来就说:用for对吗?这说明他的思维没有上高度,眼光只盯着语法来想问题。 比较好的思维方式时,先想好大致的思路,这个思路是用自然语言描述的,不是用具体的编程语言描述的。例如,如果有一个问题,让你求a和b的最大公约数。有一点数论背景的人都会想到,用辗转相除法(广义欧几里德除法),算amodb,余数计为q,则有(a,b)(b,q),继续算bmodq,一环一环算下去,当整除时,除数就是最大公约数。上面这个就是我所谓的思路,不涉及具体的编码。先有思路,再转换为编程语言的具体代码,这样一来思路清晰,二来不易出错。根据这个思路,很容易写成代码: while(b){ intqab; ab; bq; } 再说说我的另一个观点:语言的细节是通过编程尝试学习的。比如,printf语句中,格式字符串中如果有s,15s,15s,15。5s,。5s,15。5s,分别什么意思?死记硬背肯定已经背晕了,但是如果你写出这样的一个程序: main(){ charstr1234567890abcdefg; printf(sn,str); printf(15sn,str); printf(15sn,str); printf(15。5sn,str); printf(。5sn,str); printf(15。5sn,str); return0; } 运行一下,很容易就能得到你想要的答案。而且,这与人的记忆方式有关,你这样试过之后,这个结果很容易形成你的长期记忆,不需要像背书一样地来背什么负号表示左对齐,。表示精度,。前面的表示宽度等等。 又如,我上面两个程序都用了语言固有的一些约定:上面那个,把一个整数作为判断条件,涉及到C语言中bool值的表示方法:非0表示真,0表示假。下面那个,函数返回值类型默认为int。这些也都可以通过编程实践试出来,不需要死记硬背。 又如,for(;;)表示一个死循环,而while()会导致编译错误,也就是说for语句的循环判断条件可以省略,while语句不能省,要想用while语句构造死循环,必须写成while(1)。 还有,你能不能一眼就看出来for(;;);跟for(;;)的区别?对,差了一个分号。没有足够多的编译实践,没有无数次的因为一个分号导致编译错误,你是无法具有这样一双慧眼的。这也就是为什么编程上机这么重要。 要学精C语言,要了解一些系统底层的知识。说实话,我个人也认为,对于毫无编程经验的人来说,入门第一门语言学C的确有些偏难了,因为C语言太过底层了。但是这也未尝不是一件好事如果把C语言能学精,那么以后学习任何一门别的语言都会觉得很容易。要学好C语言,你需要知道,在程序运行时,进程的内存布局分为哪些块:有代码区、静态数据区、栈区、堆区等等,当你知道这些后,你自然而然就会清楚,为什么说可以把static变量看作是全局变量,为什么malloc得到的东西如果不free就会永远存在,为什么我们的局部变量也叫自动变量,何为传值不传参。了解一些体系结构的知识,你才能知道register关键字代表什么含义。了解一个hello,world程序从源代码到。exe文件经历了哪些变化过程,知道什么叫编译,什么叫链接,你才能明白extern关键字的意义。等等。其实单为了学习一门语言,要看三四本书当然有点过了,但是简单地通过搜索引擎或者通过请教老师,把底层的一些知识搞懂,才能真的说你很懂C语言。 指针是C语言的灵魂。 这是我的《计算机导论》老师反复跟我们强调的一句话。直到使用C语言一年多之后,我才深刻地体会到了这句话的含义。没有指针,C语言就是渣;有了指针,C语言就是一门万能的语言。 初学C语言,到了指针这里,如果对intppi,intpppi,int(pa)〔〕,intap〔〕这样的声明都一点都不晕,才能说指针学得达到入门的水平了。一个能减轻你的大脑负担的方式是,见到指针之后,把指针变量本身画出来,把它指的内容画出来,用一个箭头指过去,这样就清晰明了了。我说的画出来,指的是内存,这样在操作指针时,你就知道,你操作的到底是什么东西,为什么它变了,为什么它没变。上机实践够多之后,这些东西就都熟悉了,就可以不过脑子直接写出正确的代码了。 给几个我们老师反复强调的口诀: 指针就是地址。 数组名就是指针。 指针就是数组,数组就是指针。 函数名就是指针。 把这些东西都理解透,你就能很容易地理解,如果有这样的声明inta〔100〕,paa; 那么,a〔i〕,pa〔i〕,(ai),(pai)都是同一个意思。 要理解函数指针,第一步,熟练使用库中的bsearch函数和qsort函数;第二步,自己写一个bsearch和qsort,注意参数要给一个函数指针。完成这两步,对于函数指针是什么东西,你就一点都不会晕了。其实只是那一句话:函数名就是指针。 浅谈递归 初学者遇到递归都会晕,甚至我的同学中,有的已经写了两年代码了,见到递归还是晕。其实大可不必害怕递归这个东西,用已有的思维方式,可以非常容易的理解它。 有的读者可能不知道我在说什么,递归就是一个函数调用它自己。比如下面这段求Fibonacci数列的程序: fibonacci(intn){ if(namp;lt;1) return1; returnfibonacci(n1)fibonacci(n2); } 就这个例子我们可以看到递归的要素: 1、递归必须有终止条件。这很好理解,如本例中的if语句,如果没有,那么会无限递归下去,最后程序会因为栈溢出而崩溃。 2、函数调用自己。 要理解递归,只要按照高中数学的思维想问题就可以,递归相当于递推公式,或者可以理解成数学归纳法。给一个初始条件,凡是后面的都根据前面的能推出来。我们写程序时,只需要把初始条件给好,把递推规则写好,剩下的,交给上帝来做。 看一看递归的运行栈,历史状态都在运行栈里,函数一层一层调用下去,再一层一层返上来,一点一点走。可以自己把运行栈画出来,一目了然。 递归能够简化我们的编码,但是带来的函数调用的开销是很大的,特别是当递归层数过多时,有可能因为递归太深导致运行栈崩溃。我们完全可以不用递归来写递归程序,这时要我们自己维护一个栈,记录中间的状态。这个编程难度对初学者来说有点大,只需要理解即可。 至于对基本语法已经很熟悉的同学,如果想深入学习,建议学一下数据结构。这可以强化你对递归和指针的理解,自己手动实现一些基本的数据结构,可以强化C语言的编程能力。 推荐的数组结构书目: 《数据结构与算法分析(C语言描述)》,机械工业出版社。 推荐的C语言教材:《C程序设计语言(第2版)》,机械工业出版社。 这本书是C语言的设计者写的,目前几乎全世界所有的C语言教材都以这本书为蓝本,大师的文字读起来就是一种享受,语言精练、易懂,内容全面。