翁恺老师讲的超级好,人也超级可爱,b站的弹幕也很可爱。
-
运算符
-
前缀后缀
-
关系运算符(比较)
所有关系运算符的优先级比算术运算符的低,比赋值运算符的高。
e.g. 7>=3+4
int a=r>0
判断是否相等的 == 和 != 的优先级比其他的低,而连续的关系运算是从左到右进行的。
5>3 == 6>4
6>5>4 结果为0
a == b == 6
a == b > 0 -
级联的if-else
if()
str;
else if()
str;
else
str;
(对齐写,格式好看) -
多路分支switch-case
case是开始的地方,break是结束的地方。 -
随机数 srand(time(0))
time是C语言获取当前系统时间的函数,以秒作单位,代表当前时间自Unix标准时间戳(1970年1月1日0点0分0秒,GMT)经过了多少秒。
形式为time_t time(time_t * t);
该函数提供两种返回方式,返回值,和指针参数。
可以根据需要选择。当参数t为空指针(NULL)时,只返回值。
而NULL的定义是(void *) 0, 所以time(0)也就是time(NULL)的另一种写法,表示只通过返回值获取时间值。#include<stdlib.h> #include<math.h> srand(time(0)); int a=rand();
如果得到的数太大,则可以a%100,范围会在0~99。
-
break和continue 从嵌套的循环中跳出
break和continue只能对他所在的那层循环使用。怎么跳出三重循环?(1)接力break:设置标志,满足标志为1时,每个循环都有一个break;(2)goto:goto out; out: 程序跳到out所在位置。 -
-1的n次方
如果在循环中,可以初始sign=1,循环中每次sign=-sign。 -
整数分解
(1)先逆序(c*10+b)*10+a再从右开始输出,但不适合700这种末尾有0的数字,结果只为7,而不是700。
(2)先计算这个整数的位数cnt,构造mask=pow(10,cnt-1),d=a/mask, a=a%mask, mask/=10,循环以mask为判定条件。注意边界值和特殊值。 -
左对齐 %-10d
-
sizeof() 某个类型或变量占据的字节数
sizeof是静态运算符,它的结果在编译时刻就决定了;
不要在sizeof的括号里做运算,这些运算不会做的。int a; sizeof(a+1.0) //sizeof(double)
-
整数类型
●char: 1字节(8比特) -128~127(默认补码)
●short: 2字节 -32768~32767
●int: 取决于编译器(CPU) ,通常的意义是“1个字”(字长)-232-1~232-1-1
●long:取决于编译器(CPU), 通常的意义是“1个字”
●long long: 8字节
字长:
-
补码
补码加原码得到溢出的0。对于-a,其补码就是(1)0-a,实际是2n- a,n是这种类型的位数。
8位:
00000000 -> 0
11111111 ~ 10000000 -> -1 ~ -128
00000001 ~ 01111111 -> 1 ~127
-
unsigned
如果一个字面量常数想要表达自己是unsigned,可以
在后面加u或U,e.g.255U。
用I或L表示long(long)。
*unsigned的初衷并非扩展数能表达的范围,而是为
了做纯二进制运算,主要是为了移位。 -
整数的输入输出
只有两种形式: int或long long
%d: int
%u: unsigned
%ld: long long
%lu: unsigned long long -
8进制和16进制
一个以0开始的数字字面量是8进制
一个以0x开始的数字字面量是16进制
%o用于8进制,%x用于16进制
8进制和16进制只是如何把数字表达为字符串,与内部如何表达数字无关 -
浮点类型
浮点的输入输出
计算机能表示的数是离散的,导致某些数不能精确表示,只能用相近的数表示。printf输出inf表示超过范围的浮点数:+∞、-∞
printf输出nan表示不存在的浮点数
inf: -12.0/0.0 +12.0/0.0(浮点数的设定可以) 12/0(编译不通过)
nan:0.0/0.0带小数点的字面量是double而非float,float需要用f或F后缀来表明身份。
怎么判断两个浮点数是否相等:
f1==f2可能失败
fabs(f1-f2) < 1e-12 (float用1e-8就可以了)选择浮点类型:
如果没有特殊需要,只使用double,
现代CPU能直接对double做硬件运算。 -
算钱钱
不能用浮点数算钱钱,可以看作整数(元角分)来做运算。 -
强制类型转换
(int)3.9001
(int)(1.0/3.0) 强制类型转换的优先级高于四则运算。 -
a<b<c
像4<x< 6这样的式子,不是C能正确计算的式子,因为4< x的
结果是一个逻辑值(0或1)。 -
优先级
-
短路
所以,不要把赋值,包括复合赋值组合进表达式。 -
参数传递
C语言在调用函数时,永远只能 传值(而不是变量) 给函数。
传值:每个函数有自己的变量空间,参数也位于这个独立的空间中,和其他函数没有关系。 -
本地变量(块)
本地变量是定义在块内的,它可以是定义在函数的块内,也可以定义在语句的块内,甚至可以随便拉一 对大括号来定义变量。
程序运行进入这个块之前,其中的变量不存在,离开这个块,其中的变量就消失了。
块外面定义的变量在里面仍然有效。
块里面定义了和外面同名的变量则掩盖了外面的。 -
main函数
return 0 是有意义的,有些人需要这个结果,表示正确执行。 -
数组下标
声明数组时,c99以前是只能是常量,c99是支持下标是变量的,只是vs2015不支持。但数量一旦确定就不再更改。 -
数组初始化
-
数组的大小
sizeof(a)给出的是整个数组所占据的大小,单位是字节。
数组大小:sizeof(a)/sizeof(a[0]) -
数组作为函数参数时
数组作为函数参数时,往往必须再用另一个参数来传入数组的大小。
数组作为函数的参数时: 不能在[ ]中给出数组的大小,不能再利用sizeof来计算数组的元素个数! -
构造素数的多种方法
数组例子:素数 -
二维数组的初始化
32.取地址运算
&运算符取得变量的地址。它的操作数必须是变量。printf("%p\n",&i);
-
指针
指针变量就是记录地址的变量。int *p=&i; //*表示p是一个指针,他指向的是一个int
访问那个地址上的变量 * :
* 是一个单目运算符,用来访问指针的值所表示的地址上的变量,可以做右值也可以做左值。
int k=*p;
*p = k+1;
-
左值
-
指针最常见的错误:定义了指针变量,还没有指向任何变量,就开始使用指针。
int *p;
*p=10; 错误,要先给p赋予值,即它指向的地址。 -
指针和const
指针本身和所指的变量都可能const。
指针是const:
所指是const:
-
const数组
●数组变量是const的指针,所以不能被赋值。
int b[]=a;//错误
int a[] <==> int* const a=… -
p+1
p+1相当于p+sizeof(类型)。
给一个指针加1表示要让指针指向下一个变量。
那么*(p+n) <-> a[n] -
*p++
-
0地址
-
动态内存分配
C99可以用变量做数组定义的大小,C99之前呢?
#include<stdlib.h>
int *a = (int* )malloc(n*sizeof(int));
free(a);
如果内存分配失败会返回0或NULL。
free只能还申请来的空间的首地址。
- 字符串
字符数组:char word[] = {'H','e','l','l',‘o','!'};
字符串:char word[] = {'H','e','l','l', 'o','!','\0'};
字符串:
字符串变量:
char *str ="Hello";
char word[] ="Hello";
char line[10] ="Hello";
\0不是字符串的一部分但是占据数组的一个字节。
C语言的字符串是以字符数组的形态存在的,不能用运算符对字符串做运算,通过数组的方式可以遍历字符串。
唯一特殊的地方是字符串字面量可以用来初始化字符数组。
以及标准库提供了一系列字符串函数。
- 字符串变量
字符串常量:
指针还是数组:
如果要构造一个字符串->数组
如果要处理一个字符串->指针 - char* 是字符串?
- 字符串输入输出
在%和s之间的数字表示最多允许读入的字符的数量,这个数字应该比数组的大小小1。
vs中不支持%7s这种写法,应
scanf_s("%s", a,8);
常见错误:
指针不知道指向哪里了。
- 字符串数组
- 单字符输入输出
putchar:
getchar:
- string.h中的字符串函数
strlen:返回s的字符串长度(不包括结尾的0)。
strcmp:比较两个字符串,返回:
●0: s1==s2
●1: s1>s2
●-1: s1<s2
strcpy:
strcat:
安全问题:strcpy和strcat都可能出现安全问题,如果目的地没有足够的空间?
安全版本:
字符串中找字符:
字符串中找字符串:
- 枚举
声明:enum color c; //enum不能少
小技巧:
这样需要遍历所有的枚举量或者需要建立一个用枚举量做下标的数组的时候就很方便了。
声明枚举量的时候可以指定值。
enum COLOR { RED=1, YELLOW, GREEN= 5};//此时yellow为2
如果有意义上排比的名字,用枚举比const int方便。
枚举比宏(macro) 好,因为枚举有int类型。
- 结构类型
结构可以做赋值运算,s1=s2。
结构作为函数参数:
整个结构可以作为参数的值传入函数;这时候是在函数内新建一个结构变量, 并复制调用者的结构的值;也可以返回一个结构;这与数组完全不同。
输入结构:
没有直接的方式可以一次scanf一个结构。
一个小问题:
结构体是函数参数时,传入函数的是外面那个结构的克隆体,而不是指针。那么主函数怎么得到结构体的值呢?(1)在这个调用的函数中,完全可以创建一个临时的结构变量,然后把这个结构返回给调用者。return s;
(2)(更好)结构指针作为参数。但是函数还是可以写成返回这个指针,即函数是struct date* 类型,这样可以方便进入别的计算。
struct date *p = &myday;
(*p) . month = 12;
p->month = 12;
用->表示指针所指的结构变量中的成员。
-
自定义数据类型typedef
C语言提供了一个叫做typedef的功能来声明一个已有的数据类型的新名字。 -
联合union
存储:
所有的成员共享一个空间;同一时间只有一个成员是有效的;union的大小是其最大的成员。
sizeof (union)=sizeof (每个成员)的最大值
初始化:
对第一个成员做初始化。 -
_func_ ,输出用%s ,表示当前函数的名字。
-
全局变量
初始化:
没有做初始化的全局变量会得到0值;指针会得到NULL值;只能用编译时刻已知的值来初始化全局变量;它们的初始化发生在main函数之前。
被隐藏的全局变量:
如果函数内部存在与全局变量同名的变量,则全局变
量被隐藏。 -
静态本地变量static:能在函数结束后继续保有原值的本地变量
在本地变量定义时加上static修饰符就成为静态本地变量;
当函数离开的时候,静态本地变量会继续存在并保持其值;
静态本地变量的初始化只会在第一次进入这个函数时做,以后进入函数时会保持上次离开时的值。
实质:
静态本地变量实际上是特殊的全局变量,它们位于相同的内存区域。静态本地变量具有全局的生存期,函数内的局部作用域,static在这里的意思是局部作用域(本地可访问)。 -
返回指针的函数
●返回本地变量的地址是危险的,已经不可控。
●返回全局变量或静态本地变量的地址是安全的。
●返回在函数内malloc的内存是安全的,但是容易造成问题。
●最好的做法是返回传入的指针。
tips:
不要使用全局变量来在函数间传递参数和结果;
使用全局变量和静态本地变量的函数是线程不安全的。
- 编译预处理指令
#开头的是编译预处理指令,它们不是C语言的成分,但是C语言程序离不开它们。
#define用来定义一个宏。
预定义的宏:
__LINE__ //两个_
__FILE__
__DATE__
__TIME__
__STDC__
- 带参数的宏
一切都要括号:
●整个值要括号
●参数出现的每个地方都要括号
#define RADTODEG(x) ((x) * 57.29578)
#define cube(x) ((x)*(x)*(x))
为什么?
注意:#define 结束一定不要加分号。
- 头文件
●在使用和定义这个函数的地方都应该#include这个头文件。
●一般的做法就是任何.c都有对应的同名的.h,把所有对外公开的函数的原型和全局变量的声明都放进去。
不对外公开的函数:
- 声明(怎么使用另一个文件的全局变量)
在.h文件中加入变量的声明。
这个有疑惑(只把声明写在头文件)就去收藏夹(小疑惑)。
标准头文件结构:
p112 声明
- 格式化的输入输出
printf:%[flags][width][.prec][hlL]type
printf("%*d\n",6,123);//相当于printf("%6d\n",123);
所以这里的6可以替换为一个变量。
%n:
scanf:%[flag]type