C语言书籍阅读-读书笔记与编程比赛

最近闲下来想找机会参加一些编程比赛锻炼锻炼,可没有一个相关网站可以搜集这些信息,因而自己记录下来:

百度之星,http://star.baidu.com/index/developer.时间07.25初赛,10月决赛。

平安产险极客编程赛.http://pingancx.zhaopin.com/.时间初赛4月9日开始,4月30日作品提交结束,决赛5月24日.

Momenta·HackPKU2018 北京大学黑客马拉松。www.hackpku.com40h,比赛时间:2018年5月11日 ~5月13日,报名时间4月20日前.

计蒜之道,2018编程比赛.https://dao.jisuanke.com/.时间5.19 19:00至22:00.

美团CodeM.https://www.nowcoder.com/activity/2017codem/index?from=meituan.初赛时间:6月11日,决赛时间7月25日.

中兴捧月算法工程师.http://challenge.zte.net/.4月16日开始,5月20日提交截止,6月中旬决赛.

华为软件精英挑战赛.http://codecraft.devcloud.huaweicloud.com/.3月9日公布赛题,4月15日开始,5月19日决赛.

黑客马拉松.https://segmentfault.com/events?category=0&city=110100.时间不定.

阿里24小时极客挑战赛(现场赛),7月15日14:00-7月16日14:00


1、《高质量程序设计指南》--C/C++,林锐

规则1 软件属性:(软件有很多属性,易用性、可靠性、可移植性等,有的望文生义,有的则难以理解)

1)健壮性,是指在异常的情况下,软件能够正常运行的能力,包括容错能力恢复能力容错能力是指,发生异常情况时系统不出错误的能力恢复能力是指,软件发生错误后,重新运行时,能否恢复到没有发生错误前的状态的能力

2)可靠性,是指在一定环境下,在给定的时间内,系统不发生故障的概率。本来是硬件领域的词,但其实软件也有用到,比如“内存泄漏”与“误差累积”都会在给定时间持续运行时发生错误,类似 于硬件长时间工作发生“罢工”现象。

规则2 C++面向对象程序设计方法概述

规则2.1 信息隐藏于类的封装特性  

  关键字public、protected和private用于声明哪些数据和函数是可以公有的、受保护的或者是私有的,这样可以达到信息隐藏的目的,即让类仅仅公开必须让外界知道的内容,而隐藏其他一切内容。
class WhoAmI
{
  public:
  void GetMyName(void);  //名字是可以公开的
  protected:
  void GetMyAsset(void); //财产是受保护的,只有我和继承者可以使用
  private:
  void GetMyGuilty(void);//罪过是受保护的,只有我自己知道,哈哈哈
  ...
};

规则2.2 类的继承特性

  如果A是基类,B是A的派生类,那么B将继承A的数据和函数,可以提高程序的可复用性。

class A
{
  public:
  void Func1(void);
  void Func2(void);
};
class B:public A
{
  public:
  void Func3(void);
  void Func4(void);
};
main()
{
  B b;
  b.Func1();  //B从A继承了函数Func1
  b.Func2();  //B从A继承了函数Func2
  b.Func3();  //
  b.Func4();  //
}

规则2.3 动态特性

  在绝大多数下,程序的功能是在编译时就确定下来的,如果程序的功能是在运行时刻才确定下来,称之为动态特性,C++的虚函数、抽象基类、动态联编和多态性构成了动态特性。

规则2.3.1 虚函数

  假定几何形状的基类为Shape,其派生类有Circle、Rectangle、Ellipse等,每个派生类都有能够绘制自己的形状,不管派生类的形状如何,我们希望用统一的方式来调用绘制函数Draw(),并让程序在运行时动态地确定应该使用哪个派生类的Draw函数。

class Shape
{
  public:
    virtual void Draw(void);
};
class Rectangle:public Shape
{
  public:
    virtual void Draw(void);
}; 

  我们将基类的Shape中的函数Draw声明为虚函数,然后在派生类中重新定义了Draw使之绘制正确的形状。

规则2.3.2 抽象基类

  在很多情况下,定义那些不被实例化为对象的类是很有用的,这种类称为抽象类,能够被实例化为对象的类为具体类抽象类的唯一目的是让其派生类继承它的函数接口,因此它通常也被称为抽象基类。

  如果将基类的虚函数声明为纯虚函数,那么该类就成为抽象基类,纯虚函数是在声明时其“初始化值”为0的函数。

class Shape  //Shape是抽象基类
{
  public:
    virtual void Draw(void)=0;;//Draw为纯虚函数
};

  抽象基类Shape的纯虚函数Draw根本不知道它自己能干什么,具体功能必须由派生类的Draw函数来实现。

规则3 文件结构

规则3.1 头文件结构

  头文件由三部分内容组成:

  1)头文件开头处的版权和版本声明

  2)预处理板块

  3)函数和类结构声明等。

  头文件的作用:

  1)通过头文件来调用库功能

  2)头文件能加强类型安全检查,某个接口被实现或者被使用时的方式如果与头文件中的声明不一致,编译器就会指出错误,这一简单的规则是大有裨益。

  3)头文件可以增强程序的可读性,更为清晰。

