如何写好 C\C++ 程序(转)
fohoo 2004-07-28 10:42:55 如何写好 C\C++ 程序
版权所有:?2000--2002 北京科泰世纪科技有限公司
1. 基本认识
1.1. 计算机科学是"人为"的科学
计算机科学是一门新兴的科学,它不同于物理、化学、天文等自然科学,它是人为创造的,它的研究对象也是人为创造的,是一门人为科学。自然科学的研究对象是客观世界,是对未知的探索;发现自然规律,研究并利用自然规律是其根本目的。作为工程师,要在给定条件下"做得更好",就是说要根据实际情况利用现有条件充分发挥"人为"的优越性。
1.2. 软件经常是理论落后于实践
众所周知,任何一门科学,都有它的理论,都应做到理论与实践相结合,软件理所当然应该这样,然而,实际情况并非如此。软件是工程,软件理论落后于实践的情况比比皆是。比如说,几乎没有人将图灵机理论应用于实际软件开发,计算机更多的是工程,工程是靠实践的,工程师要始终坚持"身体力行",不要忽视写程序,要从实际应用中去领悟软件的真谛。
1.3. 编程语言不是目的
首先,语言是工具,编程语言同样是工具;其次,语言不是目的,学习语言的目的就是为了更好地表达人的意思,而不是为了学习而学习。因此,对于工程师来说,不应把学习几门编程语言作为最终目的,而是要多快好省的"使用工具"实现自己的目的,开发出优秀的软件。
1.4. 冯·诺伊曼模型至今颠扑不破
存储程序理论至今已有五十年历史,冯·诺伊曼模型今后仍是计算机的基本模型,非冯模型不是不可能,但却越来越遥远。因此,工程师要恪守"规矩成方圆"的准则,好的工程师要会用工具,要会用"尺"来衡量,这里的"尺"就是冯·诺伊曼机的内存,做工程时一定要用"尺"量,尽量减小误差,编写程序不能违背计算机的基本模型,掌握语言,要做到知其然更知其所以然,利用语言操作计算机硬件,使其有效工作,才是最终目的。
北京科泰世纪科技有限公司 版权所有 2000年--2002年
2. C/C++编程中应该特别注意的一些问题
下面通过变量、栈、堆在设计中如何使用,来讨论如何写好C/C++程序(所有例题答案附在全文后)
2.1. 全程、局部、动态变量
例1:请回答下面程序中注释行中提出的问题
int a; // 内存中占多少字节?分配在哪一区?
int b = 1; // b 与 a 是否相连?什么是数据区?
void main()
{
int *p; // 分配在哪一区?
p = (int *)malloc(100); // 100 字节在哪?
…
}
2.2. 内存空间
不清楚内存就写不了好程序,因此对内存的理解,对工程师来说至关重要。(对于本公司的工程师,搞不清楚内存,程序不可以check in)
下图是内存空间的简单逻辑结构示意图,低(零)地址在下面,高地址在上面。
· 用户禁区:是从零地址开始的一段空间,严禁用户使用,保护系统空间;
· 程序代码区:用来存储当前运行程序的源代码;
· 全程数据区:存储全程变量,大小不受限制;
· 堆:堆是内存中可以动态分配的一片空间,用于存放程序运行过程中动态产生的变量,它是从低地址向高地址生长的,可以是链式结构,原则上讲只要内存中有剩余空间,堆就可以动态增长下去;
· 栈:是从高地址向低地址方向生长的存储区,它的实现有特殊性,是后进先出的,并且它的大小是受限的,使用中具有灵活,速度快,不需要人工释放,不存在竞争冒险问题等优点;
· 动态连接库代码;动态连接库数据;每个动态连接库又有它的堆、栈。
例2:一个关于指针入栈的问题。
void foo(char * p) {p = "世界";}
void main() {
char *p = "你好";
foo(p);
printf("%s\n", p); // 输出什么?
}
此题要求察看反汇编代码,建议使用Microsoft的compiler。并且要求了解编译后产生的所有文件,至少知道它们都是做什么的,有什么用途。
例3:请回答下面程序中注释行中提出的问题。
int a[10000000]; // 是否出错?
void main() {
int a[10000000]; // 是否出错?
int *a = (int *) malloc(10000000); // 是否出错?
*a = 1;
printf("%d\n", *a);
}
例4:关于堆使用的问题。
void main() {
char *p = (char *) malloc(10000000);
char *q = new char[10000000];
……
free(p); // 释放多少内存?
delete q; // 释放多少内存?
}
2.3. C/C++ Calling Conventions(调用习惯)
调用习惯决定编译程序编译源文件时,如何处理函数调用时传递参数的压栈次序,由谁(调用者还是被调用者)负责弹栈等。(参见MSDN)
· __cdecl Caller pops stack (C)
· __stdcall Callee pops stack (C++)
· __fastcall ECX passes this pointer (优化C)
__cdecl 是C\C++函数调用的默认形式,由调用函数清除栈,生成的可执行代码包含清除栈的部分,因此,对于同样的函数,它比__stdcall形式调用的可执行代码长。
__stdcall通常用于Win32 API函数调用,由被调用函数清除栈,采用这种方式调用,要有函数原型。
__fastcall用于优化的C,只要可能,就使用寄存器,速度快。
北京科泰世纪科技有限公司 版权所有 2000年--2002年
3. 程序运行基本概念
3.1. 主程序/子程序(EXE)
二进制文件,可以在机器上直接运行。
3.2. 程序库/模版库(LIB)
对于一些常用的函数,如printf、strcpy等,把他们编成库函数,由使用者调用,减少重复劳动和出错的可能,但编译后代码长度并没有变小。
3.3. 动态链接库(DLL)
当多个进程都需要调用某个函数时,为了节省内存空间把这些函数编成动态连接库,由多个进程动态共享。
在选择使用LIB还是DLL时,要考虑应用中具体情况,比如说多少进程共享一个DLL合适,效率如何等等,更具实际做出权衡。另外,DLL也有其缺点,例如不同版本DLL的兼容性不可能做到完美。
北京科泰世纪科技有限公司 版权所有 2000年--2002年