c语言01

来源:互联网 发布:ip变换软件 编辑:程序博客网 时间:2024/05/01 12:30

C语言01

编写c语言的目的:

根本————代码正确、可执行

提升————代码可读性高(易读、易懂、注释明确、符合规范)、高效

完美————健壮、可移植

第一步的根本自然不必多说;代码写完之后,在测试的时候难免要修改,而且大项目的开发一般需要多人协作,因此除了个人要保证自己代码可以被他人看懂之外,还要保证自己代码规范可以与他人完美组合,没有冲突,这就需要有统一的规范——一般把一些默认的规范作为标准。在这个基础上,代码可以协作执行了,考虑代码效率问题,完美时间复杂度与空间复杂度的优化;最后,考虑到操作系统平台的差异,需要提升代码移植性,代码健壮性。使代码可以跨平台移植,且代码可以保证长久的健壮,不会被丢弃,少补丁。

 

现在,来分析一下c中常见的编程错误和优秀编码规范吧

1、注意=与==的区别——前者为赋值,后者为判断

if(100==a[i])与if(a[i]==100)前者较好,可以避免把==写成=造成的错误

2、&&与||的操作

与操作——对于&&操作,例如if((a>1) && (b++)),如果a>1成立,则执行b++;但是如果a>1不成立,后面的不会再执行

或操作——对于||操作,例如if((a>1) || (b++)),如果a>1不成立,则执行b++;但是如果a>1成立,后面的不会再执行

如此使用,第一个条件的判定对于第二个条件是否执行影响巨大,可能会间接影响全局,因此使用时注意

例如:

a=1;b=3; if((a>1) && (b++)) c=5;                 结果为 a=1 b=3

a=2;b=3; if((a>1) && (b++)) c=5;                 结果为 a=2 b=4

3、这个不是陷阱,是一个在c陷阱与缺陷里面十分恐怖的表达式——(*(void(*)())0)();

看着脑袋就大了,这个表达式是执行0所在地址的程序,现在我们来分析一下(力争逐步分析):

第一步——什么是指向函数的指针,例如int (*pt)();这个函数就是指向函数的指针,pt指向这个函数的入口地址(函数是在内存之中的,pt指向函数的入口);那么如何调用函数呢,(*pt)();表示从pt指向的地址开始执行函数

第二步——代码从0开始执行,使用(*0)()不就ok了吗?不是如此,因为*后面必须跟着一个指针;而且这个指针必须是函数指针;

 

首先定义一个函数指针 void(*pt)();

然后强制转换赋值,把0强制转换为函数指针 pt =(void(*)()) 0;

最后执行操作 (*point_0_addr)();

将三个操作合并成一个,即为 (*(void(*)())0)();

4、switch操作——注意break在其中的作用。break与continue的区分

5、结构使用时,如果结构为指针,使用”->”;否则使用”.”

6、malloc分配内存,必须及时释放,否则会造成内存泄漏(最好加上限制,保证不越界)

7、数组与指针

数组:

>1、仅支持一维数组,但数组的元素可是任意类型的数据。(数据可以为数组,保证了多维数组的实现)

>2数组的大小必须在编译初期作为常数指定。(需要分配空间进行初始化)

>3数组的索引值从0开始。

>4仅能确定数组大小,以及数组下标为0的元素指针。

指针的特点:

>1指针本身的大小都是一样的,仅与机器位宽有关(32位系统就是32/8=4字节;64位系统就是64/8=8字节)

>2任何指针都是指向某种类型的变量(指针是可变的,除非加上const的限制)

>3一个指针只能指向一个地址,一个地址可以被任意多个指针指向

 

8、宏定义的使用,避免冲突

#ifndef NUM

#define NUM 100

#endif

9、——尽量不用全局变量,不得已使用的话,一定用g_开头标示出来;

——宏定义 务必全用大写,不要用’_’开头;——'_'开头的是c内部定义的,避免冲突

——枚举务必全用大写;

目的是为了尽快区分,避免代码混乱和造成冲突

10、一个变量只有一个功能,禁止重复使用;

例如:int ret;...;ret = result1();…; ret = result2();…;return0;

              int ret1,ret2;...;ret1 = result1();…;ret2 = result2();…;return 0;

即使对操作无任何影响,但是在比较复杂的代码中前者会造成代码可读性下降

在不同的类型变量操作中,特别注意默认类型转换和强制类型转换造成的数据精度丢失

建议所有的常量使用const全局变量,而不是用宏定义——(这个建议是为了反编译,假如代码出错了,而且从机器指令中发现错误的地址,此时需要反编译得到汇编语言,来查询地址操作,定义为const反编译的代码容易理解)

11、函数的参数尽量不要超过四个

因为一个进程的函数执行最多分配4个寄存器来存储参数,多的存到栈中,栈中数据的获取比寄存器慢的多~~~如此要求只是为了提高代码的执行质量

——函数的参数尽量采用整型长度

因为对于32位系统,int为4字节(4*8=32位);对于64位系统,int为8字节(8*8=64位)。指针与int总是保持一致。传递int与指针可以保证寄存器可以完全的存储(不需要做其他的操作),加快处理速度。因此,最好使用int或指针传递。

12、int a=0x12345678;char *p;p=&a;      得到*p = 120

这里定义了一个整型数据a,然后把它的地址给了p,

解析:a是一个整型,有四个字节,低位在前,高位在后存储方式,按顺序——78(字节1)、56(字节2)、34(字节3)、12(字节4)

现在q就是78(字节1))的地址,输出为7*16+8=120。不同的系统可能不同,也可能位高位在前,低位在后存储方式,此时输出的结果为1*16+2=18

13、结构struct的大小

struct point{

char b;

char c;

double d;

int a;};

使用sizeo(point)结果为24; 如果把struct中的int a与double d换个位置,使用sizeo(point)结果为16。

原因:

struct内部以字节最大的数据为基准,进行匹配,就如上面的double最大,为8字节,所以struct就以8字节为最小单位,因为之前定义的不足8字节(两个char为2字节),所以看作8字节,在double之后的int也被看作8字节,结果为24。

调换位置之后,double变为最后一个,之前的三个加起来不足8字节,因此结果为16

14、关于getchar()一类函数的问题

Int a;

a=getchar();

需要注意的是getchar();返回值为int(因为可能为EOF),所以使用int而不是char来接收结果(EOF为-1,char表示不了)

15、在函数中返回指针

理解之后——举例一个函数操作

char* fun1(){

char a[10] =“kkkkkkkk”;

char *p = a;

return p;}

如果执行这个函数  fun1,在栈区分配空间,p为指向栈区的指针。因为函数执行完之后,栈区会被自动回收,因此返回p无法找到“kkkkkkkk”这个字串。

16、<<与>>的注意

按位操作运算符:

<<左移

>>右移

左移就是带着符号位左移,没有区别(高位移出,低位补0)

右移是与unsigned有关的:

1、假如为unsigned的无符号定义,则直接右移,高位补0

2、如果不是unsigned,最高位符号位不动,次高位根据符号位补充。即符号位为1,则补1;符号位为0,则补0

 

 

 

 

0 0
原创粉丝点击