forked from shangerxin/BookNotes
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathC Primer Plus=Stephen Prata;Note=eshang.txt
567 lines (531 loc) · 15.2 KB
/
C Primer Plus=Stephen Prata;Note=eshang.txt
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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
C Primer Plus=Stephen Prata;Note=eshang
>ASCII, 美国标准信息交换码
:概述
>起源, Dennis Ritchie make C from Unix
>Obfuscated Code, 含糊代码, C独有的代码竞赛
>可以认为C++是C的一个超级,一般情况下,任何一个C程序都是有效地C++程序
>应用, unix, game, lucas movie, computer language, pc, robot
>目标代码, 可执行文件盒库
*.c->compiler->*.obj ->linker -> *.exe
*.lib
启动代码
>编译器
- unix下的c编译器, cc
- linux下的c编译器, gcc, http://www.gnu.org/software/gcc/gcc.html
- windows
- ibm pc
- macintosh
>标准
- ANSI/ISO C|C89|C90, 1989正式采用, 定义了标准库
- C99标准, 不是为了添加新特性, 而是为了满足新目标
* 国际化
* 整理现有管理以解决明显缺点, 针对64位处理器进行调整
* 针对科学和工程项目进行改进
:C概述
>为什么不内置输入输出, 因为不是所有的情况都需要I/O操作, 简化语言设计
>C99引入C++单行注释风格
>C90, 变量声明只能在块的开始处, 之前不能有任何其他语句
C99, 变量可以再任何位置声明
>变量长度
- C90, 31
- C99, 63, 支持UCN, universal character names
>C关键字列表
- C90/ISO标准
auto, enum, unsigned, break, extern, return, void, case, float,
short, volatile?, char, for, signed, while, const, goto, sizeof,
continue, if, static, default, inline, struct, do, int, switch,
double, long, typedef, else, register, union
- C99标准
restrict?, _Bool, _Complex?, inline?, _Imaginary?
- _Complex, 复数
- _Imaginary, 虚数
:数据和C
>类型
- short int
- long int
- long long int, C99
- unsigned int
- unsigned long int, C99
> 转义字符
- \a, 警报
- \b
- \f, 走纸
- \n
- \r, 回车
- \t
- \v
- \\
- \'
- \"
- \?
- 0oo
- \xhh
> _Bool, 0 false, 1 true, 原则上可以只使用一位存储, C99
> 可移植的类型, inttypes.h, 例如包含, 有的编译器没有实现intypes.h头文件
- uint32_t
- int8_t
- int_fast8_t
以上都不是关键字, 是包含inttypes.h引入的宏
>浮点数
- 上溢, 超出表示范围
- 下溢, 超出表示精度
- NaN
> 复数和虚数类型
- float_Complex
- double_Complex
- long doulbe_Complex
- double_Imaginary
:字符串和格式化输入/输出
>字符串就是char数组
>string.h标准库
>printf函数, 增加格式字符
- C90
- C99, h, hh, j表示intmax_t|uintmax_t
>scanf
:运算符,表达式和语句
>优先级
- ()
- +, -, 一元
- *, /
- +, -
- =
>sizeof, 以直接为单位返回目标的大小, 返回类型为size_t的整数类型
>typedef, 来替代一些数据类型, 起到可移植的目的
- 例如, typedef double real
>%, 操作, C99规定趋零结尾规则, 即取模后符号保持一致. 保证符号与操作数一致
- 11/5 = 2, 11%5 = 1
- 11/-5 = -2, 11%-2 = 1
- 11/-5 = -2, -11%-5 = -1
- -11/5 = -2, -11%5 = -1
>类型转换, 隐式, 强制
>参数 argument, 参量 parameter的C99使用词汇规定
- 实际参数或者产量, 使用参数 argument
- 形式参数使用术语参量, parameter
>函数声明, 与函数定义
:C控制语句, 循环
>for, while, do while
>表达式真伪, 1真, 0假; C将非零数作为真处理
>_Bool值, C99定义, 只有1, 0
:分支与跳转
>if, else, switch,continue, break, case, default, goto
>ctype.h, 字符函数, 接受一个字母作为参数, 如果该字符属于某特定的种类, 则返回非零值
- isalnm()
- isalpha()
- isblank(
- tolower()
- toupper()
>条件运算, expresion? true:false
>continue, break
>goto
:字符输入/输出和输入确认
>输入输出并不是C定义的部分, 是留给实现C的编译器来提供的函数
>getchar(), putchar()
>缓冲区
* 无缓冲, 直接输出, 即输入字符直接显示
* 缓冲, 缓冲区满才输出到目标
- 行缓冲
- 缓冲区
* 具体是缓冲输入还是非缓冲输入取决于编译器的提供
- IBM PC, 提供非缓冲函数库, conio.h头文件支持
- ANSI C中提供setbuf()和setvbuf()提供对缓冲的一些控制
>键盘输入, 由流stdin表示
>输出流, stdout
>输入输出函数就是与标准输入输出流打交道
>文件结尾
- DOS, 使用ctrl+z表示
- 也用文件大小表示文件结尾, 不内嵌在文件中ctrl+z, 输入流函数遇到文件结尾会返回EOF
>命令行下的输入重定向, <, 输出重定向 >
>getchar与scanf, 组合使用要当心
- getchar读取每个输入字符, 包含特殊字符
- scanf, 会跳过空格, 制表符, 换行符
:函数
>递归, recursion
>函数名声
>函数参数
>unix编译
- cc
>linux编译
- gcc
>main函数可以被其本身递归或被其他函数调用
>多个源文件编译
- unix
cc file1.c file2.c
cc file1.c file2.o
- linux
gcc file1.c file2.c
gcc file1.c file2.o
- dos 下生成的文件为obj, 不是o扩展名
:数组和指针
>对数组声明const
const type aryName[num] = {...}
>取得数组的大小
- sizeof aryName
>取得数组元素的大小
- sizeof aryName[index]
>指定初始化项目, C99, designated initializer, 允许选择某些元素进行初始化, 例如对数组最后一个元素初始化
- C90, int aryName[3] = {0, 0, 123};
- C99, int aryName[3] = {[2] = 212};
>指定数组大小
- C90格式
int n = 5;
int m = 8;
float a[5];
float a[2*2+1]
float a[(int)5.1]
- C99格式, 额外支持变长数组, variable-lenght array, 创建后不会改变
float a[n];
float a[m];
函数声明中变长数组可以省略名称, 但是维度需要使用星号
type functionName(type ary[*][*]);
>C中数组是连续按行存储的
>数组名是该数组首元素的地址
type *pArray = &aryName[0] = aryName
>声明数组参量
>复合文字, C99新增, compound literal, 文字是非符号常量, 类似于数组的初始化格式
(type [initial_total_number]){value0, value1, ...};
(int []){0, 1, 2};
int *p = (int[2]){1,2};
可以作为形参传递给顺序复合的函数变量
int sum(int ary[], int n);
int tatal = sum((int []){1, 2, 3, 4}, 6)
这点弥补了C90没有数组常量供函数传递的不足
:字符串和字符串函数
>gets, puts, fgets, fputs
>string.h
>命令行参数, argc, argv
type main(int argc, char *argv[])
:存储类, 连接和内存管理
> auto, extern, static, register, const volatile, restricted
>存储时期, storage duration
>作用域, scope
>连接, linkage
>代码块作用域, block scope
- C90, 代码块作用域的变量必须在代码块开始处声明
- C99, 扩展包括for, while, do while循环或者if语句控制的代码, 即使定义的变量没有被{}起来也, 也作为块作用域变量处理
- 函数原型作用域, 在定义函数原型的时候
type functionName(type var0, type var1);
>函数作用域
>连接
- 外部连接, external linkage, 则变量可以再多个文件中使用, 全局变量
- 内部连接, internal linkage, 可以再一个文件中使用, 全局静态变量
- 空连接, no linkage, 代码块内静态变量
>存储时期
- 静态存储时期, static storage duration
* 具有文件作用域的变量都具有静态存储时期, 无论定义为全局变量或全局静态变量
- 动态存储时期, automatic storage duration
* 局部变量
>存储类
- 自动
- 寄存器, register
- 外部静态连接, 所有函数之外直接定义变量(全局)
- 内部静态连接, 函数之外(全局)使用static
- 空连接, 代码块内使用关键字static
>C99的支持, 有的编译器需要编译时手动开启C99特性, 例如gcc
gcc -std=c99 *.c
>具有外部连接的静态变量, 只有在定义的文件中才能初始化
type global_var;
extern type global_var_define_in_other_file;
>存储类和函数
- static type functionName(parameters...);
使用static的原因就是创建ige为特定模块说私有的函数, 只能在定义文件使用
- type functionName(paramters...);, 外部函数, 可以被其他文件函数调用
>随机函数和静态变量
>分配内存
- malloc(), free()
- calloc(), 在ANSI以前版本返回char指针, 在ANSI返回一个void指针
>类型限定
- C90, 增加const, volatile, 来创建受限类型
* 在指针和参量中使用const
* 对全局对象使用const
* volatile, 告诉编译器该变量除了可被程序改变意外还可能被其他代理改变. 典型地是用于共享内存中
volatile int signal_pulse;
* 如果一个变量只能由外部程序改变, 不能由本身程序改变那么就可声明为
const volatile int input_by_other_program_or_hardware;
- C99, restrict,关键字, 为了增强计算支持, 编译器保证指定的变量只能从指针访问, 是唯一的且初始的方式
int *restrict restar = (int*)malloc(10*sizeof(int));
表示在指定代码区域中没有其他指针指向指定的内存区域, 保证修改的唯一, 便于编译器优化
*例如两个访问内存区域的函数
void *memcpy(void *restrict s1, const void * restrict s2, size_t n);
表示源内存指针s1指向的内存与s2一定不同
void *memmove(void *s1, const void *s2, size_t n);
则没有不能同源的限制
>C99, 旧关键字新位置, 为了编译器优化而改变
- 函数声明
void function(int * contst p0, int *restrict p1);
可以声明为
void function(int a[const], int a[restrict]);
- 对于static变量需要增加数组长度
double function(int a[static 20]);
- 以上都是为了编译器优化代码而增加
:文件输入输出
>fopen(), getc, putc, exit, fclose, fprintf, fscanf, fgets, fputs, rewind, fseek, ftell, fflush, fgetpos, fsetpos, feof, ferror, ungetc, setvbuf, fread, fwrite
>IO级别
- 低级IO, low level io, 使用操作系统提供的基本io服务
- 标准io, statndard high level io, 标准的C函数库包和stdio.h头提供的io
>标准文件指针
- stdin
- stdout
- stderr
>其他标准函数
- intungetc(int c, FILE *fp); 向指定输入流放回一个字符
- fread, fwrite, 是二进制读写
- setvbuf, 替换标准IO使用的缓冲区
:结构和其他数据形式
> struct, union, typedef
- struct struct_name
{
...
}struct_varable_name;
- C99结构的指定初始化项目
struct struct_type varable_name = { .some_sub_variable = value };
可以按照任意顺序指定, 或指定部分变量的值
>C99, 复合文字结构, 来初始化指定类型的结构
(struct name){variable 0, variable 1}
>C99, 结构的伸缩型数据成员, flexible array memeber, 规则
* 必须是最后一个成员
* 至少有一个其他的非伸缩型成员
* 声明方式同数组但括号内为空
* 例如
struct flex
{
int count;
double scores[]; //伸缩型成员
};
* 使用方法是声明flex类型的指针然后分配足够的空间
>union, 能够在同一个内存空间(不同时)存储不同类型的数据
- 定义
union name
{
type0 name0;
type1 name1;
type2 name2;
};
- 使用
union name var0;
var0.name0 = var_type0;
var0.name1 = var_type1;
>枚举enum
enum name
{
element0,
element1,
element2
};
实际枚举就是一个连续值的常量数组
>共享名字空间, namespace, 来识别一个名字的程序部分, C中变量和标记(类型定义)是在不同的内存空间中
- C中
struct ret{double x; double;};
int rect;
是可以定义的
- C++中, 变量和标记(类型定义)是在同一个内存空间中的, 所以在C++中上面的代码是非法的
>typedef, 为某一类型创建别名, 由编译器编译的. 常用语复杂类型定义
>变量声明的修饰符
- *, 表示指针
- [], 表示数组
- (), 表示函数
- 函数指针 type (*pname)(), (type * pname)(), 前一个括号保证了符号*与名字结合, 后一个符号说明类型
- 返回指针的函数 type *pname()
:位操作
>~, &, |, ^, >>, <<, &=, |=, ^=, >>=, <<=
>C99, 位字段, bit field, 例如定义基于位的结构, 是signed int或unsigned int
struct
{
unsigned int bit_var0 :1;
signed int bitv_var1 :1;
unsigned int bit_var2 :8; //占用8bit
}
>位字段与位的位置间的对应是依赖于系统环境的, 例如高位或低位系统
:C预处理与C库
>预处理指令
#define, #include, #ifdef, #else, #endif, #ifndf, #if, #elif, #line, #error, #pragam
>函数
sqrt(), atan(), atan2(), exit(), atexit(), assert(), memecpy(), memmove(), va_start(), va_arg(), va_copy(), va_end()
>编译过程
- 查找换行
- 替换注释
- 处理宏
- 编译器把宏主题当做语言符号, token, 而不是字符型字符串
>预处理中的符号
- #, #可以把语言符号转化为字符串, 称为stringizing
#define PSQR(x) print("The square of "#x"is %d",((x)*(x)))
使用
int y = 5;
PSQR(y);
输出
The square of y is 25.
- ##, 可以将两个语言符号粘合成一个符号
#define XNAME(n) x##n
使用
XNAME(4)
宏展开后会得到
x4
>C99, 可变宏, ...和 __VA_ARGS__
- 宏参数的最后一个参数为省略号, ..., 这时预定义__VA_ARGS__就可以被用在替换部分中, 表明省略号代表什么
#define PR(...) print(__VA_ARGS___)
使用
PR("howday");
PR("weght = %d", 5);
>宏还是函数, 注意
- 宏的名字中不能有空格
- 用括号包住整个宏体
- 大写字母表示宏名
- 在嵌套运算中使用宏可以加快程序运行速度
>文件包含, #include
- #include <head_file>, 搜索系统目录
- #include "head_file", 当前目录
- #include "path_to_head_file", 指定目录
- 标准头文件的信息类型
* 明显常量
* 宏函数
* 函数声明
* 结构模板定义, FILE
* 类型定义, typedef
>其他指令
- #undef
- 条件编译指令
* #ifdef
* #else
* #endif
* #ifndef
* #elif
- 预定义宏
* __DATA__, 进行预处理的日期
* __FILE__, 当前源代码文件名
* __LINE__, 当前源代码文件中的行号
* __STDC__, 设置为1时为遵循C标准
* __STDC_HOSTED__, 本机环境设置为1, 否则为0
* __STDC_VERSION__, 为C99时设置为199901L
* __TIME__, 源文件编译的时间
- #line, 用于重置__LINE__和__FILE__宏报告的行号和文件名
#line change_current_line_number rename_current_file_name
- #pragma, 可以用命令行参数修改编译器的某些设置, 也可用#pragma置于源代码中
#pragma c9x on //例如指定编译器开启C99特性
>内联函数, inline function, 使用说明符inline, 建议编译器尽可能快递调用函数
inline type functionName(parameter)
{...}
正常状态无法预留获得函数地址, 如果获得, 则编译器不会把原函数作为内联函数处理
C对内联函数放松了唯一定义的限制, 推荐在每个文件中定义并使用内敛函数
与C++不同的是, C允许内联函数与外链(extern type functionName(parameters);)同时使用, 在不同的文件中引入不同类型的函数
>库
- C没有官方库, UNIX 的C实现称为事实上的标准, ANSI C在此基础上开发了官方库
- C与C++中void *是不同的, 把void *付给一个其他类型的时候C++需要强制类型转换, 而C不需要
- math.h
- stdlib.h
- assert.h
- string.h
- stdarg.h, 可变参数
* 函数原型中使用省略号
* 定义一个va_list类型的变量
* 宏将该变量初始化为一个参数列表
* 用宏访问这个参数列表
* 用宏完成清理
* 例如
#include <stdarg.h>
...
double sum(int lm, ...)
{
va_list ap;
double tot = 0;
int i;
va_start(ap, lim); //将ap初始化为参数列表
for(i = 0; i < lim; i ++)
tot+= va_arg(ap, double); //访问参数列表的每一项
va_end(ap); //清理
return tot;
}
:高级数据表示
>抽象数据类型, abstract data type, ADT, 把问题的解决方法和数据结合在一起
- 使用头文件定义数据结构与函数原型定义(接口)
- 源文件实现
:参考资料
>运算符
>类型, C99
float, 6位有效数字, 使用32位
double, 10位有效数字, 64位
复数类型float_Complex, double_Complex, long_Complex, float_Imaginary, doulbe_Imaginary
>限定符
auto, extern, static, register
>C99标准增加的ANSI C库
- assert.h
- complex.h
- cypte.h, 字符处理
- errno.h, 错误报告机制
- fenv.h, 浮点数环境
- inttypes.h, 整数类型和格式转换
- locale.h, 本地化
- math.h
* 增加定义 float_t, double_t, 与float, double同样宽, 为了提高计算效率而设置
- setjmp.h, 非本地跳转, 可以不遵循通常的函数调用和函数返回顺序, 有助于处理错误情况
- signal.h, 信号处理, 发送一些系统信息, DOS终端等
- stdarg.h, 可变参数
- stdbool.h, 布尔值支持
- stddef.h, 通用定义
- stdint.h, 整数类型
- stdio.h, 标准IO库
- stdlib.h, 通用工具库
- tgmath.h, 通用型数学, 扩展了通用调用宏, 扩展了math.h, complex.h中的部分函数, 例如正弦余弦等
- time.h, 日期时间
- wchar.h, 多字节字符和宽字符工具
- wctype.h, 宽字符分类与映射工具
>扩展的整数类型
- inttypes.h, C99, 提供16, 32, 64位整数定义
int8_t, int16_5, int32_t, uint8_t...
>扩展的字符串支持, 为某些非国际键盘没有特殊符号而设计
- 需要开启编译器特性来支持
- 三字符序列表示符号
??(, 表示]
??), 表示]等
>可选拼写, iso646.h, 用拼写表示符号
- and, &&
- and_eq, &=
- UCN, 通用字符名
wchar_t value\u00F6 = '\u00f6'; //直接在代码或者赋值中使用UCN字符
>数值计算增强, 更加接近于fortran, IEC 60559要求, 预定义宏在支持的编译器会赋值
__STDC_IEC_559 为1
__STDC_IEC_559_COMPLEX__为1
- fenv.h头文件, 提供了与浮点数环境交互的方式
- 复数支持, complex.h
float Complex
double Complex
long double _Complex
float _Imaginary
double _Imaginary
long double _Imaginary
>C和C++的差别
- 函数原型, C++中是必须的, 函数参数要指明类型
- char常量, C中看做int, 而C++看做char类型
- const修饰, C中全局const具有外部连接, C++中它具有内部连接
C中 const double PI = 3.14 等于 C++中 static double PI = 3.14
- 结构和联合
C/C++中结构定义可以嵌套, C中允许直接引用并定义嵌套的结构元素, C++中不允许需要从父级引用
struct box
{
struct point
{
int x;
int y;
}
struct point lowerright;
};
struct box a; //OK
struct point b; //C is OK, C++ no
box::point c; //C no, C++ OK
- 枚举, C++中更严格, int到枚举要经过强制类型转换, 可以不用enum关键字定义枚举类型
- void的指针, C++中给其他类型需要强制类型转换
- 宽字符支持
wchar_t, C++中内建
- 复数类型, C++提供complex头文件支持, 与Ccomplex.h不兼容
- 内联函数, C中更灵活, C++中要求多个文件中的内联函数一致
- C++中没有的C99特性
* 指定初始化项目
* 复合初始化项目
* 受限指针
* 变长数组
* 伸缩型数组成员
* long long和unsigned long long
* 可移植整数类型inittypes.h, stdint.h
* 通用字符名
* 附加数学库函数
* 通过fenv.h访问浮点数环境
* 预定义表示__func__
* 具有可变参数的宏