[C/C++]重读《The C Programming Language》
第一次读这本书的时候是大三初,现在打算重读一遍!。
第一章 导言
1. 学习一门新程序设计语言的唯一途径就是用它来写程序。
2. 每个程序都从main函数的起点开始执行。
3. 在C语言中,所有变量必须先声明后使用。
4. C语言中的基本数据类型的大小取决于具体机器。
5. 在允许使用某种类型变量值的任何场合,都可以使用该类型的更复杂的表达式。
6. 标准库的输入/输出模型是按照字符流的方式处理,每行字符都有0个或者多个字符组成,末尾是换行符。
7. 函数为计算的封装提供了一种简便的方法,函数都是通过传值调用。
1 2 3 4 5 6 7 8 9 10 | #include <stdio.h> // 文件复制 int main() { int c; while ((c = getchar ()) != EOF) { putchar (c); } return 0; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | /* * 编写一个将输入复制到输出的程序, * 并将其中连续的多个空格用一个空格代替. */ #include <stdio.h> int main() { int c; int flag = 0; // 当前字符是否为空格 while ((c = getchar ()) != EOF) { if ((c == ' ' && flag == 0) || (c != ' ' )) { putchar (c); flag = !flag; } } return 0; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | #include <stdio.h> #define IN 1 // 单词内 #define OUT 0 // 单词外 // 统计行数, 单词数和字符数 int main() { int c; int numLines = 0; // 行数 int numWords = 0; // 单词数 int numCharacters = 0; // 字符数 int state = OUT; while ((c = getchar ()) != EOF) { ++numCharacters; if (c == '\n' ) { ++numLines; } if (c == ' ' || c == '\t' || c == '\n' ) { state = OUT; } else if (state == OUT) { state = IN; ++numWords; } } printf ( "%d\t%d\t%d\n" , numLines, numWords, numCharacters); return 0; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | #include <stdio.h> #define MAXLINE 1000 /* * 读入一行字符 */ int getline_s( char *s, int lim) { int c, i; for (i = 0; i < lim - 1 && (c = getchar ()) != EOF && c != '\n' ; ++i) { s[i] = c; } if (c == '\n' ) { s[i] = c; ++i; } s[i] = '\0' ; return i; } /* * 拷贝数组 */ void copy( char *to, char *from) { int i = 0; while ((to[i] = from[i]) != '\0' ) { ++i; } } int main() { int len; int max = 0; char line[MAXLINE]; char longest[MAXLINE]; while ((len = getline_s(line, MAXLINE)) > 0) { if (len > max) { max = len; copy(longest, line); } } if (max > 0) { printf ( "%s\n" , longest); } return 0; } |
第二章 类型,运算符与表达式
1. 变量名不要以下划线开头,以避免与标准库的冲突。
2. 数据类型及描述,此外还可以在这些基本数据类型上加上一些限定符,例如short和long等。
数据类型 | 描述 |
char | 字符型,占用一个字节,可以存放本地字符集的一个字符 |
int | 整型,通常反映所用机器中整数最自然的长度 |
float | 单精度浮点数 |
double | 双精度浮点数 |
3. short与int类型至少为16位,而long类型至少为32位,并且short类型不得长于int类型,而int类型不得长于long类型。
4. 有关类型长度定义的符号常量以及其他与机器和编译器有关的属性可在<limits.h>和<float.h>中找到。
5. 常量表达式在编译时求职,不在运行时求值。(#define)
6. 字符串常量就是字符数组。
7. 枚举常量: enum boolean{NO, YES}。
8. 默认情况下,外部变量与静态变量初始化为0,未经初始化的自动变量的值未定义。
9. 在负操作数的情况下,整数除法截取的方向以及驱魔结果的符号取决于具体实现。
10. 标准库<ctype.h>定义了一组与字符集无关的测试和转换函数。
11. 位操作运算符:按位与(&),按位或(|),按位异或(^),左移(<<),右移(>>)。
12. sizeof(对象长度)是运算符,我一直以为是函数。冏
1 2 3 4 5 6 7 8 9 | // strlen简化版本 int strlen ( const char *str) { int i = 0; while (str[i] != '\0' ) { ++i; } return i; } |
1 2 3 4 5 6 7 8 9 10 11 12 | // 将字符串转换成整数值 int atoi ( char *s) { int i; int result = 0; for (i = 0; s[i] != '\0' ; ++i) { if (s[i] >= '0' && s[i] <= '9' ) { result = result * 10 + (s[i] - '0' ); } } return result; } |
1 2 3 4 5 6 7 8 9 10 11 | unsigned long next = 1; //返回取值0~32767之间的伪随机数 int rand ( void ) { next = next * 110531245 + 12345; // ? return (unsigned int )(next / 65535) % 32768; } // 随机数种子 void srand (unsigned int seed) { next = seed; } |

1 // 从字符串s中删除字符c 2 void squeeze(char *s, int c) { 3 int i, j; 4 for (i = j = 0; s[i] != '\0'; ++i) { 5 if (s[i] != c) { 6 s[j++] = s[i]; 7 } 8 } 9 s[j] = '\0'; 10 }

1 // 将字符串t连接到字符串s的尾部, 假设s有足够的空间 2 void strcat(char *s, char *t) { 3 int i, j; 4 i = j = 0; 5 while (s[i] != '\0') { 6 i++; 7 } 8 9 while ((s[i++] = t[j++]) != '\0'); 10 }
第三章 控制流
1. continue语句只用于循环语句,不用于switch语句。
2. 少用goto。

1 // 希尔排序 2 void shellsort(int *v, int n) { 3 int gap; 4 int i, j; 5 int temp; 6 for (gap = n / 2; gap > 0; gap /= 2) { 7 for (i = gap; i < n; ++i) { 8 for (j = i - gap; j >= 0 && v[j] > v[j + gap]; j -= gap) { 9 temp = v[j]; 10 v[j] = v[j + gap]; 11 v[j + gap] = temp; 12 } 13 } 14 } 15 }
第四章 函数与程序结构
1. register可以声明寄存器变量,即所声明的变量在程序中使用频率较高,该变量被放入机器的寄存器中,使得程序更小,执行速度更快。但编译器可以忽略。
2. 初始化:
- 在不进行显式初始化时,外部变量和静态变量都被初始化为0,而自动变量和寄存器变量的初值则没有定义;
- 对于外部变量和静态变量,初始化表达式必须是常量表达式,且只初始化一次;
- 数组的初始化可以再声明的后面紧跟一个初始化表达式列表,用花括号括起来,当数组长度大于初始化元素个数时,对于外部变量,静态变量和自动变量来说,没有初始化的元素被初始化为0;
- 字符数组初始化可以用一个字符串来代替用花括号括起来并用逗号分隔的初始化表达式序列。
3. 预处理器:
- 文件包含: #include "*.h" 和 #include <*.h> ;
- 宏替换: #define 名字 替换文本, 宏定义就是文本替换, #undef 取消名字的定义;
- 条件包含: #if , #endif, #elif, #else和 #ifndef, #ifdef

1 // 使用递归打印十进制数n 2 void printd(int n) { 3 if (n < 0) { 4 putchar('-'); 5 n = -n; 6 } 7 8 if (n / 10) { 9 printd(n / 10); 10 } 11 12 putchar(n % 10 + '0'); 13 }

// 快速排序 void qsort(int *v, int left, int right) { int i, last; if (left >= right) return ; swap(v, left, (left + right) / 2); last = left; for (i = left + 1; i <= right; ++i) { if (v[i] < v[left]) { swap(v, ++last, i); } } swap(v, left, last); qsort(v, left, last - 1); qsort(v, left + 1, right); }
第五章 指针和数组
1. 通用指针类型void *;
2. 地址运算符&只能用于内存中的对象,即变量和数组元素,不能用于表达式,常量或者register类型的变量;
3. 一元运算符*是间接寻址或者间接引用运算符,作用于指针,用来取指针所指向的对象;
4. 数组和指针的不同: 指针是一个变量, pa = a 和 ++pa都是合法的,但是数组名不是变量, a = pa 和 ++a是非法的;
5. 类型ptrdiff_t表示两个指针之间的差值;
6. 字符串常量是一个字符数组;
1 // 该过程没有进行字符串的复制,而只是涉及到指针操作 2 // c语言没有提供将整个字符串作为一个整体进行处理的运算符 3 cha *pmessage; 4 pmessage = "now is the time";
7. 下面两个定义差别很大:
1 // 定义一个数组 2 char amessage[] = "now is the time"; 3 4 5 // 定义一个指针 6 char *pmessage = "now is the time";
8. 在C语言中,二维数组其实是一个特殊的一维数组,数组元素按行存储;
9. 下面两种声明:
// 该数组有13个元素,其中每个元素都是指向整型对象的指针 int* daytab[13]; // daytab是一个指针,指向具有13个整型元素的一维数组 int (*daytab)[13];
10. strstr(s, t)函数用于返回字符串t在字符串s中第一次出现的位置, 否则返回NULL;
第六章 结构
1. 结构的初始化:
1 // Point 2 struct Point { 3 int x; 4 int y; 5 }; 6 7 struct Point pt = {320, 200};
2. 粗语言提供了一个编译时一元运算符sizeof,用来计算任一对象的长度,不能用于条件编译语句#if:
1 // 返回对象或者类型占用的存储空间字节数 2 sizeof 对象 or sizeof(类型名)
3. 不要认为结构的长度等于各成员长度之和,不同对象有不同的对齐要求,所有结构中可能会出现未命名的空穴(hole);
2015-10-27更新,待续
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步