C笔记《C Primer Plus 6E》

C Primer Plus 6E

C编译器:把C代码转换成计算机内部指令的程序。

面向对象编程:通过对语言建模来适应问题,而不是对问题建模来适应语言。

编程7步:定义程序的目标-设计程序-编写代码-编译-运行程序-测试和调试程序-维护和修改程序。

选择一个合适的方式表示信息可以更容易地设计程序和处理数据。

int main(void)
int表明函数的返回类型,此为整数,返回给操作系统
void表明传入函数的信息,此为无

两种注释:
/这是一种注释,
可单独放一行或多行。
/
//这也是一种注释,只能写成一行。(语句后或单独一行)

花括号 { } 用于标记函数体的开始和结束,还可用于把函数中的多条语句合并为一个单元或块。

所有变量都必须先声明才能使用。

给变量命名时要尽量使用有意义的变量名或标识符,否则要在注释中解释含义。

变量名称只能包含小写字母、大写字母、数字和下划线(不包括空格),且第1个字符不能是数字。
实际参数:传递给函数的特定值。
形式参数:函数中用于储存值的变量。

转义序列:代表难以表示或无法输入的字符。\n:换行符,\t:Tab键,\b:退格键。

%d:%提醒程序要在该处打印一个变量,d表明把变量作为十进制整数打印。

有返回值的C函数要有return语句。

编程技巧:在函数中用空行分隔概念上的多个部分。每条语句各占一行。

编译器报错的位置可能比真正的错误位置滞后一行。

调试程序(debug)的方法:
1、 自己模拟计算机逐步执行程序。
2、 在程序中的关键点插入额外的printf()语句,以监视特定变量值的变化,了解程序执行情况。
3、 使用调试器(debugger)。

C编程惯例:把main()放在开头,调用的函数其定义放在后面,且开头需写函数原型:
void butler(void);
int main(void)
{
butler();
}
void butler(void)
{

}

int a, b=1; /只有b被初始化为1/

int:不小于16位,short:至少16位,long:至少32位,long long:至少64位。

0x或0X前缀表示十六进制,0前缀表示八进制。

以十进制显示数字,使用%d;以八进制显示数字,使用%o;以十六进制显示数字,使用%x。要显示其前缀0、0x,必须分别使用%#o、%#x。

unsigned:无符号类型(非负值),常用于计数。

一个较小值末尾加上L后缀:将其作为long类型对待。

printf () 使用%u说明显示unsigned int类型的值,使用%ld说明显示long类型的值,%hd %ho分别表示以十进制/八进制显示short类型的整数。

字符常量初始化:char grade=’A’; 或char grade=65;

非打印字符,使用转义序列赋给字符变量:char nerf=’\n’;

char grade=’4’; 表示字符4,而不是数值4。

printf() 函数用%c指明待打印的字符。若用%d打印char类型变量的值,打印的是一个整数(对应的ASCII码值)。

浮点数类型:
float:至少能表示6位有效数字,取值范围至少是e-37~e+37。通常储存一个浮点数要占用32位,8位用于表示指数的值和符号,24位用于表示有效数及其符号。
double:双精度,至少能表示10位有效数字,最小取值范围同上,一般占用64位。
long double:至少与double类型精度相同。

通常浮点型常量被储存为double类型,要将其储存为float类型,则在浮点数后面加上f或F后缀,加L后缀成为long double类型。

p计数法:用十六进制表示浮点型常量,数前加0x或0X前缀,用p代替e,2的幂代替10的幂。0xa.1fp10。

printf() 使用%f打印十进制计数法的浮点数,用%e打印指数计数法的浮点数,用%a打印p计数法的浮点数。

3种复数类型:float_Complex、 double_Complex、 long double_Complex
3种虚数类型:float_Imaginary、 double_Imaginary、 long double_Imaginary

sizeof() :以字节为单位给出指定类型的大小,打印的转换说明为%zd。

在变量名中加i_前缀表示int类型,us_前缀表示unsigned short类型。

第4章

字符串:被存储在char类型的数组里,数组末尾位置为字符\0,即空字符,用它来标记字符串的结束,若数组有40个存储单元,则只能存储39个字符。

