标题:关于C编程过程中应该注意的事项(书写习惯以及快捷键使用)欢迎老手!!
只看楼主
zhagqn_Bc
Rank: 2
等 级:论坛游民
帖 子:33
专家分:19
注 册:2009-10-31
结帖率:100%
已结贴  问题点数:20 回复次数:9 
关于C编程过程中应该注意的事项(书写习惯以及快捷键使用)欢迎老手!!
刚学C 知道写程序习惯很重要

虽然老师 教科书上有看一些

不过不够典型
 
想听听大家的意见

这样写的又快又好

经验或者说教都行

搜索更多相关主题的帖子: 事项 欢迎 书写 习惯 老手 
2009-11-18 16:41
longlong89
Rank: 11Rank: 11Rank: 11Rank: 11
来 自:广州
等 级:小飞侠
威 望:6
帖 子:1043
专家分:2754
注 册:2009-8-18
得分:2 
多看看好的代码风格(别人的)
实在没有看看 The C Programming language 上的代码风格
良好的风格会提高你的code的可读性
它会让你受益终身

想象力征服世界
2009-11-18 18:08
d7d7
Rank: 4
等 级:业余侠客
帖 子:91
专家分:210
注 册:2008-9-29
得分:2 
这帖子不错,顶一个.
常量声明用#define 清淅又易修改
for语句后一般加括号,易读,减少失误
注意缩近,便于阅读.

最后说一个必需用到的快捷键:Ctrl+s保存,一定要形成习惯,写完一段就保存.
2009-11-18 19:23
aihuaqiong
Rank: 2
等 级:论坛游民
帖 子:17
专家分:16
注 册:2009-10-21
得分:10 
代码风格是一个很个性化的东西,每个人都会有自己的喜好和见解。这里列出的是我个人的风格,并且是一般的代码风格。所谓一般是指文中没有对标识符的命名有太多的规定,如全局变量、局部变量、宏等。相关规则一般在具体的项目中给出,不同的项目可以有不同的命名规则。

 

屏幕空间:这里基于标准的UNIX终端(Terminal)来定义屏幕的大小,宽度为80个字符,高度为24或25行。

 

 

1 缩进
1.1 基本规则
使用8字符宽度的tab来控制缩进。除了注释,空格从来不用于缩进;相应的,tab只用于缩进,不用于其他场合。

缩进(indentation)的目的是为了清楚的表现一个逻辑块的开始和结束,使用8字符这样的大缩进可以表现得更明显。

关于缩进的风格有很多,其中反对tab的不在少数。反对的理由之一是tab在不同的系统和编辑器上可能有不同的定义,从而导致本来很规整的代码在别的系统上显示错位。这确实是一个问题,所以,开始之前,请确认使用的编辑器将tab设置等于8字符宽度。

反对使用8字符tab的另一理由是,当缩进层次太多时,代码向屏幕右侧跑得太快,导致跨行代码增多,难于阅读。确实,在一些复杂的商业逻辑中,缩进层次可能很多。对此这里引用Linux Kernel代码风格中的一个解释:如果你的代码有超过3层的缩进,那么你需要重新设计你的程序。

要做到这点不容易,尤其当程序员水平有限,或者项目紧急,没有时间来优化代码。此时使用别的缩进方案(如4空格缩进)可能是一个折中的方案。

 

1.2 基本形式
以if语句为例:

        if (mapping)
                spin_lock(&mapping->i_shared_lock);
不要写在一行上:

        if (mapping) spin_lock(&mapping->i_shared_lock);
类型定义:

struct nlmsgerr
{
        int error;
        struct nlmsghdr msg;
};
 

1.3 switch语句
switch语句稍微有点例外,每个case标号与switch关键字在同一个缩进层次:

        switch (behavior) {
        case MADV_SEQUENTIAL:
                vma->vm_flags |= VM_SEQ_READ;
                break;
        case MADV_RANDOM:
                vma->vm_flags |= VM_RAND_READ;
                break;
        default:
                break;
        }
 

 

2 空格
2.1 关键字
在多数情况下,关键字后面用一个空格来与其他代码分开,比如if、switch、case、for、do、while等。

        for (i = 0; i < smp_num_cpus; i++)
        while (!cachep->growing)
        do {
                ...
        } while (sizes->cs_size);
