9月份,我将向工程学院的学生们提供C语言的第一次讲座(通常我会教数学和信号处理,但我也做过很多C语言的实际工作,没有给他们讲课).计算机科学不是他们的主题(他们更多地研究电子和信号处理),但他们需要有良好的编程背景(其中一些可能会成为软件开发人员)
今年将是他们学习C的第二年(他们应该知道指针是什么以及如何使用它,但当然,这个概念尚未被同化)
除了经典的东西(数据结构,经典算法......),我可能会把我的一些讲座集中在:
在用C编码之前设计算法(并用伪代码编写)(在编码之前考虑)
使您的代码可读(注释,变量名称,...)和
指针,指针,指针!(它是什么,如何以及何时使用它,内存分配等......)
根据您的经验,您的老师从未教过您的C中最重要的概念是什么?我应该关注哪个特定点?
例如,我应该将它们介绍给某些工具(lint
,......)吗?
const
在指针上下文中使用关键字:
以下声明之间的区别:
A) const char* pChar // pointer to a CONSTANT char B) char* const pChar // CONSTANT pointer to a char C) const char* const pChar // Both
所以用A:
const char* pChar = 'M'; *pChar = 'S'; // error: you can't modify value pointed by pChar
和B:
char OneChar = 'M'; char AnotherChar = 'S'; char* const pChar = &OneChar; pChar = &AnotherChar; // error: you can't modify address of pChar
我的老师花了很多时间教我们,指针是可怕的小goobers,如果不正确使用可能会导致很多问题,他们从来不打扰向我们展示它们真正有多强大.
例如,指针算法的概念对我来说是陌生的,直到我已经使用C++几年:
例子:
c [0]相当于*c
c [1]相当于*(c + 1)
循环迭代:for(char*c = str;*c!='\ 0'; c ++)
等等...
而不是让学生害怕使用指针,教他们如何适当地使用它们.
编辑:由于注释引起了我的注意,我刚刚读了一个不同的答案,我认为在讨论指针和数组之间的细微差别(以及如何将两者放在一起以促进一些相当复杂的结构)方面也有一些价值,以及如何const
在指针声明方面正确使用关键字.
他们真的应该学习使用辅助工具(即编译器以外的任何东西).
1)Valgrind是一个很好的工具.它非常容易使用,它可以完美地跟踪内存泄漏和内存损坏.
它将帮助他们理解C的记忆模型:它是什么,你能做什么,以及你不应该做什么.
2)GDB + Emacs与gdb-many-windows.或者其他任何集成调试器.
它会帮助那些懒惰的人用铅笔和纸张逐步完成代码.
不仅限于C; 这是我认为他们应该学习的内容:
1)如何正确编写代码:如何编写不可维护的代码.读到这一点,我发现至少有三项我犯了罪.
说真的,我们为其他程序员编写代码.因此,对我们来说,写清楚比写聪明更重要.
你说你的学生实际上不是程序员(他们是工程师).所以,他们不应该做一些棘手的事情,他们应该专注于清晰的编码.
2)STFW.当我开始编程时(我开始使用Pascal,而不是转到C语言),我通过阅读书籍来做到这一点.我花了无数个小时试图弄清楚如何做事.
后来,我发现我必须弄清楚的所有内容已经被许多其他人完成,并且至少有一个人已经在网上发布了它.
你的学生是工程师; 他们没有太多时间投入编程.所以,他们有这么短的时间,他们应该花时间阅读其他人的代码,或许,可能会习惯于成语.
总而言之,C是一门非常容易学习的语言.他们在编写任何超过几行的东西时会遇到很多麻烦,而不是学习独立的概念.
当我不得不使用C作为学校中较大项目的一部分时,能够正确使用gdb(即根本不能),最终预测谁将完成他们的项目,谁不会.是的,如果事情变得疯狂,你有大量的指针和内存相关的错误gdb将显示奇怪的信息,但即使知道这可以指向人们正确的方向.
还提醒他们C不是C++,Java,C#等是个好主意.当你看到有人像C++中的字符串一样处理char*时,这种情况最常出现.
unsigned vs signed.
位移运算符
比特屏蔽
位设置
整数大小(8位,16位,32位)
面向对象:
struct Class { size_t size; void * (* ctor) (void * self, va_list * app); // constructor method void * (* dtor) (void * self); // destructor method void (* draw) (const void * self); // draw method };
(代码来源)
便携性 - 很少在学校教授或提及,但在现实世界中出现很多.
宏的(危险的)副作用.
使用valgrind
工具很重要,所以我建议至少提一下
Makefile以及构建过程的工作原理
GDB
皮棉
编译器警告的用处
关于C,我认为重要的是要强调程序员应该知道"未定义的行为"究竟意味着什么,即知道即使它似乎与当前的编译器/平台组合一起工作也可能存在未来的问题.
编辑:我忘了:教他们如何搜索并在SO上提出正确的问题!
使用一致且可读的编码风格.
(这应该可以帮助您查看他们的代码.)
相关:不要过早优化.首先了解瓶颈的位置.
知道当你递增指针时,新地址取决于该指针所指向的数据的大小...(IE,char*递增和unsigned long*之间的区别是什么)...
首先要确切了解分段故障究竟是什么,以及如何处理它们.
知道如何使用GDB很棒.知道如何使用valgrind很棒.
开发C编程风格......例如,当我编写大型C程序时,我倾向于编写相当面向对象的代码(通常,特定.C文件中的所有函数都接受一些(1)特定的struct*并对其进行操作. ..我倾向于有foo*foo_create()和foo_destroy(foo*)ctor和dtors ...)...
理解链接器.任何使用C的人都应该理解为什么"static int x;" 在文件范围不会创建全局变量.编写一个简单的程序,其中每个函数都在自己的翻译单元中并分别编译每个函数在学习C的早期阶段不够频繁.
始终有效警告.使用GCC,至少使用-Wall -Wextra -Wstrict-prototypes -Wwrite-strings
.
I/O很难.scanf()
是邪恶的.gets()
应该永远不会被使用.
当您打印未被'\n'
终止的内容时,stdout
如果您想立即打印它,则必须刷新,例如
printf("Type something: "); fflush(stdout); getchar();
const
尽可能使用指针.例如void foo(const char* p);
.
使用size_t
存储大小.
Litteral字符串一般不能修改,所以制作它们const
.例如const char* p = "whatever";
.
破坏的内存可以触发各种奇怪的错误.
调试器可能骗你.
我认为整体想法似乎非常好.这些是一些额外的东西.
调试器是一个好朋友.
检查边界.
确保指针在使用之前实际指向某个东西.
内存管理.
希望之前没有发布过(只是非常快速地阅读),但我认为当你必须使用C时,非常重要的是了解数据的机器表示.例如:IEEE 754浮点数,大与小端,结构对齐(这里:Windows vs Linux)......为了实现这一点,制作一些有点谜题(解决一些问题而不使用任何功能)非常有用然后printf打印结果,有限数量的变量和一些逻辑运算符).此外,了解链接器如何工作,整个编译过程如何工作等基本知识也很有用.但特别是理解链接器(没有它,很难找到某种错误...)
这本书帮助我提高了我的C和C++技能:http://www.amazon.com/Computer-Systems-Programmers-Randal-Bryant/dp/013034074X
我认为对计算机体系结构的深入了解会使好的和坏的C程序员之间产生差异(或者至少它是一个重要的因素).
教他们单元测试.
一般最佳实践如何?
总是假设其他人已经编写了您的代码,并且它在互联网上免费提供,并且比您在截止日期之前生成的任何内容更好地编写和测试.
提前退货/避免其他条款
初始化所有变量
每个功能一页作为指导(即一起使用较小的代码片段)
何时使用switch,if-else if或hash表
避免全局变量
始终检查您的输入和输出(我不相信我自己的代码.)
大多数功能应返回状态
[致其他人:随意编辑此内容并添加到列表中]
关于检查输入:
我曾经匆忙写了一个大程序,并且在我的函数中编写了各种Guard Clauses,输入检查.当我第一次运行该程序时,那些快速流动的子句中的错误甚至无法读取它们,但程序没有崩溃,可以干净地关闭.这是一个简单的问题,通过列表和修复错误快速的错误.
将Guard子句视为运行时编译器警告和错误.
我认为你不应该是教学工具.那应该留给Java老师.它们很有用并且被广泛使用但与C无关.调试器与它们希望获得的访问权限一样多.很多时候你得到的是printf和/或闪烁的LED.
教他们指针,但教他们好,告诉他们他们是一个整数变量代表在记忆中的位置(在大多数课程中他们也有一些组装训练,即使它是为了一些想象的机器,所以他们应该能够理解)和不是一个星号前缀变量,它以某种方式指向某个东西,有时变成一个数组(C不是Java).教他们C数组只是指针+索引.
让他们编写将溢出和段错误的程序,然后确保他们理解为什么会发生这种情况.
标准库也是C,让他们使用它并让他们的程序在你的私人测试中痛苦地因为使用了gets()和strcpy()或者双重释放了某些东西.
强制他们处理不同类型的变量,endianness(你的测试可以在不同的arch中运行),float to int conversion.让他们使用掩码和按位运算符.
即教他们C.
我得到的是C语言中的一些批处理,也可以在GW-BASIC中完成.