创建数组:char name[40]; //char表明数组中每个元素的类型,40表示元素数量。

如果用scanf()读取基本变量类型的值,在变量名前加上一个&;而如果是把字符串读入字符数组中,不要使用&。

scanf(“%s”,name); //若输入(a b),则scanf()只读取a,%s从第1个非空白字符开始读取,在遇到下一个空白(空格、制表符或换行符)时就不再读取输入。

scanf(“%c”, &ch); //从输入中的第1个字符开始读取。
scanf(“ %c”, &ch); //从输入中的第1个非空白字符开始读取。

scanf(“%d,%d”, &n, &m); //用户将输入一个数字、一个逗号,再输入一个数字。

while ((ch = getchar()) != EOF) //检测是否到达文件结尾

sizeof():以字节为单位给出对象的大小,对于数组则是数组的大小,对于字符串则包括字符串末尾不可见的空字符;strlen():给出字符串中的字符长度(包括空格和标点符号),对于数组不包括未使用的存储单元,也不包括空字符。

定义符号常量:
#define NAME value
末尾不加分号,NAME用大写表示符号常量。

printf()打印百分号使用的转换说明:%%

printf()函数有返回值,返回打印字符的个数,包括空格和换行符。

scanf()函数返回成功读取的项数。

scanf(“%*d %*d %d”, &n);//跳过2个整数,把第3个整数拷贝给n。

printf(“%010d”, m);//用前导0代替空格填充字段宽度。

求模运算符:%,a%b,为a除以b的余数。

a=1,b=a++,,则b=1,a=2,++为后缀形式的话,先进行赋值、运算、判断等,再递增。

如果一个变量多次出现在一个表达式中,或者出现在一个函数的多个参数中,不要使用递增或递减运算符。

强制类型转换运算符:(type),例如:(int)1.6,值为1。

第6章

while (n++<3); //;被视为一条语句,空语句,重复执行。

C中,所有的非零值都视为真,只有0被视为假。

while(a!=0)替换为while(a)

在构建比较是否相等的表达式时,将常量放在左侧:while(5==a)

运算符优先级,表6.2

x*=2与x=x*2相同

逗号运算符:for(a=1,b=2; a<10; a++,b=b+a)

do
statement
while(expression);

当循环涉及初始化和更新变量时,用for循环更好,其他情况用while更好。

把程序要素分为接口部分和实现部分,接口部分描述如何使用一个特性,即函数原型所做的;实现部分描述具体的行为,即函数定义所做的。

第7章

赋值表达式的值是赋值运算符左侧运算对象的值,即,(a=1)的值为1。

字符输入输出函数:ch=getchar(); 等同于scanf(‘’%c’’, &ch); putchar(ch); 相当于printf(‘’%c’’, ch);

ctype.h系列字符函数,表7.1、7.2。

else 与离它最近的if匹配,除非最近的if被花括号括起来,若最近的被括起来,则继续往上找。

条件运算符: ?:
C语言中唯一的三元运算符(带3个运算对象)
max=(a>b) ? a : b; //若a>b,则max=a,否则max=b。
(n%2) ? n=3*n+1 : n/=2;

循环辅助:
continue; //跳过本次迭代的剩余部分,并开始下一轮迭代。
break; //终止包含它的循环,并继续执行下一阶段。

switch语句:
switch(number)
{
case 常量1:statement 1;
break;
case 常量2:statement2;
break;
default: statement3;
}
statement4;
switch在圆括号中的测试表达式的值应该是一个整数值(包括char类型),case标签必须是整数类型(包括char类型)的常量或整型常量表达式,不能用变量。

第8章

缓冲区 buffer,分为完全缓冲I/O(当缓冲区被填满时才刷新缓冲区(内容被发送至目的地))和行缓冲I/O(在出现换行符时刷新缓冲区)。
EOF:标志着检测到文件结尾,定义在stdio.h文件中,值为-1。

重定向输入和输出:
./progfile1
./prog>file1<file2
把file2作为输入,file1作为输出。

第9章

函数中声明的变量是局部变量,可以在程序的其他地方(包括main())中使用同名但是是不同的变量。
一个函数中可以使用多个return语句,return语句可以终止函数并把控制返回给主调函数的下一条语句。
scanf(“%*s”); //处理非整数输入,%s:跳过字符串
指针
地址运算符:& 后跟一个变量名时,&给出该变量的地址。
间接运算符:
后跟指针名或地址时,*给出储存在指针指向地址上的值。
声明指针:int * ptr;
*和指针名之间的空格:在声明时使用,在解引用变量时省略。

第10章

获得未知大小数组的大小:sizeof days / sizeof days[0]
初始化指定的数组元素:
int arr[6] = { [5] = 212 }; //把arr[5]初始化为212,未被初始化的元素都被置0。

C不允许把数组作为一个单元赋给另一个数组,除初始化以外也不允许使用花括号列表的形式赋值。

多维数组:float rain [5] [12];
数组名是数组首元素的地址
在C中,指针加1指的是增加一个存储单元。对数组而言,意味着加1后的地址是下一个元素的地址,而不是下一个字节的地址。
dates + 2 == &dates[2]
*(dates + 2) == dates[2]

函数处理数组怎样知道数组何时开始、何时结束:
1、 使用一个指针形参标识数组的开始,用一个整数形参表明待处理数组元素个数。
2、 传递两个指针,一个指明开始,一个指明结束。

只有当ar是指针变量时,才能使用ar++这样的表达式,数组名不可以(因为ar++意味着改变了数组的存储位置,即地址,可以使用ar+1)。

C保证指向数组后面第一个位置的指针仍是有效的指针。
指针求差:要求两个指针分别指向同一数组的不同元素,求差得出两元素之间的距离。
注意事项:不能解引用未初始化的指针:
int * pt;
*pt = 5; //严重的错误。在使用指针之前,必须先用已分配的地址初始化它。做法:可以用一个现有变量的地址初始化该指针,或者使用malloc()函数先分配内存。
如果函数的意图不是修改数组中的数据内容,那么在函数原型和函数定义中声明形式参数时应使用关键字const。
指向const的指针不能修改它所指向的地址上的值:
double rates[5]={1,2,3,4,5};
const double * pd=rates;
*pd = 6; //不允许
指向const的指针可以被const数据或非const数据的地址赋值,但普通指针只能被非const数据的地址赋值。
const指针不能更改它所指向的地址:
double rates[5]={1,2,3,4,5};
double * const pc=rates;
pc = &rates[2]; //不允许,该指针不能指向别处
*pc = 6; //可以,更改rates[0]的值
使用两次const的指针既不能更改它所指向的地址,也不能修改它所指向的地址上的值:
double rates[5]={1,2,3,4,5};
const double * const pc=rates;
pc = &rates[2]; //不允许
pc = 6; //不允许
指向数组的指针:
int (
pz) [2]; //pz是一个指向内含两个int类型值的数组的指针,等价于: int pz [ ] [2];
int * pax[2]; //pax是一个内含两个指针元素的数组,每个元素都是指向int的指针
变长数组VLA:
int rows=2;
int cols=3;
double ar [rows] [cols];
字符串数组,由于其末尾的空字符,不用传递数组的大小,函数通过检测字符串的末尾也知道在何处停止。

第11章

要在字符串内部使用双引号,必须在双引号前面加一个反斜杠(\):
printf(“\”Run!\”exclaimed Dick.\n”);

字符串常量用双引号括起来的内容被视为指向该字符串储存位置的指针。

const char m1[4] = { ‘L’, ‘o’, ‘v’, ‘e’} //这是一个字符数组
const char m2[5] = { ‘L’, ‘o’, ‘v’, ‘e’, ‘\0’} //这是一个字符串数组

使用指针表示法创建字符串:(建议使用const限定符,如果打算修改字符串就不要用此法)
const char * pt1 = “Something is pointing at me.”;

指向字符串的指针数组:
const char * pt2[6] = {“Something”, “is”, “pointing”, “at”, “me”, “.” };
pt2是一个内含6个指针的数组。这样表示字符串数组效率更高,但是缺点是不能更改。要更改的话就要使用char类型的二维数组。

声明数组将分配储存数据的空间,而声明指针只分配储存一个地址的空间,所以指针声明后要先初始化。

数组和指针的区别:
数组名是常量,指针名是变量;
都可以使用数组表示法(a[i]);
都能进行指针加法操作;
只有指针可以进行递增操作;
可以将数组名赋值给指针,但不能把指针赋值给数组。

gets()函数用于读取字符串,直至遇到换行符。(C11标准中已经被废除)puts()函数只显示字符串,而且自动在显示的字符串末尾加上换行符。

字符串函数(在头文件string.h中):
strlen()函数:统计字符串的长度
strcat()函数:接受两个字符串作为参数,把第二个字符串的备份拼接到第一个字符串末尾,拼接后新字符串作为第一个字符串,第二个字符串不变。
strncat()函数:在strcat()函数基础上增加第三个参数,指定最大添加字符数。
strcmp()函数:比较输入的两个字符串,若参数相同,则返回0,否则返回非零值。可用来确定两个字符串的顺序。
strncmp()函数:在strcmp()函数基础上增加第三个参数,只比较第三个参数指定的字符数。
strcpy()函数:把第二个字符串拷贝给第一个字符串。函数返回的是第一个字符的地址。第一个参数不必指向数组的开始,可用于拷贝数组的一部分。
strncpy()函数:在strcpy()函数基础上增加第三个参数,指定可拷贝的最大字符数。
sprint()函数:声明在stdio.h中,把数据写入字符串,把多个元素组合成一个字符串,第一个参数是目标字符串地址,然后是格式字符串和待写入项的列表(同printf())。

char *strchr(const char * s, int c); //如果s字符串中包含c字符,则返回指向s字符串首次出现的c字符的指针,如未找到,返回空指针。

char *strrchr(const char * s, int c); //如果s字符串中包含c字符,则返回指向s字符串最后一次出现的c字符的指针,如未找到,返回空指针。

atoi()、atol()和atof()函数把字符串形式的数字分别转换成int、long和double类型的数字。strtol()、strtoul()和strtod()函数把字符串形式的数字分别转换成long、unsigned long和double类型的数字。

第12章

作用域:描述程序中可访问标识符的区域。一个C变量的作用域可以是块作用域,函数作用域,函数原型作用域或文件作用域。
块:用一对花括号括起来的代码区域。
C99标准以前:具有块作用域的变量都必须声明在块的开头。
函数作用域:仅用于goto语句的标签。
函数原型作用域:用于函数原型中的形参名(变量名),范围是从形参定义处到原型声明结束。
文件作用域:变量的定义在函数的外面,范围为从它的定义处到该定义所在文件的末尾。文件作用域变量也称为全局变量。

一个翻译单元:一个源代码文件和它所包含的头文件。

链接:C变量有3种链接属性:外部链接、内部链接和无链接。具有块作用域,函数作用域,函数原型作用域的变量都是无链接变量,即这些变量属于定义它们的块、函数或原型私有。具有文件作用域的变量可以是外部链接或内部链接,外部链接变量可以在多文件程序中使用,内部链接变量只能在一个翻译单元中使用。

区分文件作用域变量是外部链接还是内部链接:查看外部定义中是否使用了存储类别说明符static:
int a=5; //外部链接
static int b=6; //内部链接

作用域和链接:描述标识符的可见性。

存储期:描述通过标识符访问的对象的生存期。C对象有4种存储期:静态存储期、线程存储期、自动存储期、动态分配存储期。

静态存储期:对象储存在静态内存中,从程序被载入到程序结束期间都存在。文件作用域变量具有静态存储期。
线程存储期:用于并发程序设计,对象从被声明时到线程结束一直存在。
自动存储期:块作用域变量通常具有自动存储期。当程序进入定义这些变量的块时,为这些变量分配内存;当退出这个块时,释放刚才为变量分配的内存。

块作用域变量也能具有静态存储期:把变量声明在块中。且在声明前面加上关键字static。

5种存储类别:自动、寄存器(使用关键字register)、静态块作用域、静态外部链接、静态内部链接。

关键字auto:C中是存储类别说明符,表明变量是自动存储期,在C++中用法完全不同。

C99新特性:作为循环或if语句的一部分,即使不使用花括号,也是一个块。

程序清单12.3:块作用域的静态变量(局部静态变量),只在编译时被初始化一次。

关键字extern:声明外部变量。不要用它来创建外部定义,只用它来引用现有的外部定义。如果外部变量定义在一个文件中,那么其他文件在使用该变量之前必须先声明它(用extern)。

外部变量:如未初始化则自动初始化为0,只能使用常量表达式初始化文件作用域变量。

关键字register:把变量归为寄存器存储类别,使用更快的内存或寄存器存储它们,且不能获取寄存器变量的地址。

函数的存储类别:外部函数(默认)或静态函数(使用关键字static),C99新增内联函数。

ANSI C 中的time()函数:返回系统时间。

malloc()函数:接受一个参数:所需内存字节数。分配内存,并返回动态分配内存块的首字节地址。可以把该地址赋给一个指针变量,并使用指针访问这块内存。
double * ptd;
ptd = (double *) malloc (n * sizeof(double));
ptd也可表示一个数组。

free()函数:参数是之前malloc()函数返回的地址,释放之前malloc()分配的内存。

calloc()函数:接受两个无符号整数作为参数,第1个是所需的存储单元数量,第2个是存储单元的大小(以字节为单位)。
long * newmem;
newmem = (long *)calloc(100, sizeof(long));
也可用free()函数释放内存。

const类型限定符常见用法:1、在指针和形参声明中使用;2、对全局数据使用

volatile类型限定符:代理(不是变量所在的程序)可以改变该变量的值。通常用于硬件地址和在其他程序或同时运行的线程中共享数据。

可以同时用const和volatile限定一个值,例如,通常用const把硬件时钟设置为程序不能更改的变量,但可以通过代理改变,此时用volatile。它们的顺序不重要。

restrict类型限定符:只能用于指针,表明该指针是访问数据对象的唯一且初始的方式。

第14章

建立结构声明:
struct book {
char title[41];
char author[31];
float value;
};
花括号内是结构成员列表,成员可以是任何一种C的数据类型,甚至可以是其他结构,结尾的分号表示结构布局定义结束。该声明可以放在所有函数外部,也可放于一个函数定义的内部,若为后者,则它的标记就只限于该函数内部使用。如果结构声明置于函数外部,则该声明之后的所有函数都能使用它的标记。

定义结构变量
struct book dickens;
把dickens声明为一个使用book结构布局的结构变量。结构的标记名(这里为book)是可选的,但当在一处定义结构布局,在另一处定义实际的结构变量时,必须使用标记。和数组名不同,结构变量名不是其地址的别名!
上述声明是以下声明的简化:
struct book {
char title[41];
char author[31];
float value;
} dickens;
可将声明结构和定义结构变量组合成一个步骤,不需使用结构标记:
struct {
char title[41];
char author[31];
float value;
} dickens;
如果要多次使用结构模板,就要用带标记的形式,或者使用typedef。

初始化结构:
struct book dickens = {
“The Math Book”,
“Bob”,
1.95
};

访问结构成员:
使用结构成员运算符——点(.)访问结构中的成员。dickens.value 相当于一个float类型变量。

结构数组:
声明结构数组:
struct book dickens[100];
dickens[0].title[4] //数组中dickens[0]元素的title成员的第五个字符

嵌套结构
struct names {
char first[20];
char last[20];
};
struct guy {
struct names handle;
char job[20];
float income;
};

指向结构的指针
声明和初始化:
struct guy * him;
him = &barney;
him = &fellow[0];
用指针访问成员:
barney.income == (*him) .income == him->income

向函数传递结构的信息
3种传递方式:传递结构本身;传递指向结构的指针;传递结构的成员。

允许把一个结构赋值给另一个结构,但是数组不能。还可把一个结构初始化为相同类型的另一个结构。还能把结构作为返回值返回。

结构中用字符指针代替字符数组处理字符串:要用malloc()为字符串分配合适的存储空间并使用指针储存该地址。

结构中使用伸缩型数组:伸缩型数组成员必须是结构的最后一个成员。

匿名结构:
struct person
{
int id;
struct { char first[20]; char last[20];}; //匿名结构
};
访问成员时:ted.first