sizeof和defined例外,在sizeof和defined后面使用小括号,不使用空格,虽然C语法并没有如此强制规定。如:

        base = sizeof(slab_t);
#if defined(CONFIG_SMP)
 

2.2 括号
函数名和括号之间没有空格。

在小括号与内部的表达式之间也没有空格。如:

        memset(addr, POISON_BYTE, size);
而不是:

        memset( addr, POISON_BYTE, size );
该规则同样适用于中括号,以及用于初始化列表的大括号。如:

        char buf[20 + 40];
        cpucache_t *new[NR_CPUS];
        struct swap_list_t swap_list = {-1, -1};
当括号中的内容为空时,左右括号之间也没有空格。如:

static const char bad_file[] = "Bad swap file entry ";
 

2.3 指针
当声明指针类型的数据和返回指针类型的函数时,星号(*)与数据名和函数名相连,而不是与类型名相连。如:

        slab_t *slabp;
static void *s_next(struct seq_file *m, void *p, loff_t *pos)
 

2.4 二元运算符
在二元运算符,诸如= + - < > * / % | & ^ <= >= == !=等的两边各用一个空格:

        slabp = objp + colour_off;
        objp -= BYTES_PER_WORD;
        if (cachep->colour_next >= cachep->colour)
        if ((flags & SLAB_POISON) && ctor)
 

2.5 三元运算符
三元运算符?:因形式的特殊性,总共用到4个空格(?和:两边各一个):

        (gfpflags & GFP_DMA) ? p->cs_dmacachep : p->cs_cachep;
 

2.6 一元运算符
在一元运算符,诸如& * + - ~ ! sizeof defined等的后面不加空格:

        address &= ~PGDIR_MASK;
        if (!offset)
static kmem_cache_t *clock_searchp = &cache_cache;
在后缀形式的++和--前不加空格:

        pte++;
        count--;
在前缀形式的++和--后不加空格:

        ++pte;
        --count;
在结构运算符.和->的两边不加空格:

        area->flags = flags;
        spin_lock(&init_mm.page_table_lock);
 

2.7 强制转换
在强制转换后面不加空格:

        area->addr = (void *)addr;
 

2.8 分号和逗号
分号(;)与前面的代码之间不加空格。

当分号用于for语句头部时,与后面的代码(右括号除外)之间加一个空格。

        for (p = &vmlist; (tmp = *p); p = &tmp->next)
当for用于永真循环时,则没有空格:

        for (;;)
逗号(,)规则与上类似。当一个初始化列表以逗号结束时,逗号与右大括号之间可以有一个空格:

int acct_parm[] = {4, 2, 30, };
在每行的末尾不要遗留多余的空白字符。如($表示行的结束):

        if (niceval < -20)$
而不是:

        if (niceval < -20)      $
 

2.9 对齐
在多行变量定义的地方,在类型与变量名之间可以适当的增加空格来对齐(注意不能用tab,tab只用于缩进!):

        int    size;
        fd_set *new_fdset;
该规则也适用于其他一些类似的场合,如宏定义:

#define STATS_INC_FREEHIT(x)  do { } while (0)
#define STATS_INC_FREEMISS(x) do { } while (0)
 

 

3 大括号的位置
3.1 语句块
这里采用Kernighan和Ritchie的风格(K&R):左括号({)在行的末尾,与前面代码之间有一个空格;右括号(})则单独成行,并与对应的块起始行对齐。即:

        while (--n) {
                p = p->next;
                if (p == &cache_cache.next)
                        return NULL;
        }
注意右括号是单独成行,除非后面还有子句,如do-while、if-else等。此时在右括号和后面的关键字之间加一个空格。形式如下:

        do {
                ...
        } while (--i);

        if (end == area->vm_end) {
                ...
        } else if (addr == area->vm_start) {
                ...
        } else {
                ...
        }
注意以上多路if-else的缩进格式,不要写成下面的形式:

        if (end == area->vm_end) {
                ...
        } else if (addr == area->vm_start) {
                ...
                } else {
                        ...
                }
该规则对所有语句块(除了函数)都适用,包括if、switch、for、while、do等。

这样做的理由是可以大幅度减少括号单独形成的行的的数目,从而在屏幕(比如24行的终端)中可以容纳下更多的代码,同时不损失代码的可读性。

 

3.2 函数定义
对于函数定义,左括号和右括号都单独成行:

