您的位置:首页 > 编程语言 > C语言/C++

c语言深刻解剖 回顾

2016-05-29 00:00 211 查看

1。使用register修饰符的注意点。

必须是能被CPU寄存器所接受的类型。这意味着register不能是数组,结构体之类的,必须是单个值,并且长度必须小于或等于整型的长度。

对register变量不能使用取址符,因为这个变量可能不会存储在内存中。而取址符仅针对在内存中存储的变量。

2。static

修饰变量:静态变量 局部、全局

修饰函数:普通的静态函数。这里的静态并不指存储方式,而是指函数的作用域仅限于本文件(编译单元),又称为内部函数。

类中的静态成员函数(C++中):表示该函数的调用不需要创建该类的实例。直接用ClassName.MemberFunction( )即可。

静态函数好处是:不同人编写不同的函数时,不用担心自己定义的函数是否与其他文件中的函数同名。

3。变量的命名规则:

尽量用最少的长度表达尽可能多的信息

当标识符由多个词组成时,每个词的 首字母大写。

对在多个文件间共同使用的全局变量或函数要加范围限定符(建议使用模块名(缩写)作为范围限定符),比如GUI_等。

命名规则:

模块名缩写(范围限定符)_作用域前缀_数据类型前缀_指针前缀_含义标识_数组/结构/后缀. ……多看别人的代码中命名来学习。

b boolean ; c char ; i int ; s short ; fp function pointer

下面是一些比较好的命名: DataGotFromSD / DataDeletedFromSD

所有的宏定义、只读变量、枚举常数全部用大写字母表示。

4.在转义字符中后面跟的数字被理解为八进制。用于表示ASCII码等于该值 的字符。

4。sizeof 运算符

//指针解引用sizeof测试
char* ch="hello";
std::cout<<sizeof(*ch)<<std::endl; //1 指针解引用求sizeof时得到的值是指针所指向类型占用内存大小。

//数组名加引用操作符求sizeof

int a[]={1,2,3,4};
cout<<sizeof(a)<<" "<<sizeof(&a)<<" "<<sizeof(&a[0])<<endl; //16,4 ,4 虽然在函数调用中,指针与整型数运算中&a表示一个数组指针,但在sizeof中好像刚好相反。前者表示数组整体长度,后者表示数组第一个元素的长度。

注:下标运算符的优先级高于取地址优先级。





5。char类型

单纯的char类型应该只用于字符值的存储和使用,有符号和无符号的“char”型变量只能用于数值的存储和使用。

单纯的char类型是有符号还是无符号是由编译环境决定的。

6。所有的无符号型常量都应该带有字母U后缀。

7。无符号数的输出:printf(“%u\n”,i);

8。bool变量与零值进行比较

对于布尔类型,在不同语言中间的定义可能不同,比如c/c++定义为1,vb则定义为-1。所以在if语句中不要写出下面的语句:

bool bTestFlag=TRUE;

if(bTestFlag==0) if(bTestFlag==1)

if(bTestFlag==TRUE) if(bTestFlag==FALSE)

而应该,写成下面这样,当然不仅仅是因为这样更简洁,更因为可以避免不同语言对TRUE/FALSE定义的差异带来的影响。

9.指针变量与”零值“进行比较

if(NULL !=p) if(NULL==p) //其他写法都有问题。NULL在不同的编译器下可能会有不同,不一定是全0。

10。编程时代码的缩进使用四个空格,而不要用TAB,因为不同编辑器下的效果不同。

c/c++中提倡的风格 函数或语句块的大括号总在一行的开头,而不是在一行结尾。

void Function( )



//program code



10。选择分支的排序

在if分支确定时,要把命中概率高的分支放在前面,把命中概率低的放在后面或放在else语句中。这样可以避免减少进行判断的次数。

11。else if 防御性编程

所有的if-else if结构应该由else子句结束,即使有时候你不知道在这种情况下有什么可以做的。而if之后直接跟else语句的情况不在本规则之内。

else if 后面跟的else语句是一种保护性编程的做法(defensive programming).else语句或者要执行适当的动作,或者要包含合适的注释以说明为何没有执行动作。这与switch语句中的要求最后有一个default子句是一致。在default子句中也可以添加一些相应的注释。添加default子句的一般写法:

default: //任何时候,将default子句只用于检查真正的默认情况。

break;

12。 case语句的排序顺序

通常,我们能够用到switch语句,说明需要的分支,比较多了,这时候如何对case语句排序至关重要。

规则1:如果所有的case语句的重要性差别不大,可以按照字典顺序进行排序。

规则2:按执行频率排序。把最长执行的代码放在前面,把最不常执行的放在后面。通常,最常执行的可能也是调试的时候要单步执行最多的代码。

13。关于程序长度

最好能够保证一个功能模块的能在一页放下,如果不行,可以将其进行拆分,写成几个子程序。然后进行调用。

14。多重循环语句

在不影响程序逻辑的情况下,将最长的循环放在内层,将最短循环放在外层,从而减少cpu跨切循环层的次数,从而提高执行效率。

15。for循环中第三个分句的写法

采用半开半闭区间写法

for( n=0;n<10;n++) //在这种情况下循环执行的次数等于10-0=10次,而不像闭区间写法那样,9-0+1=10。

16.在c/c++中,如果一个函数没有显式指定返回值,会被认为返回int类型。

在c语言中中,有些编译器允许给无参数的函数传送任意类型的参数。

17。标准库中使用void*类型参数的例子。

void*类型的参数可以接收任意类型的指针的实参。

