K&R the C programming language——study notes

来源:互联网 发布:制作投票的软件 编辑:程序博客网 时间:2024/06/11 10:06

1.p7 关于打印字符

%d 按照十进制整数打印

%3d 按照十进制整数打印,至少3个字符宽,且在打印区域内右对齐

%f 按照浮点数打印

%6f  按照浮点数打印,至少6个字符宽

%.2f 按照浮点数打印,小数点后有两位小数

%6.2f 

\t \n  

printf("Hello,World\n");

2.p9 符号常量 在C++中是用const实现的,

const int buffer_size=250;

而在C中,处理"幻数"的方法是给予它们有意义的名字。#define可以把符号名(符号常量)定义为一个特定的字符串

#define UPPER 300#define LOWER 0#define STEP 20

符号常量通常用大写,与变量区分,符号常量不需要出现在声明中。且记住define后面没有分号

3.p10

一次读写一个字符

getchar() putchar()

while((c=getchar())!=EOF){putchar(c);}

EOF表示end of file文件结束符号


4.p14 单词计数

用字符常量来表示状态0,1

nc=nw=nl=0 将3个变量初值都设为0

5.p21 自己书写getline函数读入输入的下一行,保存至传递的数组实参,并且返回数组长度

int getline(char s[],int lim){int i,c;for(i=0;i<lim-1 && (c=getchar())!=EOF && c!='\n';++i)s[i]=c;{s[i]=c;++i;}s[i]='\0';return i;}

6.p29 常量表达式是仅仅包含常量的表达式,这种表达式在编译时求值,而不在运行时求值

回顾符号常量#define UPPER 100

7.strlen

int strlen(char s[]){int i;i=0;while(s[i]!='\0')++i;return i;}

8.enum简化符号常量的书写 

enum month {JAN=1,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC};


9.回顾在函数模板部分,sum函数使用强制类型转换,返回较大的格式

template<typename TypeA,typename TypeB> TypeA sum(TypeA & m,TypeB & n)

static_cast<long>(s);


10.如果表达式左边的变量重复出现在表达式的右边,如i=i+2

可以简写为i+=2

复合赋值运算符


11.while(cin>>i)就如同while((c=getchar())!=EOF)一样重要 P41


12.bitcount计算整形数中为1的二进制位数

int bitcount(unsigned x){

int b;

for(b=0;x!=0;x>>=1)

if(x & 01)

b++;

return b;

}


13.p58

C中函数的声明和使用与C++中不一样,extern

14.p71

寄存器变量register声明告诉编译器,它所声明的变量在程序中使用频率较高。其思想是,将register变量放在机器的寄存器中,这样可以使程序更小、执行速度更快。

register int x;

15.printd 打印十进制数n

16.p75

使用递归写reverse 

void Func(char* p, int N)  {      assert(p != NULL);      if (N == 1)          return;      else      {          char ch = p[0];           p[0] = p[N-1];          p[N - 1] = ch;          Func(p+1,N-2);      }  }  

测试代码:  

    char p[] = "ABCDEFGHI";  

    Func(p, 9);  


17.C预处理器

预处理器是编译过程中单独执行的第一个步骤。最常用的预处理器命令#include指令(用于在编译期间把制定文件的内容包含进当前文件中)和#define指令。

宏定义的形式如下

#define 名字 替换文本

后面所有出现名字记号的地方都将被替换为替换文本。宏的定义看上去是函数调用,但实际上只是直接将替换文本插入到代码中。

比如

#define Max(A,B) ((A)>(B)?(A):(B))x=Max(p+q,r+s);//就是替换为x=Max((p+q)>(r+s)?(p+q):(r+s))

但是使用宏时要注意几个情况,其一,上面的参数表达式要重复计算两次,而且如果表达式存在副作用,比如

Max(i++,j++)就会出错

第二,#define square(x) x*x

这样的宏定义是错的,比如square(x+1),所以在使用宏定义时一定要注意括号的使用。