void lock_vma_mappings(struct vm_area_struct *vma)
{
        struct address_space *mapping;
 
        mapping = NULL;
        if (vma->vm_file)
                mapping = vma->vm_file->d_inode->i_mapping;
        if (mapping)
                spin_lock(&mapping->i_shared_lock);
}

 

 

4 长行代码
每行代码的长度原则上不能超过屏幕宽度,即80个字符。

超过80个字符的行应分成多行书写,后续行的长度不大于前面行的长度,并缩进放置在右侧。这规则也适用于长参数列表的函数和长字符串。如:

        printk(KERN_WARNING "Warning this is a long printk with "
                                     "3 parameters a: %u b: %u "
                                     "c: %u \n", a, b, c);
当函数参数太多时,出于可读性和维护性的考虑,也可采用如下方式:

        if (msq != NULL) {
                len += sprintf(buffer + len, "%10d %10d %4o %10lu
%10lu %5u %5u %5u %5u %5u %5u %10lu %10lu %10lu\n",
                                msq->q_perm.key,
                                msg_buildid(i, msq->q_perm.seq),
                                msq->q_perm.mode,
                                msq->q_cbytes,
                                msq->q_qnum,
                                msq->q_lspid,
                                msq->q_lrpid,
                                msq->q_perm.uid,
                                msq->q_perm.gid,
                                msq->q_perm.cuid,
                                msq->q_perm.cgid,
                                msq->q_stime,
                                msq->q_rtime,
                                msq->q_ctime);
对于长字符串,如果觉得以上规则妨碍了字符串格式的控制,那么也可以超过80字符,如上例,但这样的情形不应该出现太多。

 

 

5 注释
采用“/*...*/”方式注释,不允许嵌套,也不允许C99风格注释“//...”。

多行注释的格式:

/*
 * This is the preferred style for multi-line
 * comments in the Linux kernel source code.
 * Please use it consistently.
 *
 * Description:  A column of asterisks on the left side,
 * with beginning and ending almost-blank lines.
 */
 

 

6 函数
6.1 定义
多个函数定义之间用一个空行分隔:

void lock_vma_mappings(struct vm_area_struct *vma)
{
        struct address_space *mapping;
 
        mapping = NULL;
        if (vma->vm_file)
                mapping = vma->vm_file->d_inode->i_mapping;
        if (mapping)
                spin_lock(&mapping->i_shared_lock);
}
 
void unlock_vma_mappings(struct vm_area_struct *vma)
{
        struct address_space *mapping;
 
        mapping = NULL;
        if (vma->vm_file)
                mapping = vma->vm_file->d_inode->i_mapping;
        if (mapping)
                spin_unlock(&mapping->i_shared_lock);
}
推荐将函数返回类型与函数名分成两行,使函数名顶头书写:

static void
remove_shared_vm_struct(struct vm_area_struct *vma)
{
        lock_vma_mappings(vma);
        __remove_shared_vm_struct(vma);
        unlock_vma_mappings(vma);
}
这样做的目的是,可以方便使用grep等工具,利用正则表达式,在很多源文件中快速定位某个函数定义所在的文件。

 

6.2 声明
在函数的声明(prototype)中保留参数的名字。虽然语法上这可以省略,但可以增强可读性:

extern long vread(char *buf, char *addr, unsigned long count);
而不是写成这样:

extern long vread(char *, char *, unsigned long);
 

6.3 返回值
有两类典型的函数返回值类型:

返回0表示成功,非0作为错误码返回并表示失败;
作为布尔值返回,非0表示成功,0表示失败。
为避免这种不统一造成混淆,规定如下:

如果函数行为是一些操作和命令,则使用第一种类型返回值;
如果函数行为是一判断,则使用第二种类型返回值。
对于其他返回实际结果的函数不在这限制之列。例如,指针类型的返回值,非NULL代表具体内容,NULL表示失败。

 

6.4 函数体
函数内部,变量定义部分与代码部分中间隔一空行。

代码之间根据逻辑关系可以加空行来分隔。

return与返回值之间加一空格,除非函数类型为void,此时return后面直接为分号。返回值不需要用括号括住,除非返回表达式很复杂,影响了可读性。

示例:

static int is_chained_kmem_cache(kmem_cache_t *cachep)
{
        struct list_head *p;
        int ret = 0;

        /* Find the cache in the chain of caches. */
        down(&cache_chain_sem);
        list_for_each(p, &cache_chain) {
                if (p == &cachep->next) {
                        ret = 1;
                        break;
                }
        }
        up(&cache_chain_sem);

        return ret;
}

 

 

7 预处理和枚举
7.1 基本规则
使用大写字母来命名宏常量和枚举中的符号。当定义一组相关的常量时,使用枚举比宏常量更合适。

“#”与include、define等之间没有空白,include、define等与后面的代码加一个空格(存在的话)。

预处理符号都是顶头书写,不缩进,即使在复杂的分支结构中:

#include <stdio.h>
#include "myheader.h"
#define MAXSIZE 100

#ifdef CONFIG_DEBUG_SLAB
#define DEBUG           1
#define STATS           1
#define FORCED_DEBUG    1
#else
#define DEBUG           0
#define STATS           0
#define FORCED_DEBUG    0
#endif
宏函数用小写字母来命名。注意使用括号来括住相关的表达式:

#define cc_entry(cache) ((void **)(((cache_t *)(cache)) + 1))
 

7.2 多语句宏
由多条操作语句组成的宏可使用do-while结构。如:

# define CHECK_PAGE(page)                                  \
        do {                                               \
                CHECK_NR(page);                            \
                if (!PageSlab(page)) {                     \
                        printk(KERN_ERR "bad ptr %lxh.\n", \
                                (unsigned long)objp);      \
                        BUG();                             \
                }                                          \
        } while (0)
 

7.3 宏的禁止写法
禁止以如下方式使用宏:

(1).改变程序流程:

#define FOO(x)                             \
        do {                               \
                if (blah(x) < 0)           \
                        return -EBUGGERED; \
        } while(0)
(2).依赖于本地变量:

#define FOO(val) bar(index, val)
(3).将宏函数作为左值:

        FOO(x) = y;
 

 

8 标号与goto
8.1 命名规则
标号使用大写字母命名,标号与后面的冒号(:)之间没有空格。

标号不缩进,顶头书写,单独成行。

 

8.2 goto的意义
goto有它存在的必要,在某些场合比非goto实现更合理。比如:

从多层嵌套循环中退出。
一个有多个初始化步骤的函数,每步失败都需要清理之前分配的所有资源。
       if (init1)
              goto INIT1_FAIL;
       if (init2)
              goto INIT2_FAIL;
       if (init3)
              goto INIT3_FAIL;
       ...
INIT3_FAIL:
       clean3;
INIT2_FAIL:
       clean2;
INIT1_FAIL:
       clean1;
 

8.3 goto的限制
虽然不禁止goto语句的使用,但也不能忽视goto的危害,故有如下一些限制:

goto与对应的标号都在一个函数内;
只能向后跳转,不能向前跳转。
 

 

9 命名
命名规则这里不做过多限定,只列出一些建议:

没必要使用ThisVariableIsATemporaryCounter这类的命名,而是使用tmp这样简短、容易书写,而且不难理解的名称。
当然,对于全局变量和全局函数,应该使用描述性将的命名,如count_active_users(),而不是cntusr()。
局部变量在不影响理解的情况下,应该简短,比如循环变量,可使用i,没必要用loop_counter。
 

 

10 typedef
不要滥用typedef。对于一般的结构和指针等类型,没必要用typedef来重命名,保留struct/union等关键字,可读性其实更好。

使用typedef的场合:

有意隐藏某些东西;
需要精确的数据类型,如u8/u16/u32;
屏蔽环境差异,如在一个地方需要int,在另一个地方需要long;
简化一些复杂的数据类型,如signal函数的说明
2009-11-18 20:12
zhagqn_Bc
Rank: 2
等 级:论坛游民
帖 子:33
专家分:19
注 册:2009-10-31
得分:0 
楼上太牛了  谢谢 支持
2009-11-19 12:34
fgchg911
Rank: 4
等 级:业余侠客
威 望:1
帖 子:131
专家分:204
注 册:2009-9-6
得分:2 
现在这世道,看别人的代码,能提高的也就是经验,水平,对风格的培养是
屁用都没,真的,现在的代码普遍没风格
你还是看看林锐的《高质量程序设计c/c++》吧
不会c++也没关系,那书对风格的培养很好,真的很好
风格 不仅仅是排版。。。还有很多
风格的提高也是代码质量的提高,水平的提高
2009-11-19 17:35
dong152liang
Rank: 2
来 自:廊坊
等 级:论坛游民
帖 子:56
专家分:29
注 册:2009-11-8
得分:2 
《The Elements of Programming Style》是一本很古老的书,尽管新奇的语言层出不穷,但这些,30 年的岁月依旧无法掩盖其中的真知灼见。

•    把代码写清楚,别耍小聪明。
•    想干什么,讲的简单点、直接点。
•    只要有可能,使用库函数。
•    避免使用太多的临时变量。
•    “效率”不是牺牲清晰性的理由。
•    让机器去干那些脏活。
•    重复的表达式应该换成函数调用。
•    加上括号、避免歧义。
•    不要使用含糊不清的变量名。
•    把不必要的分支去掉。
•    使用语言的好特性,不要使用那些糟糕的特性。
•    该用逻辑表达式的时候,不要使用过多的条件分支。
•    如果逻辑表达式不好理解,就试着做下变形。
•    选择让程序更简洁的数据表达形式。
•    先用伪代码写,再翻译成你使用的语言。
•    模块化。使用过程和函数。
•    只要你能保证程序的可读性,能不用 goto 就别用 。
•    不要给糟糕的代码打补丁 - 重写就是了。
•    把大的程序分成一小片一小片来写,分块测试。
•    使用递归程序来处理递归定义的数据结构。
•    正确和错误的输入数据都要测试。
•    确保输入不会超出程序的限制。
•    依靠文件结束来终止输入,而不是依赖一个记数。
•    把文件结束作为一个输入状态来处理。
•    识别出错误的输入;如果有可能就修复它。
•    让输入数据很容易构造出来,让输出数据不言自明。
•    使用统一的输入格式。
•    让输入容易校对。
•    如有可能,提供更自由的输入格式。
•    使用输入提示,允许使用默认值。并把它们显示出来。
•    把输入输出放到子程序里。
•    确保所有的变量在使用前都有初始化。
•    不要因为一个 bug 而停止不前。
•    打开编译程序的调试选项。
•    常量结构用数据声明初始化,变量结构用执行代码初始化。
•    小心 off-by-one 错误。
•    当循环中有多个跳出点时要小心。
•    如果什么都不做,那么也要优雅的表现出这个意思。
•    用边界值测试程序。
•    手工检查一些答案。
•    防御式编程 - 为不可能的情况写几句代码。
•    10.0 乘 0.1 很难保证永远是 1.0 。
•    7/8 等于 0 ,而 7.0/8.0 不等于 0 。
•    不要直接判断两个浮点数相等。
•    先做对,再弄快。
•    先使其可靠,再让其更快。
•    先把代码弄干净,再让它变快。
•    别为了获得一丁点“性能”就牺牲掉整洁。
•    让编译器做些简单的优化。
•    不要过分追求重用代码;下次用的时候重新组织一下即可。
•    确保特殊的情况是真的特殊。
•    保持简洁以获得速度。
•    不要死磕代码来加快速度 - 找个更好的算法。
•    用工具分析你的程序。在做“性能”改进前先评测一下。
•    确保注释和代码一致。
•    不要在注释里仅仅重复代码 - 让每处注释都有价值。
•    不要给糟糕的代码做注释 - 应该重写它。
•    给变量都起个有意义的名字。
•    把程序重新整理一下,让阅读代码的人更容易理解。
•    为你的数据布局写一个文档。
•    不要过分注释。
2009-11-19 17:47
xiefeng122
Rank: 3Rank: 3
等 级:论坛游侠
帖 子:126
专家分:139
注 册:2009-4-1
得分:2 
发个给你吧
高质量C++C_编程指南.rar (260.25 KB)
2009-11-19 17:51
zhagqn_Bc
Rank: 2
等 级:论坛游民
帖 子:33
专家分:19
注 册:2009-10-31
得分:0 
谢谢了  我会认真看的!!
2009-11-22 10:51
pgy
Rank: 11Rank: 11Rank: 11Rank: 11
来 自:C
等 级:小飞侠
威 望:8
帖 子:1248
专家分:2329
注 册:2009-9-23
得分:0 
回复 8楼 xiefeng122
嘿嘿,我也有,word版

我可好玩啦...不信你玩玩^_^
2009-11-22 13:14



参与讨论请移步原网站贴子:https://bbs.bccn.net/thread-292741-1-1.html




关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.439512 second(s), 9 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved