跳转至

《Pointers On C》(Kenneth Reek) 【US】

第一章 快速上手

1.1 简介

1.1.1 空白和注释

注释并不能真正将代码从源文件中删除(/**/)

逻辑上删除C代码——#if指令

1
2
3
#if 0   
statements   
#endif   

在#if和#endif之间的程序段可以去除

1.1.2 预处理指令

#include
#include
#include
#define ABC 20``
预处理器解释的:预处理器读入源代码,根据预处理指令对其进行修改,然后将修改过的代码递交给编译器。
eg:->#include

1.1.3 main函数

每个C程序都必须有一个main函数→程序执行的起点 main函数内容:花括号之间({})

关键字:int 表示函数返回一个整型值

   void表示函数不返回任何值

传递方式 数组参数: 引用2 标量和常量: 值

C函数的参数传递规则如下:所有传递给函数的参数都是按值传递的

字符串:一串以1NUL字节结尾的字符。 字符串常量:“  ”中间的

1.1.4 read_column_numbers函数

1.1.5 rearrange函数

1.2 补充说明

1.3 编译

1.4 总结

格式化输入与输出 非格式化输入与输出
printf putchar
scanf getchar

1.5 警告的总结

1.在scanf函数的3前未加 & .

2.==与=

1.6 编程提示的总结

1.7 问题

1.8 编程练习

第二章 基本概念

2.1 环境

环境 功能
翻译 源代码被转换为可执行的机器指令
执行 实际执行码

2.1.1 翻译

多个源文件经过 编译 ——>目标代码 各个目标文件由 链接器(同时引入库)捆绑在一起——>单一完整可执行程序

1.文件名约定

C源代码——>.C 由#include指令包含到C源代码的文件被称为头文件——>.h预处理命令

2.编译和链接

cc:C编译器

2.1.1 执行

1.程序载入内存中。在宿主环境中(具有操作系统)完成,尚未初始化&&不是存储在堆栈中的变量得到初始值
2.一个小型的启动程序与程序链接在一起,调用main函数
3.执行程序代码


(1)使用运行时 堆栈 (stack)——>存储函数的局部变量和返回地址 (2)使用 静态 (static)内存——>存储与静态内存中的变量在程序的整个执行阶段一直保留它们的值(即不变)

4.程序的终止

1.normol:main/exit; 2.break; 3.error. 4.environment

2.2 词法规则

词法规则类似于语法——>你如何在源程序中形成单独的字符段,即 标记 (token) 一个ANSI C程序分为声明和函数。 函数:定义了需要执行的工作。 声明:描述了函数和(或)函数将要操作的数据类型(sometimes=数据本身)

2.2.1 字符

三字母词 eg: ??(——>[    ??<——>{ 转义字符 \ \

2.2.2 注释

/**/

2.2.3 自由形式的源代码

唯一非法:相邻的标记之间必须出现一至多个空白字符(或注释) 肥皂盒哲学

2.2.4 标识符

由ANSI标准定义的C语言

关键字[https://blog.csdn.net/CSDN2133/article/details/122010384?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522167698178516800180625650%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=167698178516800180625650&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2]共32个:

auto double int struct break else long switch case enum register typedef char extern return union const float short unsigned continue for signed void default goto sizeof volatile do if while static

1.数据类型关键字

A基本数据类型(5个)

void:声明函数无返回值或无参数,声明无类型指针,显式丢弃运算结果 char:字符型类型数据,属于整型数据的一种 int:整型数据,通常为编译器指定的机器字长 float:单精度浮点型数据,属于浮点数据的一种 double:双精度浮点型数据,属于浮点数据的一种

B类型修饰关键字(4个)

short:修饰int,短整型数据,可省略被修饰的int long:修饰int,长整形数据,可省略被修饰的int signed:修饰整型数据,有符号数据类型 unsigned:修饰整型数据,无符号数据类型

C复杂类型关键字(5个)

struct:结构体声明 union:共用体声明 enum:枚举声明 typedef:声明类型别名 sizeof:得到特定类型或特定类型变量的大小

D存储级别关键字(6个)

auto:指定为自动变量,由编译器自动分配及释放。通常在栈上分配 static:指定为静态变量,分配在静态变量区,修饰函数时,指定函数作用域为文件内部 extern:指定对应变量为外部变量,即在另外的目标文件中定义,可以认为是约定由另外文件声明 const:与volatile合称“cv特性”,指定变量不可被当前线程/进程改变(但有可能被系统或其他线程/进程改变)

2.流程控制关键字 A跳转结构(4个)

return:用在函数体中,返回特定值(或者是void值,即不返回值) continue:结束当前循环,开始下一轮循环 break:跳出当前循环或switch结构 goto:无条件跳转语句

B分支结构(5个)

if:条件语句 else:条件语句否定分支(与if连用) switch:开关语句(多重分支语句) case:开关语句中的分支标记 default:开关语句中的“其他”分治,可选

C循环结构(3个)

for:for循环结构,for(1;2;3)4;的执行顺序为1->2->4->3->2...循环,其中2为循环条件 do:do循环结构,do 1 while(2);的执行顺序是1->2->1...循环,2为循环条件 while:while循环结构,while(1) 2;的执行顺序是1->2->1...循环,1为循环条件 以上循环语句,当循环条件表达式为真则继续循环,为假则跳出循环。

标准C语言的32个关键字

1、基本数据类型: signed unsigned char int float double short long void 2、构造数据类型: struct union enum 3、数据存储类别: auto static extern register 4、数据优化: const volatile 5、9条基本语句: if else switch case break default while do for return continue goto 6、其它: typedef sizeof

预编译处理符 “#”

include

define

ifdef

ifndef

if

else

else if

endif

等等。

一、基本数据类型

1、unsigned :无符号的 //用来声明一个无符号的变量。 unsigned char var; //var的范围:0~255

2、signed :有符号的(可以省略不写) //用来声明一个有符号的变量。 signed char var; //var的范围:-128~127

3、char :字符型 //用来声明一个字符型变量。 //占一个字节空间 char var;

4、int :整型 //用来声明一个整型变量。 //C51:占两个字节空间,ARM:占四个字节 int var;

5、float :浮点型 //用来声明一个浮点型(实型)变量。 //最多能表示到7个有效数据位。 //占四个字节空间 float var;

6、double :双精度型 //用来声明一个双精度实型变量。 //最多能表示到15~16个有效数据位。 //占四个字节空间 ,有的系统占八个字节 double var;

7、short :短整型 //用来声明一个短整型变量。 //C51:跟int一样,ARM:占两个字节 short var;

8、long :长整型 //用来声明一个长整型变量。 //ARM:跟int一样,C51:占四个字节 long var;

9、void :空型 //表示一个函数没有返回值,或者无形参。 void function(void);

二、构造数据类型

1、struct

1
//用来声明一种结构体类型。

struct stu{ char sex; int age; float score; struct stu *Next; }; struct stu var;

2、union //用来声明一种共用体类型。 //该类型的变量所在空间的大小以其成员占最大的那个为准, //存入该变量中的值以程序中最后存入的数值为当前值 union non{ char sex; int age; float score; }; union non var;

3、enum //用来声明一种枚举类型。 //规定枚举类型的变量,只能在限定的范围内取值 //否则,编译会出现警告(达到数据安全的效果) enum em {a = 23,b,c,d = 56,e}; //其中b=24,c=25,e=57 enum em var;

三、数据的存储类别

1、auto :自动的(可省略不写) //定义一个局部变量,默认为auto类型的, //当它所在的函数调用结束时,释放内存 //使用时才分配内存,用完即释放 auto char var;

2、static :静态的 //①定义一个局部变量,该变量在定义时只进行 //一次初始化,以后每次调用它所在的函数,其值 //都会保持上一次调用的结果,它所在的空间不会 //被释放 //②被static修饰的全局变量,则只能在它所在的C //源文件中使用,其它文件不能调用,(内部全局变量) //③被static修饰的函数,只能在该函数所在的C源文 //件中被调用,其它文件不能调用,(内部函数) static char var; static void function();

3、extern :外部的 //①想要调用别的C源文件中的某一个全局变量, //可以使用该关键字在该文件中修饰声明该变量 //即可调用(前提是该变量没有被static修饰) //该类型的变量也是一直占着内存不释放 //②想要调用别的C源文件中的某一个函数, //可以使用该关键字在该文件中修饰声明该函数 //即可调用(前提是该函数没有被static修饰) extern char var; extern void function();

4、register :寄存器的 //被这个关键字修饰的变量,建议编译器将该变量在 //使用时放到CPU内部寄存器中,以提高执行效率 //注意:该关键字只是"建议",到底有没有将变量放到 //寄存器中无从得知。一般使用在循环次数比较多的 //地方。 //在使用时才分配内存,用完即释放 register long i = 30000;

四、数据优化

1、const :常的

1
//常变量:被const关键字声明的变量,其值不能被改变。

//即在声明该变量时必须要初始化该变量。 //var本身还是一个变量。(数据安全检测) const char var = 100; char arr[var]; //试图声明一个长度为100的字符型数组 //在MDK (ARM)中可以使用常变量来声明数组的长度 //在VC++编译环境中也可以。 //在C51-keil中不可以使用常变量来声明数组的长度 char const p; //指针变量p不可改变,但是它指向的地址里面的值可变 char constp; 或 const char p; //指针变量p可以改变,但是它所指向的地址里面的值不能改变 const char const p; //p地址不可改变,里面的值也不能变

2、volatile :随时会改变的

1
//被volatile修饰的变量或寄存器会意想不到地发生改变。

//①某些变量或寄存器(如状态寄存器)可能会受硬件影响; //②在多线程任务中,被其它线程修改(共享内存段); //③一个在中断服务中使用到的全局变量 //④编译器会对C程序进行优化; //为了保证程序不被优化,保证CPU取到的数据是最新的 //(即提醒CPU每次都必须到内存中取出变量当前的值而不 //是取cache或者寄存器中的备份),使用该关键字修饰,如: int p;p = 1; p = 2; //编译时,编译器会对上面的语句进行优化, //会直接优化成: intp; p = 2; //为了保证上面的语句不被优化,加volatile修饰变量: int volatile p;

五、9条语句

四种结构:

1、顺序结构:0条 //声明语句、运算语句、赋值语句等等

2、选择结构:2条 //多选一

1
①、if -else if -else if ... else

if(表达式1) {语句s;} else if(表达式2) {语句s;} else if(表达式3) {语句s;} else {语句s;} //用法:顺序判断if后的"表达式"是否为真 //如果碰到为真的情况,则执行其下面的{}里的"语句" //执行完后,即退出这个"多选一"的结构

1
②、switch-case-break

switch(变量) { case 常量1:语句;...;break; case 常量2:语句;...;break; case 常量3:语句;...;break; default:语句; } //用法:顺序判断"变量"是否与"常量"相等, //如果相等,则执行该常量:后的"语句s",遇到break即跳出 //这个结构 unsigned char i = 6; unsigned char dat; switch(i) { case 3:dat = 5;break; case 5:dat = 34;break; case 6:dat = 99;break; case 7:dat = 56;break; case 6:dat = 100;break; default:dat = 68; //默认执行 } //注:如果少了break,则顺序判断i的值与 //case后面的常量是否相等,如果相等,则执行其 //后面的语句,以后不再判断,再继续执行下面的 //每一条case 后面的语句,直到default. //这种用法不使用!

3、循环结构:3条

1
①、for

for(语句1;语句2;语句3) { 语句4; 语句...; } //用法:语句1:条件初始化 // 语句2:判断语句,判断条件是否成立 // 语句3:修改条件语句 //先执行语句1,再进行对语句2的判断,如果成立 //则执行{}里的语句4...,再执行语句3,在判断 //语句2是否依然成立,。当语句2不成立时,结束循环

1
②、while

while(表达式) { 语句; ....; } //用法:先判断“表达式”是否成立,如果成立 //则执行{}里的语句,执行完再次判断“表达式” //是否依然成立,成立则继续执行{},不成立则结束 //此循环结构。

如何来设计一个死循环?两种方法: for(;) while(1)

1
③、do-while

do{ 语句1; ...; }while(表达式); //用法:先执行{}里的语句,执行完后,判断 //"表达式"是否成立,如果成立,继续执行{}; //如果不成立,则结束循环

4、转移结构:4条

1
①、break

//仅用于跳出循环结构 //且仅能跳出一层循环结构 for(i=10;i>0;i--) { t = 10; while(t--) { dat++; if(dat == 3) break;//跳出while()结构。 } }

1
②、continue

//用于终止本次循环,继续从下次循环开始 //正式程序中不使用,仅用于调试程序 char buf = 10; while(buf--) { a++; b++; continue;//遇到continue则结束这次循环 d++; //这条永远都不会执行到 e++; //这条永远都不会执行到 }

1
③、goto

//无条件转移 //一般都不建议在大的程序当中使用 unsigned char dat=10; while(dat--) { a++; b++; Lable: c++; if(c == 3) goto Lable;//直接跳到Lable标号去 d++; }

1
④、return

//用于函数返回, //在函数调用时,遇到return即返回。 //如果需要返回某个值,在其后加上返回值。 //返回值类型必须和函数类型一致。 void function() { char a,b; a++; return; //遇到return即返回调用处 b++; //不会被执行 return; //不会被执行 }

六、其它

1、typedef : 类型重定义

1
typedef unsigned char uchar;

//用uchar重新定义unsigned char #define uchar unsigned char //用uchar 替换unsigned char 跟宏定义 的区别: ①、typedef 是在编译阶段进行定义 宏定义是在预编译处理阶段完成展开 ②、typedef 是类型重新定义,不是简单地替换 宏定义只是简单的替换,没有定义 typedef unsigned charM; #define M unsigned char M p1,p2; //对于typedef来说:p1是指针变量,p2也是 //对于宏定义来说:p1是指针变量,p2是普通变量

2、sizeof

1
//用来求类型所占的字节数

int var = 10; char g; g = sizeof(var++);//g = 4; //注意:var++该条语句没有被执行! //()里的语句是不会被执行的。

2.2.5 程序的形式

较合理的组织形式: 一个C程序的源代码保存在一个或多个源文件中,但一个函数只能完整出现在同一个源文件中。 C程序的源文件应该包含一组相关的函数&&(使实现抽象数据类型成为可能)。 每个源文件分别编译,最后合体

2.3 程序风格

使用Tab键 别人看的懂

2.4 总结

2.5 警告的总结

2.6 编程提示的总结

2.7 问题

2.8 编程练习

第三章 数据

类型,特点,声明 变量的三个属性——作用域、链接属性、存储类型即 可视性(它在什么地方可以使用) + 生命期(值能保存多久)

3.1 基本数据类型

4种基本数据类型——整形、浮点型、指针、聚合类型

3.1.1 整型家族

字符 短整型 整形 长整型 (搞这么复杂,不就是字符和整形吗) 有符号 无符号 字符在本质上是小整型值

整形字面值

字面值:字面值常量——一种实体,指定了自身的值&&不允许发生改变

枚举类型

它的值是符号常量非字面值的类型 enum

3.1.2 浮点类型

3.1.3 指针

每个内存位置都由地址唯一确认并使用. 指针只是地址的另一个名字. 指针变量就是一个其值为另外一个(一些)内存地址的变量 使用字符串常量会生成一个“指向字符的常量指针”,当一个字符串常量出现于一个表达式时,表达式所使用的值就是这些字符所存的地址。因此,可以把字符串常量赋值给一个“指向字符的指针”,后者指向这些字符所存储的地址. 不能把字符串常量赋值给一个字符数组,因为字符串常量的直接值是一个指针,而不是字符本身. 所以调用库函数操纵.

3.2 基本声明

3.2.1 初始化

3.2.2 声明简单数组

3.2.3 声明指针

3.3 typedef

结构方面 应该使用tupedef而不是#define来创建新的类型名,因为后者

1
2
3
#define d_ptr_to_char char *
d_ptr_to_char  a, b
正确的声明了a但b却被声明为一个字符在关于指针是用typedef更为合适

3.4 常量

const

3.5 作用域(scope)

局部变量局部访问

四种类型:

  • 代码块:{} 之间的——可以被里面的所有语句访问 文件:全局声明的标识符 原型:只适用于在函数原型中声明的参数名 函数:一个函数中的所有语句标签必须唯一

3.5.1 代码块作用域

避免在嵌套的代码块中出现相同的变量名

3.5.2 文件作用域

全局

3.5.3 原型作用域

参数名冲突

3.5.4 函数作用域

只适用于语句标签,语句标签用于goto语句,即一个函数中的所有语句标签必须唯一

3.6 链接属性(如何处理在不同文件中的标识符)

external(外部):表示同一个实体 internal(内部) none(无):单独的个体

3.7 存储类型

指存储变量值的内存类型--决定变量何时创建、何时销毁、它的值将保存多久。 存储变量的3个地方:普通内存、运行时堆栈、硬件寄存器。(不同地方有不同的特性) auto static register extern

初始化

静止变量:0 自动变量:垃圾值

3.8 static关键字

见3.7

3.9 作用域、存储类型示例

3.10 总结

3.11 警告的总结

3.12 编程提示的总结

3.13 问题

第四章 语句

4.1 空语句

只包含一个分号

4.2 表达式语句

= ==

4.3 代码块

{}

4.4 if语句

当else悬空,找最近的

4.5 while语句

循环的测试在循环体开始之前进行

4.5.1 break和continue语句

break:永久终止循环,跳出循环 continue:结束当前循环,进行测试,判断是否进入下一次循环

4.5.2 while语句的执行过程

4.6 for语句

便于把所有用于操纵循环的表达式收集在一起,便于寻找。

4.7 do语句

至少执行一次

4.8 switch

4.8.1 switch中的break语句

最后一个case其实不用,只是以后是维护方便

4.8.2 default子句

所有的值都不跟case匹配 可以用于检测任何非法值

4.8.3 switch语句的执行过程

4.9 goto语句

尽量不用

4.10 总结

4.11 警告的总结

4.12 编程提示的总结

4.11 问题

4.11 编程练习

第五章 操作符和表达式

operator

5.1 操作符

按照优先级分组

5.1.1 算术操作符

1
+ - * / %

5.1.2 移位操作符

<< >>必须为整形 不可用a << -5 右移位:逻辑移位和算数移位(当操作值为-(即最高位为1)时,不同) 逻辑移位:用0补充(与左移位相同) 算术移位:移入的位由先前该值的符号位决定,1 1|| 0 0

5.1.3 位操作符

AND OR XOR

5.1.4 赋值操作符

+=效率更高

5.1.5 单目操作符

!求反 &产生操作数的地址 ~求补1-0 0-1

5.1.6 关系操作符

C用整数代替布尔类型 if(a)=if(a!=0) if (a==0)=if(!a)

5.1.7 逻辑操作符

&&:比> <都要低 ,如果左边的值非零则不再求值 ||:如果左边的值为1,则直接输出 被称为短路求值--测0和非0

5.1.8 条件操作符

表达式1?表达式2:表达式3 优先级很低,一般不加括号也没有问题

5.1.9 逗号操作符

a,b,c逗号操作用于将两个或多个表达式分割开来,自左向右逐个求值,即输出的值为最后一个值(c) 也有优点

1
2
3
4
5
6
7
while(a>0)
{
  a=get();
  asd(a);
}
变为
while(a=get(),asd(a),a>0) 易于维护

5.1.10 下标引用、函数调用和结构成员

下标引用

下标:[] ——并不仅限于数组名 下标引用接受两个操作数:数组名和索引值 下标值总是从零开始,不会对下标值进行有效检查。 array[]——*(array+1)等价(优先级不同)

函数调用

函数调用接受一个或者多个操作数。第一个是希望调用的函数名,剩余的操作数就是传递给函数的参数。 将函数调用以操作符的方式实现——表达式可以代替常量(第七章)

结构成员

. ->都用于访问一个结构的成员。 若s是个结构变量,s.a访问s中叫a的成员 若s是个指向结构的指针,用->访问其内容

5.2 布尔值

C 不具备显式的布尔类型,用整数代替 0是假,其余为真

5.3 左值和右值

a=b+25; a可为左右值,b只可为右值

5.4 表达式求值

类型转换

5.4.1 隐式类型转换

5.4.2 算术转换

5.4.3 操作符的属性

lexp:左值表达式——位置 rexp:右值表达式——值

5.4.4 优先级和求值的顺序

使用临时变量,让函数调用再单独的语句中进行

5.5 总结

5.6 警告的总结

5.7 编程提示的总结

5.8 问题

5.9 编程练习

第六章 指针

基础

6.1 内存和地址

位——字节——int 10亿=4G 硬件事项:边界对齐——int存储的起始位置只是特定的字节(通常是2/4的倍数) ·内存中每个位置都有唯一的地址标识 ·内存中的每个位置都包含一个值 高级语言通过名字来访问内存位置——a,b(即变量)——编译器来实现 硬件仍然通过地址访问内存位置

6.2 值和类型

6.3 指针变量的内容

6.4 间接访问操作符

*:间接访问/解引用指针

6.5 未初始化和非法的指针

声明一个指针变量并不会自动分配内存 初始化

6.6 NULL指针

不指向任何东西,对NULL * 是非法的(ERROR),不可传递

6.7 指针、间接访问和左值

6.8 指针、间接访问和变量

&a=25; &产生a的地址,访问地址——等价于a=25;

6.9 指针变量

6.10 指针的指针

**c:二级指针

6.11 指针表达式

char ch='a'; char cp=&ch; 1.&ch cp &cpcp
2.cp+1(cp+1) 3.++cp cp++ ++cpcp++ (cp)++ 4.++++cp ++*cp++

6.12 实例

6.13 指针运算

当一个指针和一个整数量执行算数运算时,整数在执行加法运算前会调整——指针所指向类型的大小.

6.13.1 算术运算

指针+-整数 指针-指针(两个指针都指向同一个数组中的元素中)=减值/元素长度

6.13.2 关系运算

避免使用

6.14 总结

6.15 编程练习

6.16 编程提示的总结

6.17 问题

6.18 编程练习

第七章 函数

7.1 函数定义

函数:函数体的实现 函数体:一个代码块,被调用时执行 被调用时,返回——实现一种有用的存根目的(为尚未实现的代码保留一个位置)——保持完整性 void:无返回值 return 0 || return (0)

7.2 函数声明

7.2.1 原型

7.2.2 函数的缺省认定

没有见到原型,认定为整型

7.3 函数的参数

传值调用:函数获得参数值的一份拷贝,不会改变实参. 传址调用:被传递的参数是一个数组名并使用下标引用该数组的参数——数组名实际上是一个指针,下标引用是间接访问的另一种方式.

7.4 ADT和黑盒

C可用于设计和实现抽象数据类型(ADT,Abstract Data Type),可以限制函数和数据定义的作用域(static).——黑盒设计. 黑盒实现细节与外界隔绝,访问的唯一方法是通过模块所定义的接口. 模块具有功能说明(模块所执行的任务)和接口说明(模块的使用).

7.5 递归

C通过运行时堆栈支持递归函数(直接或间接调用自身的函数)的实现. 效率低,简单,不去追究细节的实现过程

7.5.1 追踪递归函数

理解函数所声明的变量是如何存储的! 当函数被调用时,它的变量的空间是创建于运行时堆栈上的,以前调用的函数的变量仍保留在堆栈上,但被新函数的变量所覆盖,无法访问. 就是一个循环覆盖的过程,区分分属不同的区. 当重复调用结束时(类似循环)(不同的是,会保存堆栈中的变量值),开始打印输出,return,并开始销毁堆栈上的变量值.

7.5.2 递归与迭代

递归的两个特性:存在限制条件,使递归stop 逐渐趋近 递归的开销:参数必须压到堆栈中、为局部变量分配内存空间、寄存器的值必须保存. 迭代实现:可读性稍差,但效率更高. 即使用循环和交换值

7.6 可变参数列表

函数原型只能显示固定数目的参数,考虑实现让一个函数在不同的时候接受不同数目的参数,当然,有一些限制. 即需要一种机制,能够以一种良好的方法访问数量未定的参数列表.

7.6.1 stdarg宏

这就是解决办法,宏定义于stdarg.h头文件,是标准库中的一部分. 这个库声明了一个类型va_list和三个宏——va_start、va_arg、va_end,相互配合,访问参数的值

7.6.2 可变参数的限制

#include<stdarg.h>
float average( int n_values,....)
{
   va_list   var_arg;
   int   count;
   float sum = 0 ;
   va_start(var_arg,n_values );//访问可变参数
   for(count = 0; count < n; count +=1 )//添加取自可变参数列表的值.
   {
      sum += va_arg( var_arg, int);
   }
   va_end( var_arg);

   return sum / n_values;
}
很危险因为都是缺省认定

7.7 总结

7.8 警告的总结

7.9 编程提示的总结

7.10 问题

7.11 编程练习

第八章 数组

多维数组、指针、初始化

8.1 一堆数组

8.1.1 数组名

int b[4]; b是什么?—— b表示整个数组,错.b是一个指针常量,是数组第一个元素的地址,类型取决于数组元素的类型. (常量的值不能修改)why?——在程序完成链接后,数组的位置固定,当程序运行时,便无法移动 数组具有确定数量的元素,而指针只是一个标量值,当数组名使用时,编译器才会为它产生一个指针常量. 有两种情况,数组名不用指针常量来表示: 1.数组名作为sizeof操作符或单目操作符&. sizeof返回整个数组的长度(不是指针的长度),&数组名产生一个指向数组的指针(不是指向某个指针常量值的指针)

8.1.2 下标引用

int   array[10];
int   *ap = array + 2;

ap              array+2 = &array[2]

*ap             array[2] = *(array+2)

ap[0]           *(ap+(0))=array[2]
                不要惯性思维C的下标引用等价于间接访问

ap+6            array+8=&array[8]

*ap+6           array[2]+6

*(ap+6)         array[8]

ap[6]           *(ap+6)=array[8]

&ap             合法但目前不知道ap在哪里

ap[-1]          array[2-1]=array[1]

ap[9]           非法但很多编译器不报错而且不检查相应下标错误可能会随机给出数字

8.1.3 指针与下标

下标绝对不会比指针更有效率,但有时候指针会比下标更有效率

8.1.4 指针的效率

正确的使用指针

8.1.5 数组和指针

8.1.6 作为函数参数的数组名

C里面所有的参数传递都是传值的方式,指针能通过间接访问更改实参的值,是因为数组的传值调用传递的是参数的一份副本,可以自由操纵 eg: *string++ 取得string所指向的那个字符,并修改string,使其指向下一个字符

8.1.7 声明数组参数

8.1.8 初始化

8.1.9 不完整的初始化

8.1.10 自动计算数组长度

8.1.11 字符数组的初始化

8.2 多维数组

1
2
3
4
int a;
int b[10];
int c[6][10];
int d[6][6][10];

a是一个简单的整数,增加了一个维数,b是一个向量,包含10个整型元素. c只是在b的基础上再增加一维,c可以看成一个包含6个元素的向量,只不过它的每个元素本身是一个包含10个整型元素的数组。 d就是一个3排、6行、10列的整型三维数组.

8.2.1 存储顺序

在C中,多维数组的元素存储顺序按照最右下标率先变化的原则,称为行主序. 遵守数组元素的实际存储方式.

8.2.2 数组名

8.2.3 下标

int matrix[3][10]
matrix[1][5]
matrix                     类型:指向包含10个整型元素的数组的指针
                           :指向包含10个整形元素的第一个子数组

matrix + 1                 指向matrix的另一行
                           注意1是根据数组的长度进行调整

*(matrix + 1)              下一行的数组的第一个元素
                           =matrix[1]

*(matrix + 1) + 5          下一行的数组的第六个元素

*(*(matrix + 1) + 5)       *(matrix[1]+5)
                           即matrix[1][5]
                  太妙了!!!

8.2.4 指向数组的指针

1
2
3
4
5
6
int a[10],*b=a;
int c[3][10],*d=c;//非法,初始化错误
int (*p)[10];//下标引用的优先级高于间接访问,[]使得间接访问首先执行
int (*p)[10] = a;//使p指向a的第一行
int *pi=&a[0][0];
int *pi=a[0];//指向a的第一个整型元素

8.2.5 作为函数参数的多维数组

8.2.6 初始化

8.2.7 数组长度自动计算

8.3 指针数组

8.4 总结

8.5 警告的总结

8.6 编程提示的总结

8.7 问题

8.8 编程练习

第九章 字符串、字符和字节

字符串以字符串常量的形式出现或者存储于字符数组中——C语言没有显式的字符串数据类型.

9.1 字符串基础

'\0'结尾 NUL

9.2 字符串长度

size_t strlen(char const *string) size_t是在头文件stddef.h中定义的,是一个无符号整数类型 寻找一种更好的算法比改良一种差劲的算法更有效率 复现已经存在的软件比重用已经存在的软件比重新开发一个效率更高

9.3 不受限制的字符串函数

程序员要自己判断

9.3.1 复制字符串

char strcpy(char dst,char const *src)

9.3.2 连接字符串

char strcat(char dst)

9.3.3 函数的返回值

返回一个指向目标函数的指针——可以嵌套使用 strcat(strcpy(dst,a), b) 很多时候他们的返回值都会被忽略

9.3.4 字符串比较

int strcmp(char const s1,char const s2) s1<s2 <0 s1>s2 >0 s1=s2 =0

9.4 长度受限的字符串函数

还有一些函数以另一种不同的方式处理字符串,这些函数接收一个显式的长度参数,这种机制可以防止难以预料的长字符串从他们的目标数组溢出

1
2
3
char *strncpy( char *dst, char const *src, size_ t len );
char *strncat( char *dst, char const *src, size_ t len );
int strncmp( char const *s1, char const *s2 size_ _t len);

strncpy:如果strlen(src)len——有多少就复制多少 !:它的值不以NUL字节结尾

9.5 字符串查找基础

9.5.1 查找一个字符

找到后返回一个指向该位置的指针,if not just return NULL

char *strchr(char const *str,int ch)
char *strrchr(char const *str,int ch)//她所返回的是一个指向字符串中该字符最后出现的位置(当有多个值时,即最右边那个)

9.5.2 查找任何几个字符

查找任何一组字符第一次在字符串中出现的位置 char strpbrk( char const str, char const *group) 返回一个指向str中第一个匹配group中任何一个字符的字符位置,if not just return NULL

9.5.3 查找一个子串('\0')

char strstr( char const s1, char const *s2) 返回一个指向s1中第一个匹配s2中第一个一个字符的字符位置,if not just return NULL

9.6 高级字符串查找

简化从一个字符串中查找和抽取一个子串的过程

9.6.1 查找一个字符串前缀

strspn、strcspn用于在字符串的起始位置对字符计数 size_t strspn( char const str, char const group) 就是从str的第一个元素开始往后数,看str中是不是连续往后每个字符都在group中可以找到。到第一个不在gruop的元素为止。看从str第一个开始,前面的字符有几个在group中

#include <stdio.h>
#include <string >;

int main() {
    int len1, len2;
    char buffer[] = "25,142,330,Smith,J,239-4123";

    len1 = strspn(buffer, "0123456789");
    len2 = strspn(buffer, ",0123456789");

    printf("  len1为:%d;  len2为:%d", len1, len2);

    getchar();
    return 0;

}                结果len1=2;len2=11;

size_t strcspn( char const str, char const group) 用来检索字符串s1开头连续有几个字符都不含字符串s2中的字符 strcspn中的C来源于求补这个概念

9.6.2 查找标记

从字符串中隔离各个单独的称为标记(token)的部分,并丢弃分隔符 char strtok( char str,char const *sep) 为什么str前面无const——strtok函数会修改它所处理的字符串 if str const 则复制一份在传递给strtok函数

9.7 错误信息

9.8 字符操作

9.9 内存操作

9.10 总结

9.11 警告的总结

9.12 编程提示的总结

9.13 问题

9.14 编程练习

第十章 结构和联合

10.1 结构基础知识

10.1.1 结构声明

10.1.2 结构成员

10.1.3 结构成员的直接访问

10.1.4 结构成员的间接访问

10.1.5 结构的自引用

10.1.6 不完整的声明

10.1.7 结构的初始化

10.2 结构、指针和成员

10.2.1 访问指针

10.2.1 访问结构

10.2.1 访问结构成员

10.2.1 访问嵌套的结构

10.2.1 访问指针成员

10.3 结构的存储分配

10.4 作为函数参数的结构

10.5 位段

10.6 联合

10.6.1 变体记录

10.6.2 联合初始化

10.7 总结

10.8 警告的总结

10.9 编程提示的总结

10.10 问题

10.11 编程练习

第十一章 动态内存分配

11.1 为什么使用动态内存配

11.2 malloc和free

11.3 calloc和realloc

11.4 使用动态分配的内存

11.5 常见的动态内存错误

11.6 内存分配实例

11.7 总结

11.8 警告的总结

11.9 编程提示的总结

11.10 问题

11.11 编程练习

第十二章 使用结构和指针

12.1 链表

12.2 单链表

12.2.1 在单链表中插入

12.2.2 其他链表操作

12.3 双链表

12.3.1 在双链表中插入

12.3.2 其他链表操作

12.4 总结

12.5 警告的总结

12.6 编程提示的总结

12.7 问题

12.8 编程练习

第十八章 高级指针话题

13.1 进一步探讨指向指针的指针

13.2 高级声明

13.3 函数指针

13.3.1 回调函数

13.3.2 转移表

13.4 命令行参数

13.4.1 传递命令行参数

13.4.2 处理命令行参数

13.5 字符串常量

13.6 总结

13.7 警告的总结

13.8 编程提示的总结

13.9 问题

13.10 编程练习

第十四章 预处理器

14.1 预定义符号

14.2 #define

14.2.1 宏

14.2.2 #define函数

14.2.3 宏与函数

14.2.4 带副作用的宏参数

14.2.5 命名约定

14.2.6 #undef

14.2.7 命令行定义

14.3 条件包含

14.3.1 是否被定义

14.3.2 嵌套指令

14.4 文件包含

14.4.1 函数库文件包含

14.4.2 本地文件包含

14.4.3 嵌套文件包含

14.5 其他指令

14.6 总结

14.7 警告的总结

14.8 编程提示的总结

14.9 问题

14.10 编程练习

第十五章 输入/输出函数

15.1 错误报告

15.2 终止执行

15.3 标准I/O函数库

15.4 ANSI I/O概念

15.4.1 流

15.4.2 文件

15.4.3 标准I/O常量

15.5 流I/O总览

15.6 打开流

15.7 关闭流

15.8 字符I/O

15.8.1 字符I/O宏

15.8.2 撤销字符I/O

15.9 未格式化的行I/O

15.10 格式化的行I/O

15.10.1 scanf家族

15.10.2 scanf格式代码

15.10.3 printf家族

15.10.4 printf格式代码

15.11 二进制I/O

15.12 刷新和定位函数

15.13 改变缓冲方式

15.14 流错误函数

15.15 临时文件

15.16 文件操纵函数

15.17 总结

15.18 警告的总结

15.19 编程提示的总结

15.20 问题

15.21 编程练习

第十六章 标准函数库

16.1 整型函数

16.1.1 算术

16.1.2 随机数

16.1.3 字符串转换

16.2 浮点型函数

16.2.1 三角函数

16.2.2 双曲函数

16.2.3 对数和指数函数

16.2.4 浮点表示形式

16.2.5 幂

16.2.6 底数、顶数、绝对数、和余数

16.3 日期和时间函数

16.3.1 处理器时间

16.3.2 当天时间

16.3.3 日期和时间转换

16.4 非本地跳转

16.4.1 实例

16.4.2 何时使用非本地跳转

16.5 信号

16.5.1 信号名

16.5.2 处理信号

16.5.3 信号处理函数

16.6 打印可变参数列表

16.7 执行环境

16.7.1 终止执行

16.7.2 断言

16.7.3 环境

16.7.4 执行系统命令

16.7.5 排序和查找

16.8 locale

16.8.1 数值和货币形式

16.8.2 字符串和locale

16.8.3 改变locale的效果

16.9 总结

16.10 警告的总结

16.11 编程提示的总结

16.12 问题

16.13 编程练习

第十七章 经典抽象数据类型

17.1 内存分配

17.2 堆栈

17.2.1 堆栈接口

17.2.2 实现堆栈

17.3 队列

17.3.1 队列接口

17.3.2 实现队列

17.4 树

17.4.1 在二叉搜查树中插入

17.4.2 从二叉搜查树删除节点

17.4.3 在二叉搜查树中查找

17.4.4 树的遍历

17.4.5 二叉搜索树接口

17.4.6 实现二叉搜索树

17.5 实现的改进

17.5.1 拥有超过一个的堆栈

17.5.2 拥有超过一个的类型

17.5.3 名字冲突

17.5.4 标准函数库的ADT

17.6 总结

17.7 警告的总结

17.8 编程提示的总结

17.9 问题

17.10 编程练习

第十八章 运行时环境

18.1 判断运行时环境

18.1.1 测试程序

18.1.2 静态变量和初始化

18.1.3 堆栈帧

18.1.4 寄存器变量

18.1.5 外部标识符的长度

18.1.6 判断堆栈帧布局

18.1.7 表达式的副作用

18.2 C和汇编语言的接口

18.3 运行时效率

18.4 总结

18.5 警告的总结

18.6 编程提示的总结

18.7 问题

18.8 编程练习

[^在其他语言中,这种叫做过程]


  1. 字符串终止符(不是字符串的一部分)
    ·是ASCII字符集中‘\0’字符的名字,字节模式为全0.
    ·NULL在头文件stdio.h中定义,不存在预定义的符号NUL,若要使用他而不是字符常量‘\0’,就必须自行定义
     

  2. 即地址调用. 

  3. 在C语言中,枚举型、字符型和各种整数的表示形式统一叫做标量类型.当在C表达式中使用标量类型的值时,编译器就会自动将这些标识符转换为整数保存.这种机制的作用是,在这些标量类型上执行的操作与整型上执行的操作完全一样.