18.宏是十分有用的,比如<stdio.h>中的getchar与putchar常常被定义为宏,这样可以避免处理字符时调用函数所需的运行时开销。<ctype.h>头文件中的函数也常常是通过宏实现的。这与C++的inline函数机制是类似的。函数比表达式的开销要大。调用函数的过程转变为宏调用也就是将替换文本插入代码的过程。再次强调,宏调用并非函数调用,而是将替换文本插入代码的过程。

使用#undef getchar取消名字的宏定义,这样可以保证之后的调用都是函数调用而不是宏调用

19.assert(p!=NULL);很常用,其中p是指向某一类型的指针变量


19.assert(p!=NULL);很常用,其中p是指向某一类型的指针变量


20.p78.  可以使用条件语句对预处理本身进行控制

比如用条件语句来确定包含哪个头文件,测试系统变量

#if SYSTEM==SYSV        #define HDR "sysv.h"#elif SYSTEM==BSD        #define HDR "bsd.h"#else         #define HDR "default.h"#endif#include HDR

再次强调区分宏调用与函数调用

21.p79 指针是一种保存变量地址的变量。通常的机器都有一系列连续编号或编址的存储单元,这些存储单元可以单个进行操纵,也可以连续成组进行操纵。通常情况下一个字节可以存放一个char类型的数据,两个相邻的字节存储单元可存储一个short类型的数据,而4个相邻字节存储单元可存储一个long类型的数据。指针是能够存放一个地址的一组存储单元。

&取地址操作符只能用于内存中的对象,即变量和数组元素,不能用于表达式,常量或register类型的变量。

回顾变量和对象的定义

&和*的优先级比算术运算符要高


22.指针的应用一 p81

由于非引用形参的函数无法改变实参,只能改变实参的局部副本,因此我们可以利用指针改变实参。swap函数


23.p83.指针与数组

p[3]   *(p+3) a[3]

都是等价的,所有可以用下标操作符的都可以用指针来实现,而且用指针编写的程序比使用下标操作符更快


24. p91

void strcpy(char *s,char *t){    while((*s++=*t++)!='\0')    ;}
但其实该字符值和空字符比较的代码是多余的,因为只要判断表达式是否是0即可。当是空字符时,表达式本身是0,所有上面的代码进一步精炼为
void strcpy(char *s,char *t){    while(*s++=*t++)    ;}

类似地,我们可以编写strcat的代码

int strlen(char *s){    int i;    while(*s++)        i++;    return i;}void strcat(char *s,char *t){    while(*(s+strlen(s))++=*t++)        ;    }

25.p96多维数组

C语言中,多维数组就是特殊的一维数组,只不过每个元素都是一个一维数组,

向函数传递多维数组的方式,一下几种是等价的

foo(int day[][10])foo(int day[2][13])foo(int (*day)[13])

指向多维数组的第一个元素的指针


26.

static对象,静态变量

全局变量声明之前加上static,就是静态全局变量。全局变量和静态全局变量都是静态存储的;在存储上无区别。区别在于他们的作用域;全局变量的作用域是整个源程序,当源程序有多个源文件组成时,全局变量在各个源程序文件都是有效的;而静态全局变量怎被限制了作用域,只在定义该变量的源文件内有用在同一程序的其他源文件不能使用!
还有静态全局变量只初始化一次,防止在其他源文件调用!而全局变量可以再其他源文件初始化!

"静态变量”这一术语有两个容易混淆的定义:

语言无关的通用定义:与程序有着相同生命周期(英语:Object lifetime)的变量;

C族语言特有的定义:以static存储类声明的变量。

静态局部变量属于静态存储方式,它具有以下特点:

(1)静态局部变量在函数内定义,但不像自动变量那样,当调用时就存在,退出函数时就消失。静态局部变量始终存在着,也就是说它的生存期为整个源程序。

(2)静态局部变量的生存期虽然为整个源程序,但是其作用域仍与自动变量相同,即只能在定义该变量的函数内使用该变量。退出该函数后, 尽管该变量还继续存在,但不能使用它。

(3)允许对构造类静态局部量赋初值。若未赋以初值,则由系统自动赋值。数值型变量自动赋初值0,字符型变量赋空字符。

(4)对基本类型的静态局部变量若在说明时未赋以初值,则系统自动赋予0值。而对自动变量不赋初值,则其值是不定的。 根据静态局部变量的特点, 可以看出它是一种生存期为整个源文件的量。虽然离开定义它的函数后不能使用,但如再次调用定义它的函数时,它又可继续使用, 而且保存了前次被调用后留下的值。 因此,当多次调用一个函数且要求在调用之间保留某些变量的值时,可考虑采用静态局部变量。虽然用全局变量也可以达到上述目的,但全局变量有时会造成意外的副作用,因此仍以采用局部静态变量为宜。


27.p97指针数组的初始化

区分指针数组和多维数组

首先,回顾一下向函数传递多维数组的三种等价方式

foo(int a[3][4])foo(int a[][4])foo(int (*a)[4])

核心在于a[3][4]的数组名a会隐式类型转化为指向数组的第一个元素即子数组的指针。int (*ptr)[4]=3;

而多维数组与指针数组的区别见p100

在多维数组a[10][20]中一共有200个指针,记住指针就是地址

而对于int *p[10]只有10个指针,数组中的每一个元素可以指向20个元素的数组。但是不一定指向20个元素,也可以指向2个元素,等等。但是系统还是分配了200个int类型长度的存储空间及10个指针的存储空间。这比多维数组占用了更多的空间。


28.p98

main函数的形参,命令行参数

C语言中,可以在程序开始执行时将命令行参数传递给程序。调用main函数时,它有两个参数,第一个参数是argc,表示运行程序时命令行参数的数目。第二个参数是argv,是指向字符串的指针数组。

命令行参数用空格隔开

且C语言规定,argv[0]是启动该程序的程序名,因此argc的值至少为1.第一个可选参数是argv[1],最后一个可选参数是argv[argc-1],且argv[argc]为空指针。

int a[3]={1,3,5};

a表示指向a[0]的指针即&a[0]

int b[3][4];

int (*a)[4]=b; 指向b的第一个元素即子数组的指针

char *s="aemg"; s是指向a的指针,s[0]为a

而char *s[]={"dbd","eefef"};  指针数组,s表示指向数组的第一个元素的指针,s[0]表示第一个元素,dbd。。。


29. p106

指向函数的指针

int (*pd)(double x,char y)


30.p112

结构体,回顾struct与class的区别在于初始的默认访问级别,

结构的初始化可以在定义的后面使用初值表进行,初值表中同每个成员对应的初值必须是常量表达式,例如

struct point{    int x;    int y;};struct point maxpt={300,200};


结构的合法用法,p114,返回类型不能像class那样直接写为sales_item,而是要写为struct point midpoint,定义一个变量也是的比如struct point maxpt。一个名为addpoint,返回类型是point结构类型的函数可以写为struct point addpoint{}

31.结构数组 p116

比如结构的类型为struct key,数组名为keynotes,那么结构数组可以写为

struct key keynotes[5];

当然这个也可以和结构的定义整合到一起去,比如

struct key{char *word;int count;} keynotes[5];


正如结构的初始化可以通过初值表,结构数组也可以通过初值表进行初始化。

struct key{char *word;int count;} keynotes[]={<pre name="code" class="cpp">"auto",0,
"break",0,
"case",0,
};

p122使用二叉树结构来统计单词出现的次数,如果说不用这种结构,那么每次输入一个单词就要判断和之前的所有单词是否相同而遍历一遍,此外我们也无法用二分法排序,因为不知道输入的单词是什么。所以二叉树结构满足了我们的要求,我们保证任何节点的左子树只包含按字典序小于该节点单词的那些单词,右子树只包含按字典序大于该节点的那些单词。

