一些C语言学习的思考

来源:互联网 发布:艾利标签打印软件 编辑:程序博客网 时间:2024/06/05 14:23

    带着问题思考是最有效的,下面的问题摘录自http://stackoverflow.com/的C语言tag,这个阅读的过程伴随着自己的一些思考。浏览网页时,单单从问题本身以及阅读网友的评论和回答只能是帮助明白这个问题,深层次的消化总结才能将其应用于实际研发工作中,这里记录下来,聊作共享。

1

问题一开始让人疑惑:

#include <stdio.h> 
int main() 
{ 
     
int x =10; 
     
while( x-->0)// x goes to 0 
     
{ 
       printf
("%d ", x); 
     
} 
}

提问的人最开始强调自己遇到一个新的运算符“-->”,问有人知道在标准里面哪个位置有说明。后面解释了是“x--”书写的时候在x和自减运算符之间添加了空格,是书写风格的问题,显然提问人是有意为之。然而真正让我感兴趣的是回答问题和评论的人。遇到这种问题,有两种回答方式,一部分人选择正儿八经地回答是因为编码风格不好导致,然后给出其它复杂的情况,显得很专业;另外一部分人开始恶搞了,更有甚者上升到理论高度,“一本正经”认为这样的书写减少大脑思考时间,让人愉悦。

总结:偶尔恶搞式的提问能够增添乐趣,程序员的幽默是来自内心的。为了集思广益,我们在很多时候还真需要这种恶搞精神。

 

2

常见的问题,一个家伙问:C语言的解析器是开源的不,能看到其源代码不?不同的编译器是不是针对不同的C语言? stdlib又是什么?

弄清楚这些问题,就等同于理解了编程语言与编译工具之间的关系,更可以说理解了C语言到底是怎么执行的,感谢stallman,赐予我们GCC。另外stdlib是标准C函数库,几乎所有的编译工具都会支持,例如gcc对它的实现,读懂了它没准你也会写函数库了。

总结:目前有种趋势是标准与工程实现同步的情况,也许未来语言标准这个概念可能会跟今天大相径庭,出现拥有源代码的KK语言。

 

3

你为什么要学习C语言?

C语言从诞生到如今已经有40来年了,它被认为是一种低层次语言,从传统的汇编语言过度到C语言的学习比较容易。另外好多程序员学习这门语言的经历应该说是从大学开始,由于时间原因,选择C语言作为教材是因为比起Pascal等语言来说,它更简单、更符合人的思维。当然还有一些程序员是因为工作的原因需要去掌握它,毕竟很多嵌入式研发需要使用C语言。

有人发出感叹“study for girls”,好吧,这也是很重要的一个原因!

总结:未来的趋势是了解多门程序语言,精通一门,你准备精通哪一门?机器语言:)

  

4

写一个程序,不用循环和条件语句,打印1到1000?

但凡遇到这种问题,相信很多人会很不屑,这种问题在正式研发中从来都不是问题,因为没有人会不用循环和条件语句,俗话说:简单为美,能够使用为什么不用?但是不要低估程序员,他们总能找到新的解决办法,他们都是聪明和富有想象力的。

例如,递归的方法

#include <stdio.h>
int f(int val) {
    --val && f(val);
    return printf( "%d\n", val+1);
}
void main(void) {
    f(1000);
}


又比如,多级表示,对于1000这样数量级是有效的,例如Linux下页表的存储就采用这种原理

#include <stdio.h>
int i = 0;
p()    { printf("%d\n", ++i); }
a()    { p();p();p();p();p(); }
b()    { a();a();a();a();a(); }
c()    { b();b();b();b();b(); }
main() { c();c();c();c();c();c();c();c(); return 0; }


玩玩文字游戏,这问题不就是Print "numbers from 1 to 1000",脑筋急转弯!
printf("numbers from 1 to 1000");
还有,能不能看成是二进制数字啊?
printf("1 10 11 100 101 110 111 1000\n");


总结:一直强调发散思维,其实无论是做研发还是生活,多点发散思维真的会平添很多乐趣

 

5 C语言背后的使用特性