void *memcpy(void *dest, const void* src,size_t len); //左边第一个参数是目标指针。

void *memset(void * buffer,int c ,size_t num);

这两个函数真正体现了内存操作函数的意义,因为它操作的对象仅仅是一片内存区域,无所谓这片内存是什么类型。

18。函数中避免返回一个局部对象的引用。

这里不仅指面向对象中的局部对象。还包括类似下面的这种情况。

char *func(void){

char str[30];

return str;

}

str属于局部变量,位于栈内存中,在Func结束的时候被释放,所以返回str将导致错误。

对于函数的返回值,要注意检查一点:返回的这个对象在函数结束时是否还存在。或者说相关变量对应的内存空间在函数结束后是否已经被回收。

19。const关键字在c和c++中的含义是不同的。例如:

const int Max =100;

int Array[Max];

在c文件中,对上述代码编译器会提示出错,而在CPP文件中则顺利运行。这表示出在C语言中,const修饰的Max仍然是变量,只不过是只读属性罢了,而在C++里,扩展了const的含义,这里就不讨论了。

20。#define与const

const定义的只读变量从汇编的角度来看,只是给出了对应的内存地址,而不是像#define一样给出的是立即数,所以,const定义的只读变量在程序运行过程中只有一份备份(因为它是全局的只读变量,存放在静态区),而#define定义的宏常量在内存中有若干个备份。

21。const可以用于修饰数组,例如:const int a[5]={1,2,3,4,5};

22.const修饰函数返回值。表示函数返回值不能作为左值。

23。const volatile int cnt=10; 这条语句是合法的。

24。const-存在于编译期的只读变量

编译器通常不为普通const变量分配存储空间,而是将它们保存在符号表中,这使得它们成为一个仅存在于编译期间的值,没有了存储和读内存的操作,使得它们效率也很高。

25。关于extern

extern用于对在其他编译单元中的

“extern”在函数声明中可有可无,只是用来标志该函数在本文件中定义,还是在别的文件中定义。

extern “C”通常用在.h文件中,通常这些文件中通常提供了第三方库的函数接口。

如果一个extern语句中有初始化动作,则这个语句是一个外部变量的定义,而不可能是声明。一个全局变量的定义中最好不要使用extern,只在声明的时候使用。

a)当要使用一个在其他编译模块(cpp文件)中使用的变量或函数时,可以采用#include头文件的方法或者使用extern语句。两者可以完成同样的功能。

但如果全局变量是在头文件中定义的,这时候只有包含头文件一种方法,因为头文件总是被包含到其他cpp文件(直接或间接地)中被编译。实际上你如果在一个cpp文件不包含头文件,而只是extern其中的全局变量会提示“无法解析的外部符号”这样的错误。

b)全局变量在全局范围内都不能重复,所以在头文件中尽量不要放全局变量的定义,而应将定义放在cpp文件中。而应该只是声明,防止由于在多个地方引用同一个头文件造成重复定义的错误。

c)如果全局变量的定义不在默认作用域,则其声明也应该在同一作用域。

26。单方面修改extern 函数原型

  当函数提供方单方面修改函数原型时,如果使用方不知情继续沿用原来的extern申明,这样编译时编译器不会报错。但是在运行过程中,因为少了或者多了输入参数,往往会照成系统错误,这种情况应该如何解决?
  答案与分析:
  目前业界针对这种情况的处理没有一个很完美的方案,通常的做法是提供方在自己的xxx_pub.h中提供对外部接口的声明,然后调用方include该头文件,从而省去extern这一步。以避免这种错误。宝剑有双锋,对extern的应用,不同的场合应该选择不同的做法。

27。static变量

static修饰的全局变量声明与定义同时进行,也就是说当你在头文件中使用static声明了全局变量后,它也同时被定义了;

最后,static修饰全局变量的作用域只能是本身的编译单元,也就是如果在一个头文件myheader.h定义了一个static变量,你在两个cpp文件中引用了这个头文件,这时候在这两个cpp文件中的static变量相互之间不会影响。

28。全局变量与命名空间

如果一个全局变量是在某个命名空间中进行定义的,那么在其他地方使用这个全局变量时,也必须using 相应的命名空间,否则你无法访问识别和访问相应的变量。当然你也可以采用在使用时指定命名空间的做法。因为默认情况下命名空间域是全局域。

貌似在头文件中无法使用using语句引入命名空间。

在cpp文件中引入的命名空间也必须是在别的头文件中定义的命名空间,而不能是别的cpp文件中定义的。

main函数一定不能放在某个自定义的命名空间里。

关于c++中命名空间和全局变量的问题-C/C++-ChinaUnix.net
http://bbs.chinaunix.net/thread-787243-1-1.html

C++ :: 的用法小结 - whz_zb的专栏 - 博客频道 - CSDN.NET
http://blog.csdn.net/whz_zb/article/details/6843369

c++中冒号(:)和双冒号(::)的用法 - SQC - SegmentFault
https://segmentfault.com/a/1190000000345680

29。typedef int int32;

unsigned int32 j=10; //这里会报错,因为typedef取的别名不支持这种类型扩展,即在重命名的类型名前后添加unsigned等修饰符。

30。 int * p1,p2; //p2只是一个int类型,而不是一个int*

而typedef int* pInt;

pInt p1,p2;//这里则是定义了两个int*

30. y=x/* p; //由于/*在c语言里有特殊的意思,所以这里应该写成y=x/(*p); 注意如果/ *之间有空格的话,不会被当作注释。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: