分享一个简洁有趣的打印菱形的程序[C语言]

今天下午群里一位朋友问到的程序,挺简洁的,于是总结了一下,发在论坛里了,也copy到博客里来吧。

首先上程序和效果图:

程序:

[code lang=”C”]
#include <stdio.h>

int line = 1;
int main()
{
printf("%*s\n",
7-(line>4? line-4: 4-line),
"*******"+2*(line>4? line-4:4-line)
);
if(++line != 8)
main();
return 0;
}
[/code]

效果图:

继续阅读分享一个简洁有趣的打印菱形的程序[C语言]

C++ 重载(overload)、重写(overrride)、重定义(redefine)总结

昨晚打开论坛,看到有朋友问了一个关于虚函数的问题,因为头太疼了,所以今天中午起床再看。

问题传送门:http://www.cppleyuan.com/viewthread.php?tid=7923

 

C++的一些特性好久没使用了,导致有些生疏了,所以查了一些资料,回顾了一下。

 

先来看几个概念:

重载(overload),重写(override,也称覆盖), 重定义(redefine,也称隐藏)

(PS:第三个我不确定在英文中是否应该称为redefine,如有问题,留言告知,谢谢)

继续阅读C++ 重载(overload)、重写(overrride)、重定义(redefine)总结

读《华为C++编程规范》笔记

首先给出我读的《华为C++编程规范》的下载传送门:

(等待上传ing…)

说明:以下都是我把我在看规范的过程中,觉得对“我”有用的地方摘录了下来,注意,我把“我”字强调了,说明我摘录的几点你们不一定觉得好。我建议大家都看看这份编码规范(网上还有google的,也建议看看),当然,什么东西都不是绝对完美的,就像这份规范一样,大家可以选择性的吸纳,规范加个性(自己的习惯),两者结合,把握好自己平衡点,才是好的。

在这几天,我也会抽时间把google的C++编码规范做一下笔记,也希望更多的朋友一起学习,一起交流。

(交流论坛:

C++奋斗乐园:www.CppLeYuan.com

)

附:最近在公司实习(今天刚辞了),给我最大的感受就是一个代码的规范性,以前写的都是几十行的小代码,所以无法感受到,真正看了大的项目,首先连一些基本(算是约定俗成的规范吧!),我都不知道,所以才额外关注了编码的规范问题!还有很好需要学的啊。。。

—————以下是笔记————–

—–|排版|—–

1.对齐只使用空格键,不使用TAB键(对应原文件1.8)
说明:以免用不同的编辑器阅读程序时,因TAB键所设置的空格数目不同而造成程序布局不整齐,不要使用BC作为编辑器合版本,因为BC会自动将8个空格变为一个TAB键,因此使用BC合入的版本大多会将缩进变乱。
(个人说明:这个以前没有注意,那天在公司,header首先给我的VS2008改了几个选项,就包括这个,当然有点不明白,现在懂了)

2.在两个以上的关键字、变量、常量进行对等操作时,它们之间的操作符之前、之后或者前后要加空格;进行非对等操作时,如果是关系密切的立即操作符(如->),后不应加空格。(对应1.11)
说明:采用这种松散方式编写代码的目的是使代码更加清晰。
由于留空格所产生的清晰性是相对的,所以,在已经非常清晰的语句中没有必要再留空格,如果语句已足够清晰则括号内侧(即左括号后面和右括号前面)不需要加空格,多重括号间不必加空格,因为在C/C++语言中括号已经是最清晰的标志了。
在长语句中,如果需要加的空格非常多,那么应该保持整体清晰,而在局部不加空格。给操作符留空格时不要连续留两个以上空格。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
(1) 逗号、分号只在后面加空格。
int a, b, c;  
 
(2)比较操作符, 赋值操作符"=""+=",算术操作符"+""%",逻辑操作符"&&""&",位域操作符"<<""^"等双目操作符的前后加空格。
if (current_time >= MAX_TIME_VALUE) 
a = b + c;
a *= 2;
a = b ^ 2; 
 
(3)"!""~""++""--""&"(地址运算符)等单目操作符前后不加空格。
*p = 'a';        // 内容操作"*"与内容之间
flag = !isEmpty; // 非操作"!"与内容之间
p = &mem;        // 地址操作"&" 与内容之间
i++;             // "++","--"与内容之间
 
(4)"->""."前后不加空格。
p->id = pid;     // "->"指针前后不加空格
 
(5) ifforwhileswitch等与后面的括号间应加空格,使if等关键字更为突出、明显。
if (a >= b && c > d)

—–|注释|—–

3.一般情况下,源程序有效注释量必须在20%以上。(对应2.1)
说明:注释的原则是有助于对程序的阅读理解,在该加的地方都加了,注释不宜太多也不能太少,注释语言必须准确、易懂、简洁。

4.说明性文件(如头文件.h文件、.inc文件、.def文件、编译说明文件.cfg等)头部应进行注释,注释必须列出:版权说明、版本号、生成日期、作者、内容、功能、与其它文件的关系、修改日志等,头文件的注释中还应有函数功能简要说明。(对应2.2)

示例:下面这段头文件的头注释比较标准,当然,并不局限于此格式,但上述信息建议要包含在内。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/*************************************************
  Copyright (C), 1988-1999, Huawei Tech. Co., Ltd.
  File name:      // 文件名
  Author:       Version:        Date: // 作者、版本及完成日期
  Description:    // 用于详细说明此程序文件完成的主要功能,与其他模块
                  // 或函数的接口,输出值、取值范围、含义及参数间的控
                  // 制、顺序、独立或依赖等关系
  Others:         // 其它内容的说明
  Function List:  // 主要函数列表,每条记录应包括函数名及功能简要说明
    1. ....
  History:        // 修改历史记录列表,每条修改记录应包括修改日期、修改
                  // 者及修改内容简述  
    1. Date:
       Author:
       Modification:
    2. ...
*************************************************/

5.源文件头部应进行注释,列出:版权说明、版本号、生成日期、作者、模块目的/功能、主要函数及其功能、修改日志等。(对应2.3)

示例:下面这段源文件的头注释比较标准,当然,并不局限于此格式,但上述信息建议要包含在内。

1
2
3
4
5
6
7
8
9
10
11
12
/************************************************************
Copyright (C), 1988-1999, Huawei Tech. Co., Ltd.
FileName: test.cpp
Author: Version : Date:
Description: // 模块描述
Version: // 版本信息
Function List: // 主要函数及其功能
1. -------
  History:         // 历史修改记录
      <author>  <time>   <version >   <desc>
      David    96/10/12     1.0     build this moudle  
***********************************************************/

说明:Description一项描述本文件的内容、功能、内部各部分之间的关系及本文件与其它文件关系等。History是修改历史记录列表,每条修改记录应包括修改日期、修改者及修改内容简述。

6.函数头部应进行注释,列出:函数的目的/功能、输入参数、输出参数、返回值、调用关系(函数、表)等。(对应2.4)

示例:下面这段函数的注释比较标准,当然,并不局限于此格式,但上述信息建议要包含在内。

1
2
3
4
5
6
7
8
9
10
11
12
13
/*************************************************
  Function:       // 函数名称
  Description:    // 函数功能、性能等的描述
  Calls:          // 被本函数调用的函数清单
  Called By:      // 调用本函数的函数清单
  Table Accessed: // 被访问的表(此项仅对于牵扯到数据库操作的程序)
  Table Updated:  // 被修改的表(此项仅对于牵扯到数据库操作的程序)
  Input:          // 输入参数说明,包括每个参数的作
                  // 用、取值说明及参数间关系。
  Output:         // 对输出参数的说明。
  Return:         // 函数返回值的说明
  Others:         // 其它说明
*************************************************/

7.在程序块的结束行右方加注释标记,以表明某程序块的结束。(对应2.19)
说明:当代码段较长,特别是多重嵌套时,这样做可以使代码更清晰,更便于阅读。

示例:参见如下例子。

1
2
3
4
5
6
7
8
if (...)
{
    // program code
    while (index < MAX_INDEX)
    {
        // program code
    } /* end of while (index < MAX_INDEX) */ // 指明该条while语句结束
} /* end of  if (...)*/ // 指明是哪条if语句结束

—–|标识符命名|—–

8.除了编译开关/头文件等特殊应用,应避免使用_EXAMPLE_TEST_之类以下划线开始和结尾的定义。(对应3.9)

—–|变量,结构|——

9.去掉没必要的公共变量。(对应5.1)

说明:公共变量是增大模块间耦合的原因之一,故应减少没必要的公共变量以降低模块间的耦合度。

10.不要设计面面俱到、非常灵活的数据结构。(对应5-4)

说明:面面俱到、灵活的数据结构反而容易引起误解和操作困难。

(个人理解,这个也是片面的,关键还是在于开发者把握好尺度的问题)

11.留心具体语言及编译器处理不同数据类型的原则及有关细节。(对应5.9)

说明:如在C语言中,static局部变量将在内存“数据区”中生成,而非static局部变量将在“堆栈”中生成。这些细节对程序质量的保证非常重要。

—–|函数,过程|—–

12.编写可重入函数时,若使用全局变量,则应通过关中断、信号量(即P、V操作)等手段对其加以保护。(对应6.4)
说明:若对所使用的全局变量不加以保护,则此函数就不具有可重入性,即当多个进程调用此函数时,很有可能使有关全局变量变为不可知状态。

13.为简单功能编写函数。(对用6.4)
说明:虽然为仅用一两行就可完成的功能去编函数好象没有必要,但用函数可使功能明确化,增加程序可读性,亦可方便维护、测试。

示例:如下语句的功能不很明显。

1
value = ( a >; b ) ? a : b ;

改为如下就很清晰了。

1
2
3
4
5
int max (int a, int b)
{
      return ((a > b) ? a : b);
}
value = max (a, b);

或改为如下。

1
2
#define MAX (a, b) (((a) > (b)) ? (a) : (b))
value = MAX (a, b);

(个人理解:#define这个不太推荐,放着好好的函数不用,何必徒增烦恼?)

—–|可测性|—–

14.使用断言来发现软件问题,提高代码可测性。(对应7.5)
说明:断言是对某种假设条件进行检查(可理解为若条件成立则无动作,否则应报告),它可以快速发现并定位软件问题,同时对系统错误进行自动报警。断言可以对在系统中隐藏很深,用其它手段极难发现的问题进行定位,从而缩短软件问题定位时间,提高系统的可测性。实际应用时,可根据具体情况灵活地设计断言。

15.用断言确认函数的参数。(对应7.9)

示例:假设某函数参数中有一个指针,那么使用指针前可对它检查,如下。

1
2
3
4
int exam_fun( unsigned char *str )
{
    EXAM_ASSERT( str != NULL ); // 用断言检查“假设指针不为空”这个条件
    ... //other program code

16.使用代码检查工具(如C语言用PC-Lint)对源程序检查。
使用软件工具(如 LogiSCOPE)进行代码审查。

(以上两个我也没用过,这几天研究下)

—–|宏|—–

17.将宏所定义的多条表达式放在大括号中。(对应13.2)

示例:下面的语句只有宏的第一条表达式被执行。为了说明问题,for语句的书写稍不符规范。

1
2
3
4
5
#define INTI_RECT_VALUE( a, b )\
a = 0;\
b = 0;
for (index = 0; index < RECT_TOTAL_NUM; index++)
INTI_RECT_VALUE( rect.a, rect.b );

正确的用法应为:

1
2
3
4
5
6
7
8
9
10
#define INTI_RECT_VALUE( a, b )\
{\
a = 0;\
b = 0;\
}
 
for (index = 0; index < RECT_TOTAL_NUM; index++)
{
    INTI_RECT_VALUE( rect[index].a, rect[index].b );
}

19:使用宏时,不允许参数发生变化。(对应12.3)

示例:如下用法可能导致错误。

1
2
3
4
#define SQUARE( a ) ((a) * (a))
int a = 5;
int b;
b = SQUARE( a++ ); // 结果:a = 7,即执行了两次增1。

正确的用法是:

1
2
b = SQUARE( a );
a++; // 结果:a = 6,即只执行了一次增1。

关于C++中断言assert的转载及汇总

首先看MSDN上的解释:

Evaluates an expression and, when the result is false, prints a diagnostic message and aborts the program.

1
2
3
void assert( 
   int expression 
);

断言assert是一个宏,该宏在<assert>中,,当使用assert时候,给他个参数,即一个判读为真的表达式。预处理器产生测试该断言的代码,如果断言不为真,则发出一个错误信息告诉断言是什么以及它失败一会,程序会终止。

通常当我们使用assert时,都在强烈说明一个含义:在这里必然如此。即assert是指你绝对确信不可能会发生的事

摘录林锐博士高质量编程一书中相关内容。
~~~~~~~~~~~~~~~~~~~~~~~~
程序一般分为Debug版本和Release版本,Debug版本用于内部调试,Release版本发行给用户使用。
断言assert是仅在Debug版本起作用的宏,它用于检查“不应该”发生的情况。以下是一个内存复制程序,在运行过程中,如果assert的参数为假,那么程序就会中止(一般地还会出现提示对话,说明在什么地方引发了assert)。

1
2
3
4
5
6
7
8
9
void memcpy(void *pvTo, void *pvFrom, size_t size) 
{ 
	void *pbTo = (byte *) pvTo;
	void *pbFrom = (byte *) pvFrom; 
	assert( pvTo != NULL && pvFrom != NULL ); 
	while(size-- > 0 ) 
		*pbTo++ = *pbFrom++ ; 
	return (pvTo); 
}

assert不是一个仓促拼凑起来的宏,为了不在程序的Debug版本和Release版本引起差别,assert不应该产生任何副作用。所以assert不是函数,而是宏。程序员可以把assert看成一个在任何系统状态下都可以安全使用的无害测试手段。
以下是使用断言的几个原则:
1)使用断言捕捉不应该发生的非法情况。不要混淆非法情况与错误情况之间的区别,后者是必然存在的并且是一定要作出处理的。
2)使用断言对函数的参数进行确认。
3)在编写函数时,要进行反复的考查,并且自问:“我打算做哪些假定?”一旦确定了的假定,就要使用断言对假定进行检查。
4)一般教科书都鼓励程序员们进行防错性的程序设计,但要记住这种编程风格会隐瞒错误。当进行防错性编程时,如果“不可能发生”的事情的确发生了,则要使用断言进行报警。

网上还有一篇关于“断言”的大师级文章:

http://blog.csdn.net/zxcred/article/details/3239883

其他一些参考链接:

http://zhidao.baidu.com/question/119204684.html

http://topic.csdn.net/t/20040922/15/3398499.html

http://topic.csdn.net/t/20040718/20/3186411.html

http://bbs.chinaunix.net/thread-1185312-1-1.html