更准确地说是一种大家在开发的时候用到的技巧,因为C语言本身是一门程序语言,其特性都是一致的。编译器则不一样,并不是所有的C语言编译器是兼容的,例如gcc和MSVC编译器,但是在实际研发当中,我们都会使用一些不是那么显而易见的技巧,例如:

利用函数指针数组来模拟面向对象的思想,提高效率;

可以使用标准函数库中提供的qsort()快速排序函数;

GNU系统中可以使用宏unlikely()来包含条件,编译时将这部分不常碰到的逻辑放在不影响效率的地方;

printf中的%n格式串;

结构体的末尾声明char buffer[1],使得结构体大小能够动态增减;

调试时打印语句使用__LINE__和__FILE__;

总结:实际研发中,尽量少使用一些编译器特性的内容,提高移植性。当然,具体情况具体分析,如果使用gcc编译器的话,应该多多掌握gcc提供的一些特性。总而言之,好的设计和算法才是程序的灵魂,C语言的灵活性仅仅是一些实现我们设计的trick,遇到之后记录就是。

 

6 寻找项目中有用的模块

几乎所有的项目都会有一些“utility”模块,utility模块会被其他模块所调用,自成一派:

#define ITEMSOF(arr) (sizeof(arr)/sizeof(0[arr])),获取数组的大小,0[arr]的写法是为了防止在面向对象语言C++中出现[ ]重载的情况

#define ever ;;

for (ever) {...},啥都不说了,就为了便于理解

void hex(void *ptr, int buflen);打印出ptr指向的长度为buflen的内存内容,便于调试,函数体自己可以写

#ifdef NDEBUG 
#define Dprintf(format,...) 
#else 
#define Dprintf(format,...)\ 
    fprintf
(stderr,"[%s]:%s:%d: " format, __FILE__,\ 
        __func__
, __LINE__,##__VA_ARGS__) 
#endif,这种宏使用方式用来插入一些打印语句比较不错

总结:这样的模块我们经常会碰到,遇到之后记录下来,减少今后的工作量,实际上软件工程的核心思想就是迭加,积少成多。

 

7

const是新增的C语言标准,例如在C99中推荐使用。C程序员在很多时候还是使用#define而不是const。在C语言中,const只是代表这个变量是只读的,而在C语言编写的大型项目中,常常需要对某个常量重新赋值,这时候采用#define显得比较灵活,也就是覆盖或者#undefine操作。另外#define和const在编译器进行处理的时候阶段也不一样,前者发生在预编译阶段,后者往往在连接阶段才会被编译器解析。

总结:研发中某些时候尽量遵守传统的习惯,并不是因为新的东西不好,而是技术上传统的东西稳定,有这么做的道理,不容易出错。


8

很有意义的问题:一个优秀的C开发人员应该能够回答哪些问题:

char *p="Sam"; p[0]='R';这段代码有什么错误?

能快速写出strcpy()函数?

比起这么多高级语言,你为什么选用C语言来开发?

malloc()在什么时候会失败?

如何声明整数指向某个地址,并对地址进行操作?

void指针能做些什么?

。。。。。。

总结:讨论是没有结尾的,开发的时候多问自己为什么,相信研发能力的提升会非常快。

 

9

这么多高级语言,学习C语言有必要吗?

这个问题很难解答,一百个人的眼中有一百个哈姆雷特,这里仅仅把论坛上的一些回答罗列出来,看了后应该能做出抉择:

学习C语言后能够更好地理解其他语言程序的运行原理,能够不断重构,使程序更快更短;

MCU很有趣,用C语言;

很多核心框架用C语言写的;

back to basics;

从事技术,你就从了C语言,从事业务,你就从了C#;

总结:很多情况下,基础牢靠与否是个永恒的话题,并不是你掌握了C语言就能更好地使用面向对象语言,或者说掌握了C语言就能独善其身,更多时候要看你是否有追求技术创新的激情。也许当你在纠结于是否要精通C语言的同时,脚本语言在一旁偷笑呢。。。

(2011年9月21日晚)


 

原创粉丝点击