第一章 快速上手
1.1 简介
- c语言中所有的函数参数都是按值传递
- 应该使用typedef而不是#define来创建新的类型名,因为#define不能正确的处理指针。
int const *pci;//pci是一个指向常量的指针,pci的值可以修改
int *const pci;//pci是一个常量指针,pci的值不能修改
int const * const cpci;//都不允许修改
第三章 数据
基本数据类型
C语言只有整型 浮点型 指针 和聚合类型(数组 结构体)这4种基本数据类型。
整型
整型包括字符 短整型 整型 和长整型,分为unsigned和signed 。长整型至少应该与整型一样长,整型至少应该和短整型一样长。short int 至少16位,long int 至少32位,int究竟是16位还是32位由编译器决定。
枚举类型enum以整型的方式存储,如果某个符号名未赋值,那么它的值比前一个符号名值+1;。
浮点类型
浮点数包括float double和long double,所有浮点类型至少要能够容纳10-37~1037之间的任何值,浮点数字面值缺省情况下是double类型的。
指针
可以把内存想象成一间间房子,指针就是门牌号,房子里的物品就是内存的值。
指针常量:c语言中没有定义指针常量。
字符串常量:字符串通常存储于字符数组中,以NUL字节结尾。在程序中使用字符串常量会生成一个”指向字符的指针“,因此可以将一个字符串常量赋值给一个”指向字符的指针“,但是不能将一个字符串常量赋值给一个字符数组。
基本申明
说明符(一个或者多个)+声明表达式列表
声明数组
C语言编译器并不检查程序对数组下标的引用是否越界。
声明指针
int *message = "hello world"
这条语句把message声明为一个指向字符的指针,并且把字符串常量中第一个字符的地址赋值给message。
typedef
typedef原则允许为各种数据类型定义新名字。
typedef char *ptr_to_char
ptr_to_char a;
这个声明把标识符ptr_to_char作为指向指针类型的新名字。a是一个指向字符的指针。应该使用typedef而不是#define 来创建新的类型名,因为#define无法处理指针类型。
#define d_ptr_to_char char *
d_ptr_to_char a, b;
上面代码正确的声明了a,但是b却被声明为一个字符。
常量const
int const a
const int a
a声明为一个整数,它的值不能被修改。有两种给常量赋值的方式:
在声明时对它进行初始化
-
在函数中声明const形参在函数调用时会得到实参的值。
int const *pci;//pci指向整型常量的指针,可以修改指针的值,但你不能修改它所指向的值。
int * const pci;//pci指向整型的常量指针,可以修改指向的值,但你不可以指针的值。
int const * const pci;//无论是指针本身还是它指向的值都是常量,不允许修改。
作用域
编译器可以确定4种不同类型的作用域-文件作用域 函数作用域 代码块作用 和原型作用域
链接属性-external internal none
- external 链接属性的标识符无论声明多少次,位于几个源文件都表示同一个实体。
- internal 在同一个源文件内的所有声明都指向同一个实体。
- none 该标识符的所有声明都指向不同的实体。
注意:static 会把默认的external链接属性变成internal。
存储类型
有三个地方可以存储变量:普通内存 运行时堆栈 硬件寄存器
static关键字
- static关键字放在代码块之外是修改变量的链接属性,从external变成internal,不修改存储属性。
- static关键字放在代码块之内是修改变量的存储属性,从自动变量变为静态变量,链接属性和作用域不受影响。
第四章 语句
4.1 空语句
4.2 表达式语句
c语言不存在赋值语句,赋值是一种操作。意味着像以下这样的语句是完全合法的。
y+3;
getchar();
```
## 4.3 代码块
## 4.4 if语句
c语言不具备布尔类型,而是用整形来代替。
## 4.5 while语句
```
while( (ch = getchar()) != EOF && ch != '\n' )
```
## 4.6 for语句
。。。。。
#第五章 操作符和表达式
## 5.1 操作符
```
int x, xx[5];
sizeof(int);//返回整形变量的字节数
sizeof(x);//返回变量x所占据的字节数
sizeof(xx);//返回xx数组的长度,以字节为单位,此处为20
```
#第六章 指针
## 6.1 内存和地址
1. 内存中的每一个字对应一个地址,一个字可以是一个或者多个字节。
## 6.3 指针变量的内容
1. 变量的值就是分配给该变量的内存位置所存储的数值,指针变量也不列外。
## 6.4 间接访问操作符
1. 指针并不存在内建的间接访问属性,所以通过间接访问操作符访问指针所指向的位置。
#第七章 函数
##7.7总结
1. 函数形参都是通过传值方式调用的,实际传送的是实参的拷贝。
2. 数组名传递也是通过传值的方式传递的,它传给函数的是一个指向该数组的指针的拷贝。
3. 函数中的形参数组使用了下标引用操作,就会引发**间接访问** 操作,它实际访问的是实参数组的元素。这个行为被称为**传址调用**。
#第八章 数组
##8.1一维数组
1. 数组具有和指针完全不同的特征,只有当数组名在表达式中使用时,编译器才会为数组名产生一个**指针常量**。注意是指针常量而不是指针变量。
2. 数组名作为sizeof操作符时,sizeof返回整个数组的长度。
3. 下标绝不会比指针更有效率,但指针有时会比下标更有效率。
##8.2多维数组
```
int matrix[3][10];
```
matrix这个名字的值表示指向第一个元素的指针,所以matrix是一个指向一个包含10个元素数组的**指针**。
```
func(matrix);
```
func函数的原型应该是下面两种形式中的任何一种:
```
void func(int (*mat)[10]);
void func(int mat[][10]);
```
把func写成下面的原型是**不正确的**:
```
void func(int **mat);
```
因为mat是一个指向整形指针的指针,它与指向整型数组的指针并不是一回事。
##8.3 指针数组
```
int *api[10];
```
下标引用的优先级高于间接访问的优先级,首先执行下标引用,所以api是一个数组;在获取到一个数组元素后,执行间接访问,间接访问的结果是一个整型,所以数组元素是一个整型指针。
#第九章 字符串
##9.1 字符串基础
1. NUL字节是字符串的终止符,但它本身并不是字符串的一部分,所以字符串的长度并不包括NUL字节。
#第十章 结构和联合
##10.1 结构成员
1. 结构成员使用(.)操作符访问
2. (.)操作符的优先级高于间接访问操作符,所以结构成员的间接访问需要添加括号,如下
```
void func(struct COMPLEX *cp);
(*cp).f;
```
这样不太方便,所以提供了->箭头操作符,箭头操作符左操作数必须是一个指向结构的指针。
```
cp->f
```
3. 结构的自引用
```
struct SELF_REF1 {
int a;
struct SELF_REF1 b;
int c;
};
非法
```
```
struct SELF_REF1 {
int a;
struct SELF_REF1 *b;
int c;
};
合法
```
##10.3 结构的存储分配
#第11章 动态内存分配
## 11.2 malloc和free
1. malloc从内存池提取一块内存,并向该程序返回一个指向这块内存的指针,但是这块内存并没有初始化
2. 对每个malloc返回的指针都要进行检查,确保非NULL
3. 向free传递一个NULL参数不会产生任何效果
4. void *类型的指针可以转换为其他任何类型的指针
#第十三章 高级指针话题
##13.3 函数指针
```
int f(int);
int (*pf)( int ) = &f;
```
初始化表达式中的&操作符是可选的,因为**函数名**在使用时总是由编译器把它转换为**函数指针**。