变量声明和初始化
int x;//只声明不赋初值
inx y = 1;//声明并赋初值
声明变量时会根据变量类型的位宽创建一块内存空间。如果不赋初值,这 块内存上是之前留下的无用的垃圾数据。初始化赋初值则会对这一块内存存入一个值。
编码时要有赋初值的习惯,而且要避免以下这样的初始化形式:
int dogs, cats = 94;
这样初始化后,dogs并没有初值,只有cats有初值。好的初始化习惯是有初值的在一起,无初值的在一起。
前缀表示进制
0x / 0X前缀标识十六进制
0 前缀表示八进制
进制对应的转换说明
%o / %O 表示八进制格式
%x / %X 表示十六进制格式
%d 表示十六进制格式
加上#号 如%#o %#x表示带上前缀显示
不同的整数类型
short int 存储空间可能比int多 有符号
long int / long 存储空间可能比Int多
long long int / long long 存储空间可能比long多 有符号 至少占64位
unsigned int / unsigned 存储空间同int 无符号
C90标准新加unsigned long / unsigned short C99新加 unsigned long long
上述这么多种整数类型是为了适应不同机器。现在的计算机普遍使用64位处理器,为了存储64位数所以引入long long类型。
目前最常见的是,long long占64位,long占64位,short占16位,int占16或32位(依机器字长而定)。
对于long比int大的机器,使用long会减慢运算速度因此尽量不适用。但对于int long都是32位的机器,应使用long以移植到16位机器时大小不变。
程序代码中使用的整数数字,编译器首先会视为int,如果超过范围编译器视其为unsigned long,然后依次以long long,unsigned long long上升。可以通过后缀显示指明类型。如:
long long x = 1 + 1000000L;//显示指定为long类型
后缀对应的类型:
l / L后缀 long类型
ll / LL后缀 long long类型
ul / UL unsigned long类型
ull / ULL unsigned long long类型
各类型对应的%转换说明:
%u unsigned int
%h short
%d int
%l long int
这几个字母都可以进行组合表示各种类型,在后面接o / x等可以指定进制格式。
char类型
ASCII范围是0-127
字符常量赋值
char broiled = 'T';//正确
char broiled = "T";//错误 双引号表示字符串
char c = 'FATE';//正确,但是c的值是取最低八位的’E’
转义序列和一些特殊字符表示方法
1 直接ASCII表示
char beep = 7;
2 转义字符表示
char nerf = '\n';
一些转义序列:
\a 警报(C90新增)
输出设备控制字符:\b 退格 \f 换页 \n换行 \r回车(移到当前行开始处) \t 水平制表符 \v垂直制表符
\\反斜杠 \’单引号 \”双引号 \?问号 \0oo八进制值 \xhh十六进制值
Beep = ‘\a’ 和 beep = ‘\007’ 或者 ‘\07’ 或者 ‘\7’ 都是等价的,可以省略前缀0,通过八进制表示ASCII
ASCII和转义序列的选择
尽量用转义序列’\f’而不是ASCII码’\014’ 因为好记,移植性好。
用ASCII码时也最好用’\032’而不是032,可以嵌入c语言的字符串而且使用字符编码的意图更明显。
_Bool布尔类型
C99新加入_Bool类型表示布尔值true/false。在内存中只占一位,true存储1,false存储0。
可移植类型:stdint.h和inttypes.h
C99新增stdint.h和inttypes.h以确保c语言类型在各系统中功能相同。
精确宽度整数类型
stdint.h包含一些新类型名,如int32_t,称为精确宽度整数类型,系统会根据宽度将此作为long或者int的别名。
最小宽度类型
int_least8_t表示可容纳8位有符号整数值的类型中宽度最小的类型的别名。
最快最小宽度类型
最快最小宽度类型int_fast8_t,可使计算机达到最快的类型集合。
最大有符号整数类型
最大有符号整数类型intmax_t,作为当前系统最大有符号整数类型的别名。
设定这些类型的目的都是为了增强可移植性
在%后的格式转换说明
如int32_t在有的系统是d有的是ld,在%后的格式转换说明又该怎么填呢?
Inttypes.h头文件定义了PRId32字符串宏,如:
printf("me32 = %" PRId32 "\n", me32);
此处PRId32会自动替换成32位对应的格式转换说明字符串。
浮点数
C标准规定float类型至少表示6位有效数字。
double和float最小取值范围相同,但至少能表示10位有效数字。
一般double类型占64位
还有long double,至少和double精度相同。
浮点型常量表示,如: -1.56E+12 2.87e-3
基本格式:有符号数字(包括小数点),e/E,有符号数表示10的指数
正号可省略,可以没有小数点或指数部分,但不能同时省略两者
可以省略小数部分或整数部分,但不能同时省略两者
系统默认以double存储浮点数常量,进行运算依据变量类型进行截断,运算精度高但速度慢。可以在常量后面加f以显示规定类型,后缀f/F为float,后缀l/L指long double。如:
double a = 1.0f + 2.00;
f/F后缀表示以float型存储该常量,默认以double型存储。
l/L后缀表示以long double型存储
C99新加十六进制浮点数表示,如:
0xa.1fp10
a表示十进制10,.1f表示1/16+15/256,p代替e,是2的幂代替10的幂,但不是所有机器都支持。
打印格式
%f 十进制
%e 指数计数法
%a 十六进制格式指数表示(如果支持
%Lf/Le/La long double类型的不同进制表示。
未在函数原型中显示说明类型的,如printf,编译器自动把float转为double。
浮点数的上溢(overflow)
数字过大超过当前类型能表达的最大范围,这样的行为在过去未定义,现在的C标准规定给赋一个无穷大的特定值,在printf中显示为inf或infinity。
浮点数的下溢(underflow)
如一个浮点数除一个较大数,因有效位数不变,计算中丢失一些有效数字,称为下溢。C语言把损失了类型精全精度的浮点值成为***低于正常的(subnormal)***浮点值。
另一个特殊浮点值NAN。例如给asin()传入大于1的值,此行为是未定义的,返回NAN。printf显示为nan,NAN或其他内容。
浮点数舍入错误
float a,b;
a = 2.0e20 + 1.0;
b = a - 2.0e20;
printf时b的值并不是1.0。因为e20是2后面20个0,加上1.0发生变化的是21位,float类型不能存储那么多位数有效数字。
printf何时将输出发送到屏幕
最初,printf把输出发送到缓冲区。
C标准明确规定了缓冲区何时发送到屏幕:缓冲区满、换行字符、需要输入时。可以用fflush()函数手动刷新缓冲区。
字符串
‘x’和”x”的区别在于”x”实际上是两个字节,字符串后面会添加’\0’空字符作为字符串结束标志。
sizeof strlen的区别
sizeof 以字节为单位给出对象大小,会包括字符串中的’\0’空字符
strlen 给出字符串中字符长度,不包括’\0’空字符
sizeof的圆括号
特定类型一定要 如sizeof(char) sizeof(float)
特定量不用。但最好所有情况都用。
常量
可以用#define 定义常量,实际上是对符号的文本替换
一个编码规范:常量用大写表示。const也是如此。
const关键字
C90标准新增,实际上也是一个变量,但const关键字限定为只读不可写。
const和#define的区别
#define只是进行文本替换,在每一个常量符号处替换成对应的常量,实际上每一处都在内存中新建了一个常量。
而const是在变量初始化时在内存的常量区中创建这个变量,后面每一个常量符号处都对这个内存地址进行访问,而不是在内存中新建。
printf中一些转换说明
%a/A 浮点数、十六进制数和p计数法(C99/C11)
%c 单个字符
%d %i 有符号十进制整数
%e/E 浮点数e计数法
%f 浮点数十进制法
%g/G 根据值不同自动选择%f或%e/E,%e/E格式用于指数小于-4或大于 等于精度时
%o 无符号八进制整数
%p 指针
%s 字符串
%u 无符号十进制整数
%x/X 无符号十六进制整数
%% 打印一个百分号
printf中的修饰符
标记 如%-10d
数字 最小字段宽度 如果不够,系统会使用更宽字段
.数字 精度 可以加前导0 默认其后跟随一个0
h 和整数类型转换一起使用 表示short
hh 和整数类型转换一起使用 表示unsigned char或char
j 和整数一起 表示 intmax_t 或 uintmax_t
l 和整数一起 表示long int 或 unsigned long int
ll 和整数一起 表示long long int 或 unsigned long long int (C99)
l 和浮点转换一起使用 表示long double
t 和整数一起使用 表示ptrdiff_t类型的值。两个指针的差值(C99)
z 和整数一起使用 表示size_t类型的值(C99)
printf的标记
- 待打印项左对齐
+ 若符号值为+,在值前面显示加号,如果是负值前面显示减号
空格 若符号值为正,在值前面加前导空格不加正号;若为负,在前面加减号+标记覆盖空格
# 转换成另一种格式
0 对于数值用前导0代替空格填充字段。对于整数,如果出现-标记或指定精度,忽略该标记
一些转换说明和待打印值不匹配出现的情况
有符号和无符号
有符号整数以补码形式存储。比如short int的二字节空间中,数字0-32767代表其本身,32768-65535表示负数。-336表示为65200,因此以无符号整数形式打印时会显示为65520。
数值长度溢出
比如以%c形式打印short int值,会截断低八位,也就是以256为模处理,以最后余数为结果。如果以%hd打印比short int更大的int整数,也是进行截断,要注意取余后的数值,如果在32767-65536范围内会被当作负数显示。
混淆整数和浮点型
float类型在printf里会被转化为double。以浮点形式显示整数或者以整数形式显示浮点都会出现错误而不是自动进行格式转化和四舍五入等处理。另外,输出参数列表中有其他不匹配的地方,某个参数用对了转换说明也会产生虚假结果。比如:
printf("%ld %ld %ld %ld\n", n1, n2, n3, n4);
其中n1是floatn2是double n3,n4是long,不仅n1n2打印错误,n3n4也是错误的。
这涉及到参数传递的底层原理。在c的参数传递中,被传递的参数从左到右压入栈(stack)中,然后按照转换说明的顺序,按照转换说明对应的宽度依次在栈中从底往上读取。%后面表示的类型位宽和变量位宽不一样,自然在读取的时候指针会错位。
printf的返回值
返回打印字符的个数,如果有输出错误返回一个负数,在检查输出错误时用到。
字符串较长时的分行
用 \ 符号进行断行
编译器会忽略多余的空白。但要注意新行从最最左边开始,如果有缩进会被当作字符串的一部分。
多个printf输出多行
用ANSI C引入的字符串连接
用多个双引号连接字符串的各个部分,每个双引号之间的空格只会合成一个空格。
scanf
格式说明
scanf中%后的格式说明和printf几乎相同
修饰符
* 抑制赋值(后面会讲)
数字 最大字段宽度。输入达到最大字段宽度处,或第一次遇到空白字符时停止
hh 把整数作为signed char或unsigned char 类型读取
ll 把整数作为long long 或 unsigned long long 类型读取(C99)
j 在整型转换说明后面时,表明使用intmax_t或uintmax_t
z 在整型转换说明后面时,表明使用sizeof返回类型
t 在整型转换说明后面时,表面使用表示两个指针差值的类型
scanf实现原理
每次读取一个字符,跳过所有空白字符直至遇到第一个非空白字符开始读取。根据要读取的数据格式(如int),scanf希望发现一个数字字符或者一个符号(+,-),找到第一个这样的字符,保存该字符并读取下一个字符,直至遇到非数字字符,此时认为读到了整数末尾,然后将这个非数字字符放回输入。则下一次读取输入时从这个非数字字符开始读取。
因此,如果第一个非空白字符是’A’,则一直重复将’A’放回并从’A’开始读取,一直无法越过’A’。另外如果带多个转换说明的scanf,C规定在第一个出错停止处停止读取输入。
除了%c,其他转换说明都会跳过代输入值前面所有的空白。比如scanf(“%d%d”, &n, &m);和scanf(“%d %d”, &n, &m);是一样的。但%c是例外,scanf(“%c”, &ch)从输入第1个字符开始读取,而scanf(“ %c”, &ch);则从第一个非空白字符开始读取。
返回值
scanf的返回值是成功读取的项数。如果没有读取任何项,便返回0。检测到文件末尾返回EOF。
空白字符的定义
空白字符包括制表符、空格和换行符
*修饰符
printf中的*
如果想指定字段宽度但不想预先预定好,而是通过程序的变量来指定字段宽度,可以先用*代替字段宽度,然后通过一个参数(变量)告诉函数这个值是多少。
比如:
printf("%*d", width, number);
以width的值为宽度,以此为格式输出number。
scanf中的*
scanf(”%*d %*d %d”, &n);
输入2013 2014 2015,则会跳过前两个数将2015输入到n