- 第一章 快速上手
总结:
1.所有C程序必须有一个main函数,它是程序执行的起点。
2.printf函数执行格式化输出,scanf用于格式化输入,getchar和putchar分别执行非格式化字符的输入和输出。
3.从逻辑上删除一段C代码,更好的办法是用#if指令。
例:#if 0
/* 代码*/
#endif
4.C语言中,数组参数是传址调用,而标量和常量是按值传递。
5.字符串常量为双引号括起来的一串字符。
例:”hello”
6.gets函数作用是从标准输入读取一行文本存储于作为参数传递给它的数组中,而puts函数作用是把指定的字符串写到标准输出并在末尾添上一个换行符。
7.使用下标前要检查它们的值,防止下标越界
8.char *strchr(const char *s, int c) 函数作用:在字符串中搜索字符参数第一次出现的位置。
strstr(str1,str2) 函数作用:判断字符串str2是否是str1的子串。如果是,则该函数返回str2在str1中首次出现的地址;否则,返回NULL。
- 第二章 基本概念
2.1 编译和连接
1.编译并链接几个C源文件: cc main.c sort.c lookup.c
2.编译几个C源文件,并为每个文件产生一个目标文件:cc -c main.c sort.c lookup.c
3.编译时加”-lname”标志,可使程序链接到name库
4.程序必须载入到内存中才能执行,绝大多数环境使用堆栈来存局部变量和其他数据。
2.2 词法规则
标识符:由大小写字母、数字、下划线组成,不能以数字开头,关键字不能作为标识符。
- 第三章 数据
总结:
1.枚举类型是整数类型,把字符声明为unsigned或signed可高程序的可移植性
2.隐式声明:函数不显示声明返回值的类型时,返回值默认为int。
3.typedef 可创建新的类型名(要和#define区别开来)
4.int const *p :指向整型常量的指针,可以修改指针,不能修改指针指向的值
int * const p :执行整型的常量指针,指针无法修改,可以修改针所指向的值
5.编译器的四个作用域:文件作用域、函数作用域、代码块作用域和原型作用域。
6.标识符3种链接属性:external(外部)、internal(内部)、none(无)
static关键字可改变标识符的链接属性为internal(内部)
7.存储类型:任何代码块之外的声明的变量存储于静态内存,在程序运行之前创建。
8.extern可跨文件调用变量、函数。
- 第四章 语句
总结:
1.例:
y+3;
getchar();
语句合法,但结果不保存于任何地方
2.使用if时,注意”悬空的else”问题
3.break:永久终止循环 continue:跳过本次循环
4.switch 和case
两个规则:
1.每个case语句的结尾绝对不要忘了加break,否则将导致多个分支重叠(除非有意使多个分支重叠)
2.最后必须使用default分支。
记住:
case后面只能是整型或字符型的常量或常量表达式。
5.while语句和do语句的选择: 循环体至少执行一次时,选择do
- 第五章 操作符和表达式
总结:
1.汇编里移位操作:
左移:值最左边的几位被丢弃,右边多出来的空位补0
右移:
逻辑移位:左边移入的位填充0
算术移位:左边移入的位由原先该值的符号位确定,符号位为1则填充1,符号位为0则填充0
2.位操作:
把指定位设为1: value = value | 1 << bit_number
把指定位清零: value = value & ~ (1 << bit_number)
3.单目操作符( ! ++ – +(正号) -(负号) ~ &(取地址) *(与指针一起用) sizeof !)
. -> 操作符用于访问一个结构的成员 结构变量用 . 指向结构的指针用->
布尔值:0为假 ,任何非0值皆为真
6.左值为表达式,而右值表示一个值。
- 第六章 指针
1.硬件通过地址访问内存位置。
2.通过一个指针访问它所指向的地址称为间接访问或解引用指针。
例:
int a=200;
int *p=&a;
printf(“%d\n”,*p); //*p称为间接访问或解引用指针。
3.指针使用必须初始化,对一个NULL指针进行解引用操作是非法的。
4.把25存储于位置100
(int )100 = 25;
5.二级指针:
例: int a = 12;
int *b = &a;
int **c = &b;
相当于*(*c) , *c访问c所指向的位置 ,**c 访问这个位置所指向的地址,也即是a
6.指针算术运算两种形式:
- 指针 + - 整数
注意下标越界问题。
- 指针 - 指针
只有当两个指针都指向同一个数组中的元素时,才允许从一个指针减去另一个指针。
7.对指针执行间接访问时,必须初始化指针。
- 第七章 函数
总结:
1.函数的标量参数分传值调用和传址调用,像函数的数组参数就是传址调用。
2.C语言可用于设计和实现抽象数据类型(ADT),也就是黑盒设计。
3.递归函数调用运行时开销:参数必须压到堆栈中,为局部变量分配空间。而非递归实现往往比递归实现效率高。
4.可变参数列表
stdarg宏,定义于stdarg.h头文件
一个va_list类型和三个宏– va_start、va_arg 、va_end
- 第八章 数组
总结:
1.数组名的值是一个指针常量(注意不是指针变量)。
2.不能用赋值符把一个数组的所有元素复制到另一个数组,要用循环一次复制一个。
错误例子:
int a[3] = {1,2,3};
int b[10];
b = a;
3.在有些环境,用register关键字可提高程序的运行效率。
4.下标表达式 array[value] 和间接访问表达式 *(array+(value)) 等价。
5.数组名作为函数参数传递,实际传递的是指向数组第一个元素的指针。
- 第九章 字符串、字符和字节
总结:
1.字符串就是一个或多个字符,以’\0’结尾,字符串长度不包括’\0’字节
2.strlen函数返回值为无符号整数,用于表达式时小心。
例:if(strlen(x) - strlen(y) >= 0) … 则条件永远为真
3.字符串函数:
复制字符串: char *strcpy(char *dst,const char *src);
连接字符串: char *strcat(char dst,const charsrc);
使用时必须保证目标字符数组的空间足以容纳需要复制的字符。
比较字符串: int strcmp(char const *s1,char const *s2);
查找一个字符:char *strchr(char const *str,int ch);
在字符串str中查找字符ch第一次出现的位置,找到则返回指向该位置的指针,找不到返回NULL指针。
查找一个子串:char *strstr(char const *s1,char const *s2);
在s1中找整个s2第一次出现的起始位置,找到则返回指向该位置的指针,找不到返回NULL指针
4.错误信息
strerror函数
例:printf(“errno is %d\n”,strerror(errno));
5.内存操作
void *memcpy(void *dst,void const *src,size_t length);
memcpy从src的起始位置复制length个字节到dst的内存起始位置,要注意传入的地址是否为空,还要注意地址重叠问题。
void *memmove(void *dst,void const *src,size_t length);
和memcpy功能相同,但memmove可以处理地址重叠情况。
void *memset(void *buffer,int c,int count);// c为要填充的字符值
- 第十章 结构体和联合
总结:
1.结构体
2.结构体的初始化:一对花括号号内部、由逗号分隔的初始值来初始化成员
例:
3.结构体内存对齐问题:
技巧:结构体成员按成员的对齐数从大到小顺序排序,可减少因边界对齐带来的空间损失
4.结构体作为函数参数时,向函数传递一个指向结构体的指针效率更高。
5.位段:访问一个值内部任意的一个内存位置,位段比较简便。
6.联合体:所有成员都存储于同一个内存位置
联合变量初始化时,这个值必须是联合第一个成员的类型。
- 第十一章 动态内存分配
总结:
1.malloc 和 free
原型:
void *malloc(size_t size);
void free(void *pointer);
free分配的是一块连续的内存,分配成功返回指向该内存的指针,分配失败返回NULL
可移植性例子:
char *pi;
pi = malloc(25 * sizeof(int));//申请100个字节的内存
- calloc 和 realloc
calloc和malloc的区别在于calloc返回指向内存的指针之前把它初始化为0,而malloc不会,要自己手动初始化这块内存。
realloc函数用于修改一个原先已经分配好的内存块的大小,若扩大,则原先的内容保留
,新增的内存添加到原先的内存块后面,若缩小,则丢弃相差的内存块。
3.常见的动态内存错误:
1.对NULL指针进行解引用
2.对分配的内存进行操作越界
3.释放非动态内存
4.释放一块动态分配的内存的一部分
错误例子:
char p=(char)malloc(10 * sizeof(char));
free(pi + 5);
5.一块动态内存被释放后继续使用
6.分配内存使用完后不释放,造成内存泄露
- 第十二章 使用结构体和指针
总结:
链表:是顺序结构,由多个非连续节点组成.通过节点中指针连接相邻的节点。
如果节点存存储一个指针称为单链表,如果节点存储两个指针称为双链表。
操作链表是通常定义三个特殊指针指向三个节点:
1.指向链表头结点指针.一般用来标记链表起始地址值.不存储任何有效数据。
2.指向链表当前访问节点指针,移动指针访问链表每个节点。
3.指向链表新节点指针。
单链表只能以一个方向进行遍历,而双链表可以从任何一端开始
4.单链表:每个节点包含指向链表下一个节点的指针,链表最后一个节点的指针为NULL。
- 第十三章 高级指针话题
总结:
1.函数指针最常见的两个用途:回调函数、转换表。
回调函数:
2.命令行参数
int main(int argc,char *ragv[]) 或 int main(int argc,char **argv)
main有两个形参,第一个argc表示行参数的数目,第二个argv指向一组参数值
例: gcc -c -o main.c insert.c -o test
argc为7, argv[0]–argv[6]分别为 gcc -c -o main.c insert.c -o test