并且每个节点包含指向该单词的指针,指向左子树的指针,指向右子树的指针和一个统计出现次数的计数值。

struct tnode{char *word;int count;struct tnode *left;struct tnode *right;};

回顾返回类型为结构类型的函数,结构数组,和指向结构类型对象的指针

(回顾C++中一个派生类的实例instance也是基类的实例,这是动态绑定和继承的基础)

如果单词不是按照随机的顺序到达的,树将变得不平衡,这种情况下,程序的运行时间将大大增加。最坏的情况下,若单词已经排好序,则程序模拟的开销将非常大。O(n^2)。

这部分在算法导论中已经提到。

下面我们就实现上面提到的使用二叉树来统计输入的单词次数。

#include <stdio.h>#include <string.h>#include <ctype.h>#define MAXWORD 100struct tnode *addtree(struct tnode *,char *);void treeprint(struct tnode*);int getword(char *,int);//回顾之前的getline函数main(){struct tnode *root; //指向结构类型的指针,struct tnode包含四个成员char word[MAXWORD];root=NULL;while(getword(word,MAXWORD)!=EOF)if(isalpha(word[0]))root=addtree(root,word);reeprint(root);return 0;

struct tnode *talloc(void); //这是创建一个新节点,并返回这个节点的指针char *strdup(char *)//addtree函数用来在p的位置或者在p的下方增加一个w节点struct tnode *addtree(struct tnode *p,char *w){if(p==NULL)p=talloc();p->word=starup(w);p->count=1;p->left=p->right=NULL;else if((cond=strcmp(w,p->word))==0)p->count++;else if(cond<0)p->left=addtree(p->left,w);else p->right=addtree(p->right,w);return p;}


struct tnode *talloc(void); //这是创建一个新节点,并返回这个节点的指针char *strdup(char *)//addtree函数用来在p的位置或者在p的下方增加一个w节点struct tnode *addtree(struct tnode *p,char *w){if(p==NULL)p=talloc();p->word=starup(w);p->count=1;p->left=p->right=NULL;else if((cond=strcmp(w,p->word))==0)p->count++;else if(cond<0)p->left=addtree(p->left,w);else p->right=addtree(p->right,w);return p;}

32.typedef的使用

简化函数指针的书写 

typedef int (*PFI)(char *,char *);PFI strcmp, numcmp;

回归函数指针与genericFunction,见链接的第17点

double genericFunction(double x,double y,double (*pfi)(double m,double n)){return (*pfi)(x,y);}

 33.stack-based memory, heap-based memory, static memory, automatic memory

其中stack-based memory要注意在scope外就会被撤销,见链接的第16条。不能返回局部对象或者局部变量的指针或引用,这会指向不确定的内存。

34.表查找与Hash  p125


35. <math.h>定义了20多个数学函数。

malloc和calloc用于动态分配存储块。malloc的声明如下:

void *malloc(size_t n)

当分配成功时,它返回一个指针,指向n个字节长度的未初始化的存储空间。

而C++则是通过new delete,比如

int *ptr=new int;

C语言的强制类型转化和C++不同

int *p=(int *) malloc(5);

free(p);


36.随机数发生器函数rand(),生成0到RAND_MAX之间的伪随机整数序列。RAND_MAX是<stdlib.h>中定义的符号常量。

下面是生成大于等于0小于1的随机浮点数的方法

#define frand() ((double) rand()/(RAND_MAX+1.0))

37.回顾类的静态成员是类的一部分而不是类的对象的组成部分,类的静态数据成员定义在类体外,且格式是<类型><类名>::<静态成员名>=常量表达式;

而调用则是<类名>::<静态成员名>

而类的静态成员函数没有隐含的this指针,也不能访问类的非静态数据成员。回顾static-memory包含的四类变量。




0 0
原创粉丝点击