规则4 程序的版式

规则4.1 空行

4.1.1 在每个类声明之后,每个函数定义结束之后都要加空行

4.1.2 在一个函数体内,逻辑上密切相关的语句之间不加空行,其他地方应加空行分隔。

//空行
void Function1(...)
{
  ...
}
//空行
void Function2(...)
{
  ...
}
//空行
void Function3(...)
{
  ...
}

规则4.2 代码行

4.2.1 一行代码只做一件事情,如只定义一个变量,或只写一条语句,这样代码容易阅读,并且方便于写注释。

4.2.2 if、for、while、do等语句自占一行,执行语句不得紧跟其后,不论执行语句有多少都要加{},这样可以防止书写失误。

if(width < height)
{
  dosomenthing();
}
//不要都写在一行,分行写,一行代码只做一件事情
int width  = 10;  //宽度
int height = 10;  //高度
int depth  = 10;  //深度

4.2.3 尽可能在定义变量的同时初始化该变量

规则4.3 代码行内的空格

4.3.1 关键字之后要留空格,像const、virtual、inline、case等关键字之后至少要留一个空格,否则无法辨析关键字,像if、for、while等关键字之后应留一个空格再跟左括号“(”,以突出关键字。

4.3.2 函数名之后不要留空格,紧跟左括号“(”,以与关键字区别。

//良好的风格
void Func1(int x, int y, int z);
if (year >= 2000)
if ((a>b) && (c<d))
for (i=0; i<10; i++)

4.3.3 对齐

程序的分界符“{”和“}”应独占一行并且位于同一列,同时与引用它们的语句左对齐。

{ }之内的代码在“{”右边数格处左对齐。

//良好的对齐习惯
void Function(int x)
{
  ...//program code
}
if (condition)
{
  ...//program code
}

4.3.4 修饰符的位置

应当将 * 与 & 紧靠变量名,例如:char *name;int *x, y;//此处y不会被误解为指针

规则5 命名规则

5.1 共性规则

5.1.1 直观且可以拼读

5.1.2 命名规则与所采用的操作系统或开发工具的风格保持一致,例如Windows标识符通常采用“大小写”混排的方式,如AddChild,而UNIX应用程序的标识符通常采用“小写加下划线”的方式,如add_child。

5.1.3 变量的名字应当使用“名词”或者“形容词+名词”。

5.1.4 避免名字中出现数字编号,如Value1、Value2等,除非逻辑上的确需要编号。

这是为了防止程序员偷懒,不肯为命名动脑筋而导致产生无意义的名字,就像没有人会把自己的子女叫做张三与李四。

5.1.5 类名与函数名用大写字母开头的单词组合而成

class Node;
class LeafNode;
void Draw(void);
void SetValue(int value);

5.1.6 变量与参数用小写字母开头的单词组合而成

BOOL flag;
int drawMode;

5.1.7 常量全用大写的字母,用下划线分割单词

const int MAX = 100;
const int MAX_LENGTH = 100;

5.1.8 静态变量加前缀 s_ (表示static)

void Init()
{
  static int s_initValue;
}

5.1.9 如果不得已需要全局变量,全局变量加前缀 g_ (表示global)

int g_howManyPeople;
int g_howMuchMoney;

规则6 表达式与基本语句

6.1 不可将浮点变量用“==”或 “!=”与数字比较,应该设法转化成“>=”

假设浮点变量的名字为x,应该将

if (x == 0.0)                    
应转化为
if ((x >= -EPSINON) && (x <= EPSINON))
//EPISINON 是允许的误差(精度)

6.2 在多重循环中,应该将最长的循环放在最内层,最短的循环放在最外层,以减少CPU跨切循环层的次数。长放里,短放外。

for(col = 0; col<5; col++)
{
  for(row=0; row<100; row++)
  {
    sum = sum + a[row][col];
  }
}

6.3 建议在for语句中,循环控制变量的取值采用“半开半闭区间”的写法

for(int x=0; x<N; x++)
{
  ...
}

起点到终点的间隔为N,循环次数为N,简洁易懂。

6.4 断言

  断言(assert)是仅在Debug版本起作用的宏,用于检查“不应该”发生的情况,在运行过程中,如果 assert 的参数为假,那么程序就会终止,一般还会出现提示对话,说明在什么地方引发了assert。

void *memcpy(void *pvTo, const void *pvFrom, size_t size)
{
  assert((pvTo != NULL) && (pvFrom != NULL));
  byte *pbTo = (byte *)pvTo;                //防止改变 pvTo 的地址
  byte *pbFrom = (byte *)pvFrom;            //防止改变 pvFrom 的地址
  while(size-->0)
  {
    *pbTo++ = *pbFrom++;
  }
  return pvTo;
}

   如果程序在 assert 处终止了,并不是说含有该assert的函数有错误,而是调用者发生了差错,assert 可以帮助我们找到发生错误的原因。

规则7 内存管理


《C语言剖析》:

1.1 C语言关键字32个

1) auto 声明自动变量,缺省时编译器默认为auto。         2) int   声明整型变量

3) double 声明双精度变量                                              4) long 声明长整型变量