联合:一种数据类型,能在同一内存空间中储存不同的数据类型。
union hold {
int digit;
double bigfl;
char letter;
};
以上声明的联合只能储存一个int类型的值或一个double类型的值或char类型的值。
fit.digit 点运算符表示正在使用哪种数据类型。

枚举类型:
enum spectrum {red, orange, yellow, green, blue, violet};
enum spectrum color;
color=blue;
第一个声明创建了spectrum作为标记名,花括号内标识符枚举了spectrum变量可能有的值。第二个声明使color作为该类型的变量。

typedef关键字
作用:可以为某一类型自定义名称
typedef unsigned char BYTE;
即BYTE与unsigned char 等价。
使用typedef的第一个原因:为经常出现的类型创建一个方便、易识别的类型名。
typedef struct { double x; double y;} rect;
使用typedef的第二个原因:常用于给复杂的类型命名。

复杂声明:
指针符号 *
函数符号 ()
数组符号 []
[]和()具有相同的优先级,按从左往右顺序,高于*。

指向函数的指针:储存着函数代码的起始处的地址。声明函数指针时,必须声明指向的函数的类型,即函数的返回类型和形参类型。
void (*pf) (char *); //pf是指向函数的指针,该函数形参为char *类型,返回void类型。
要声明一个指向特定类型函数的指针,可以先声明一个该类型的函数,再把函数名替换为(*pf)形式的表达式。

第15章

按位逻辑运算符(不改变原变量值)
按位取反:~
按位与:&
按位或:|
按位异或:^

移位运算符
左移:<< 将其左侧运算对象每一位的值向左移动其右侧运算对象指定的位数,移出左末端的值丢失,空出的位置用0填充。
右移:>>
char ps[10];
int n;
ps[i]= (01 & n) + ‘0’; //即可把整型的0、1转换为字符’0’、’1’。

位字段:是一个signed int或unsigned int 或bool类型变量中的一组相邻的位。位字段通过结构声明来建立,结构声明为每个字段提供标签,并确定该字段的宽度。
struct {
unsigned int autfd : 1;
unsigned int itals : 2;
} prnt;

第16章

预处理器不做计算,不对表达式求值,只进行替换。

#define HAL ‘Z’ //定义了一个字符常量
#define HAL ‘‘Z’’ //定义了一个字符串

双引号中的宏,预处理器不会进行替换。

不要在宏中使用递增或递减运算符。

#运算符:可用宏参数创建字符串

##运算符:预处理器记号粘合剂

用圆括号把宏的参数和整个替换体括起来,确保在引用的表达式中正确地展开。

条件编译:
#ifdef #else #endif 指令,告诉编译器根据编译时的条件执行或忽略代码块。
#ifndef #if #elif

泛型选择:
#define MYTYPE(X) _Generic ((X),
int : “int”,
float : “float”,
double : “double”,
default : “other”
) // MYTYPE(5) 求值得“int”

内联函数:inline static void eatline ( )

通用工具库:在stdlib.h 头文件中,包括随机数生成器,查找和排序函数,转换函数和内存管理函数。

atexit( )函数,以函数名为参数,注册该函数,当调用exit( )时就会执行这些函数,后添加的先执行。main( )结束时会隐式调用exit( )

ANSI C允许把指向任何数据类型的指针强制转换成指向void的指针。

qsort( )函数,快速排序

assert( ):接受一个整型表达式作为参数,如果表达式为假,则终止程序且显示失败的具体信息。可用于辅助调试程序。

第17章

C的内置类型:简单变量,数组,指针,结构,联合。

设计一种数据类型:包括设计如何储存该数据类型,和设计一系列管理该数据的函数。

链表中每一个链节叫做节点(node),每个节点包含形成链表内容的信息和指向下一个节点的指针。

链表只支持顺序访问,不能使用二分查找。但是方便插入和删除项。数组相反。既支持频繁插入和删除项,又支持频繁查找,则应选择二叉查找树。

二叉树的每个节点都包含一个项和两个指向其他节点(子节点)的指针。

实现二叉查找树最直接的方法是通过指针动态分配链式节点。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值