C - jwyx/ForFun GitHub Wiki
总结进度
P 74
C注意点
main(): 空参数表
escape character: 转义字符
\t, \b, \", \\, \n
printf: %% 表示百分号本身, %c, %s, %o, %x, %d, %f, %ld
%.0f 强制不打印小数点和小数部分
declaration: 说明变量的属性(主要是类型),但并不分配存储单元
definition: 创建或分配存储单元
关键字均为小写
=: 从右到左
&&/||: 从左到右;&&比||高一个优先级;一旦能判断出最终的结构为真或假,则求值立即终止
C Concept
Main:
返回0表示正常终止
返回值为非0表示异常终止或出现结束条件
Variable
format: 类型名 变量表 ;
基本数据类型 built-in:
char: 一个byte
int: 16/32 bits,反映了所用机器中整形的最自然长度
float: 32 bits,至少6位有效数字, 10^-38 ~ 10^38
double
整型限定符: 可以加在基本数据类型前,int可以省略
short: 通常 16 bits
long: 通常 32 bits
类型限定符: 限定char或任何整型
signed
unsigned: >= 0;遵守算数模2^n定律,其中n是该类型占用的位数
Note:
不带限定符的char类型对象是否带符号取决于具体机器,但可打印字符总是正值
浮点数类型:
float, double, long double可以表示相同长度,也可以表示2/3种不同的长度
整型限制:
short/int至少为16 bits
long至少为32 bits
short不能长于 int 类型
int不能长于 long 类型
类型长度定义的符号常量以及编译器有关的属性定义文件:
<limits.h> // <float.h>
storage & visibility & life:
auto & local variable:
函数内可见 / stack / 函数结束前
只在函数被调用时存在,在函数执行完毕退出时消失
如果没有赋值,存放的是无效值
外部变量:
全局可见 / data / 程序结束前
使用外部变量之前,必须知道外部变量的名字
函数内部,声明extern int max
or 外部变量出现max声明出现在函数前
初始化:
外部变量和静态变量:
初始化表达式必须是常量表达式(因为在程序开始执行前进行初始化),且只初始化一次
将默认被初始化为0
自动变量和register变量:
初始化表达式可以包含任意在此之前已经定义的值,包括函数调用
每次进入函数或程序块都将被初始化
默认初值没有定义
初始化表达式的个数比数组元素
少: 对外部变量,静态变量和自动变量来说,没有初始化表达式的元素将被初始化为0
多: 报错
字符数组: 可以用一个字符串来代替花括号括起来并用逗号分隔的初始化表达式序列
const:
任何变量的声明都可以使用const限定符
该限定符指定变量的值不能被修改
对数组而言,const限定符指定的数组所有元素都不能被修改
如果试图修改const限定符的值,其结果取决于具体的实现
常量:
1234: int
l/L结尾 or 整数太大无法用int类型表示: long
u/U结尾: unsigned
浮点数: 包含一个小数点(123.4) 或 一个指数(1e-2) 或 both
无后缀: double
f/F结尾: float
l/L结尾: long double
0: octal
0x/0X: hexadecimal
八/十六进制: 也可用 L 和 U 后缀
符号常量是一个整数,e.g. '0'
转义字符集:
\a 响铃 \\ 反斜杠
\b 回退 \? 问号
\f 换页 \' 单引号
\n 换行 \" 双引号
\r 回车 \ooo 八进制数
\xhh 十六进制数
\t horizontal tab
\v vertical tab
\0 null / 0
常量表达式:
仅仅包含常量的表达式
编译时求值,而不是在运行时
可以出现在常量可以出现的任何位置
字符串常量: e.g. "..."
内部表示为字符串数组,使用'\0'作为结尾
enum: 常量整型值的列表,e.g. enum boolean { NO, YES };
缺省第一个为 0, 依次加 1
不同名字可以有相同的值,但是名字必须互不相同
枚举 提供变量类型检查
Expression
Comment: ignore by complier
/* ... */
允许出现空格,\t或\n之处都可以使用注释
Function:
如何源程序分布在多个文件中,在编译和加载时,需要做更多的工作,但这是操作系统的原因,并不是语言的属性决定的
形参: formal parameter
实参: actual parameter
return: 可以跟任何表达式
函数声明/原型: 参数名可选
值传递: 传递给被调用函数的参数值存放在临时变量中,而不是存放在原来的变量中
format:
return_value function_name (parameters_list) {
// definition and statements
}
各部分均能省略: dummy() {}, 默认返回类型为int
return expr1; // expr1两侧通常加(), 此处的括号是可选的
某些地方返回,某些地方没有返回值,不是非法的,但是不建议!
如果没有函数原型,则函数将在第一次出现的表达式中被隐式声明
如果函数声明中是空参数表,那么对参数不做任何假设,并会关闭所有的参数检查,为了兼容老c语言程序
但是建议:
如果函数带参数,则要声明它们
如果没有参数,则使用void进行声明
函数本身是外部的:
外部变量与函数具有相同的性质: 通过同一个名字对外部变量的所有引用
符号常量:
大写字母拼写
#define 末尾没有分号
输入/输出模型:
字符流
getchar()/putchar(c)
EOF (end of file)
统计行数 等价于 统计\n
Array:
下标从0开始,可以是任何整形表达式
运算符:
整型除法截断结果中的小数部分
%不能应用于 float 或 double
在有负操作数的情况下,整数除法截取的方向以及取模运算结果的符号取决于具体机器的实现
overflow和underflow也如此
++/-- 只能作用于变量
按位运算符 只能作用于 整型操作数,即 只能作用于带符号或无符号char, short, int, long类型
&: 屏蔽某些二进制位
|: 将某些二进制位置1
>>/<<: 右操作数必须是非负数
<<: 左移补0
>>: unsigned,右移补0;signed,左移,根据 机器 补 符号位(算术移位) 或 0(逻辑移位)
~: 二进制反码(1/0互换)
op=:
+,-,*,/,%,<<,>>,&,^,|
和普通形式的区别是: 左值仅计算一次
容易理解,不容易出错,有助于编译器产生高效代码
赋值运算表达式的类型是它的左操作数的类型,其值是赋值操作完成后的值
条件表达式:
expr1 ? expr2 : expr3
如果expr2 和 expr3的类型不同,结果类型遵循转换规则
建议expr1部分用括号包围
运算符优先级: [c-operator-priority](www.slyar.com/blog/c-operator-priority.html)
() [] -> .
! ~ ++ -- - * & (type) sizeof R to L
* / %
+ -
<< >>
< <= > >=
== !=
& (按位)
^ (按位)
| (按位)
&&
||
?: R to L
= += -= *= /= %= &= ^= |= <<= >>= R to L
,
C语言没有指定同一运算符中多个操作数的计算顺序,没有指定函数各参数的求值顺序
最佳求值顺序同机器结构有很大关系,由编译器决定
某些情况下逗号并不是逗号运算符,比如分隔函数参数的逗号,分隔声明中变量的逗号等,这些逗号并不保证表达式从左到右顺序求值
类型转换:
当一个运算符的几个操作数类型不同,需要通过一些规则转化为某种共同的类型
自动转换 是把'比较窄的'操作数转换为'比较宽的'操作数,并且不丢失信息的转换
不允许无意义的表达式
可能导致信息丢失的表达式,编译器可能会给出警告
<ctype.h>定义了一组与字符集无关的测试和转换函数
C语言的定义保证了机器的标准打印字符集中的字符不会是负的,
但是,存储在字符变量中的位模式在某些机器中可能是负的,而在另一些机器上可能是正的。
为了保证程序的可移植性,如果要在char类型的变量中存储非字符数据,最好指定signed或unsigned
真 == 非0
隐式算术类型转换: 附录 A.6
如果二元运算符的两个操作数具有不同的类型,在进行运算之前先要把“较低”的类型提升为“较高”的类型,运算结果为较高的类型
1. 如果没有unsigned类型的操作数,可参照如下
如果一个为long double,另一个转化为long double
如果一个为double,另一个为double
如果一个为float,另一个为float
将char和short类型的操作数转换为int类型
如果一个为long,另一个为long
NOTE: float不会自动转换为double,这一点和最初的定义不同。使用float是为了在使用较大的数组时节省存储空间,
有时也为了节省机器执行时间(double运算特别费时)
2. 如果含有unsigned,转换规则要复杂一些
带符号值和无符号值之间的比较运算是与机器相关的,因为它取决于机器中不同整数类型的大小
控制流:
else的歧义解决: 每个else与最近的前一个没有else配对的if进行匹配
switch: 是否与一些 常量整数值 或 常量表达式 中的某一个值匹配,并执行相应的分支动作
使用break,建议default处也添加break
除了计算需要多个标号的情况,应尽量减少从一个分支直接进入下一个分支执行这种用法,如使用需注释
while (/* expression */) { ... }
for (初始化; 条件; 增长步长) { ... }
都可以是任何表达式
必须有一个循环体,单独的分号代替,称为空语句
三部分均可省略,但必须保留分号
do-while: 在循环体执行后测试终止条件,至少被执行一次
break: 从switch语句或最内层循环中立即跳出
continue:
while/do-while中,意味着立即执行测试部分
for中,意味着立即转移到递增循环变量部分
不用于switch中表示特殊含义
goto: 终止程序在某些深度嵌套的结构中的处理过程
e.g. goto error;
error:
/* error handling codes */
标号的命名同变量命名的形式相同,标号的后面要紧跟一个冒号
标号可以位于对应的goto语句所在函数的任何语句的前面
标号的作用域是整个函数
尽量不要用goto
作用域:
即程序中可以使用该名字的部分
1.函数内自动变量/函数参数:
声明该变量名的函数
2.外部变量或函数:
从声明它的地方开始,到其所在的(待编译的)文件的末尾结束
如果要在外部变量的定义之前使用该变量,或者外部变量的定义与变量的使用不在同一个源文件中,
则必须在相应的声明中强制性地使用关键字extern
如果变量定义在所有函数的外部:
分配存储单元,同时作为该源文件其余部分的声明
一个外部变量只能在某个文件中定义一次,其他文件可以通过extern声明来访问它(定义外部变量的源文件也可以包含extern声明)
外部变量的定义中必须指定数组的长度,但extern声明则不一定要指定数组的长度
外部变量的初始化只能出现在其定义中
使用static声明限定外部变量和函数,可以将其后声明的对象作用域限定在被编译源文件的剩余部分
static的内部变量是一种只能在某个特定函数中使用但一直占据存储空间的变量
register 声明告诉编译器,该变量使用频率很高,将其放在机器的寄存器中;但编译器可以忽略此选项
register 声明只适用于自动变量以及函数的形参, e.g. register int x;
但是无论寄存器变量实际上是否存放在寄存器中,它的地址都不能访问
变量的声明(包括初始化)位置:
函数开始的 { 之后
其他符合语句的 { 之后,在匹配的 } 之前一直存在
每次进入函数块:
自动变量重新初始化
静态变量只在第一次进入时初始化
好的程序风格应该避免隐藏外部作用域中相同的名字
C预处理器:
#define 名字 替换文本
替换文本可以是任意的
替换只对记号进行,对括在引号中的字符串不起作用
缺点: 需要防止副作用,计算次序问题
优点: 将函数定义成宏,可以避免调用函数所需的运行时开销
#undef 名称
#expr 和 ## 的使用??
Style
one statement per line
one space before & after operator
magical number: 以符号常量形式出现