5) char 声明字符型变量                                                   6) float 声明浮点型变量

7) short 声明短整型变量                                                  8) signed 声明无符号类型变量

9) unsigned 声明无符号类型变量                                   10) struct 声明结构体变量 

11) union 声明联合数据类型                                          12) enum 声明枚举类型

13) static 声明静态变量                                                  14) switch 用于开关语句                                               

15) case 开关语句分支                                                   16) default 开关语句中的“其他”分支                          

17) break 跳出当前循环                                                 18) register 声明寄存器变量                                          

19) const 声明只读变量                                                  20) volatile 说明变量在程序执行中可被隐含地改变

21) typedef 用以给数据类型取别名                                 22) extern 声明变量是在其他文件中声明                        

23) return 子程序返回语句                                             24) void 声明函数无返回值或无参数,声明空类型指针    

25) continue 结束当前循环,开始下一轮循环                 26) do 循环语句的循环体                                                

27) while 循环语句的循环条件                                       28) if 条件语句                                                               

29) else 条件语句的否定分支                                         30) for 一种循环语句                                                     

31) goto 无条件跳转语句                                              32) sizeof 计算对象所占内存空间大小(披着函数皮的关键字)

1.2 声明与定义

A)int i;B)extern int i;

  定义是指(编译器)创建一个对象,为这个对象分配一块内存并给它取一个名字,这个名字就是我们所说的变量名或者对象名。一个变量或对象在一定的区域内只能被定义一次,如果定义多次,编译器会提示重复定义同一个变量或者对象。

  声明是告诉(编译器)这个名字已经匹配到一块内存上了,下面的代码用到的变量或对象是在别的地方定义的,声明可以出现多次。定义与声明最重要的区别是:定义创建了对象并为这个对象分配了内存,声明没有分配内存。

1.3 signed 与 unsigned 转换

1)i+j 的结果居然为 4294967286 ,有符号与无符号数相操作时,有符号数会转化为无符号数

2)i为无符号数,不会小于0,因而for循环将无限循环。


1.4 memecpy与 memset 函数

void *memcpy(void *dest,const void *src,size_t len);
void *memset(void *buffer, int c, size_t num);
//将IntArray_a 清0
int IntArray_a[100];
memset(IntArray_a, 100*sizeof(int));
//将 srcintarray_a 拷贝给 destIntArray_a
int destIntArray_a[100],srcintarray_a[100];
memcpy(destIntArray_a, srcintarray_a, 100*sizeof(int));

1.5 volatile 变量

volatile int i = 10;
int j = i;
int k = i;
  volatile 关键字告诉编译器i是随时可能发生变化的,每次使用它的时候必须从内存中取出i的值,因而编译器会重新从i的地址读取数据放在k中。如果i是一个寄存器变量或者表示一个端口数据或者是多个线程的共享数据,就容易出错,所以说volatile可以保证对特殊地址的稳定访问。

1.6 #pragma pack(n)

  使用指令 #pragma pack(n),编译器将按照n个字节对齐。

  使用指令,#pragma pack(),编译器将取消自定义字节对齐方式。

1.7 指针的移动

int main()
{
  int a[5] = {1,2,3,4,5};
  int *ptr = (int *)(&a + 1);
  printf("%d,%d",*(a+1),*(ptr-1));
}
   对指针进行加1操作,得到的是下一个元素的地址,一个类型为T的指针的移动,是以sizeof(T)为移动单位 ,a 是一个一维数组,&a+1,移动 &a + 5*sizeof(int); 将超过数组的界限,*(ptr-1),因为ptr为int* 类型,所以*(ptr-1)是指向a[4],即5. *(a+1),即为a[1].

1.8 数组指针与指针数组

int *p1[10];            //指针数组   int *(p1[10])
int (*p2)[10];          //数组指针

int *p1[10];    [] 的优先级比* 要高,因而,p1将先与[]结合,所以 int *p1[10]; 中p1为数组,它里面的元素都是指针,因而是指针数组。

int (*p2)[10]; (*p2)说明为指针,int 修饰的是数组的内容,即数组的每个元素,数组在这里没有命名,这里的p2是个指针,指向一个包含10个int类型数据的数组,即数组指针。

1.9 二级指针

一级指针保存的是数据的地址,二级指针保存的是一级指针。

2.0 数组传递

c语言中,当一维数组作为函数参数的时候,编译器总是把它解析成指向其首元素地址的指针。





《c专家编程》:




《ARM构架》


发布了19 篇原创文章 · 获赞 32 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/wangwangmoon_light/article/details/79942374