2.1基本数据类型
1.整型(%d)
题目要求以内(或者32位整数),就用 int 型;
以内(例如)(或者64位整数),用 long long 型。
2.浮点型(%f)
碰到浮点型的数据都应该用 double 来存储
3.字符型
(1)字符变量和字符常量(%c)
char c = 'e'
c——字符变量;e——字符常量
(2)字符串常量(%s)
char str[10] = "wo ai de ren bu ai wo."
2.1.4 符号常量和 const 常量
#define pi 3.14
const double pi 3.14
(宏定义:直接将对应的部分替换,然后才进行编译和运行)
#include <stdio.h>
#define CAL(x) (x * 2 + 1)
int main(){
int a = 1;
printf("%d\n", CAL(a + 1));
return 0;
}
CAL(a+1) 实际上是 (a+1*2+1) ——把替换的部分原封不动替换进去,所以要加必要的括号
2.1.5 运算符
除法运算符:向下取整
自增运算符:++i 先使用 i 再将 i 加 1 ,i++ 先将 i 加 1 再使用 i
逻辑运算符:&& || !
条件运算符:A ? B : C 如果 A 为真,则返回 B ,如果 A 为假,则返回 C
2.2 顺序结构
2.2.1 赋值表达式(=)
可以将 其他运算符 放在前面来实现赋值操作的简化。例如:n+=2 — n = n + 2
2.2.2 scanf 和 printf
scanf:除了 char 数组整个输入的情况下不加 & 之外,其他变量类型都需要加 &
三种实用的输出方式:
%md:使不足 m 位的 int 型变量以 m 位进行右对齐输出,其中高位用空格补齐;如果变量本身超过 m 位,则保持原样。
%0md:与 "%md" 唯一不同点,用 0 补齐,而不是空格
%.mf:让浮点数保留 m 位小数输出
2.2.3 getchar 和 putchar
getchar:输入单个字符
putchar:输出单个字符
2.2.5 typedef(重命名)
给复杂的数据类型起一个别名:typedef long long LL;
2.2.6 常用 math 函数
1. fabs(double x)——绝对值
2. floor(double x)——向下取整;ceil(double x)——向上取整
3. pow(double r, double p)——
4. sqrt(double x)——
5. log(double x)——
6. sin(double x)、cos(double x)、tan(double x) 参数要求是弧度制(用弧长与半径之比度量对应圆心角角度的方式)
例如:double db1 = sin(pi * 45 / 180); 输出结果:0.707107
7. asin(double x)、acos(double x)、atan(double x) 返回 反正弦值、反余弦值、反正切值
例如:double db1 = asin(1); 输出结果:1.570796
8. round(double x) 四舍五入,返回也是double,需要强制转换取整
2.3 选择结构
2.3.1 if语句
技巧:如果表达式是 "!=0" 则可以省略 "!=0" ;if(n) —— if(n != 0)
如果表达式是 "==0" 则可以省略 "==0";if(!n) —— if(n == 0)
2.4 循环结构
do...while 和 while 的不同:
do...whiile 会先执行循环体一次,然后才去判断循环条件是否为真。
while 只要条件成立,就反复执行
2.4.4 break 和 continue
break:强制退出 switch 语句
continue:临时结束循环的当前轮回,然后进入下一个轮回
2.5 数组
【递推】
根据一些条件,可以不断让后一位的结果由前一位或前若干位计算得来。分为 顺推 和 逆推。
2.5.2 冒泡排序
本质:交换,通过每次交换的方式把当前剩余元素的最大值移动到一端,当剩余元素减少为0时,排序结束
#include <stdio.h>
int main(){
int a[10] = {3, 1, 4, 5, 2};
for(int i = 1; i <= 4; i++){ // 进行 n-1 趟
for(int j = 0; j < 5-i; j++){ // 第i趟从 a[0] 到 a[n-i-1] 都与它们下一个数比较
if(a[j] > a[j+1]){
int temp = a[j];
a[j] = a[j+1];
a[j+1] = temp;
}
}
}
}
2.5.3 二维数组
int a[5][6] = {{3, 1, 2}, {8, 4}, {}, {1, 2, 3, 4, 5}};
for(int i = 0; i < 5; i++){
for(int j = 0; j < 6; j++){
printf("%d ", a[i][j]);
}
printf("\n");
}
输出结果:
3 1 2 0 0 0 8 4 0 0 0 0 ...
notes:
如果数组大小较大(大概级别),则需要定义在函数外面。(原因:函数内部申请的局部变量来自系统栈,允许的空间较小;而函数外部申请的全局变量来自静态存储区,允许申请的空间较大)
2.5.4 memset —— 对数组中的每一个元素赋相同的值
只赋值 0 或 -1,如果赋其他数字(例如1),用 fill 函数
函数格式:memset(数组名,值,sizeof(数组名)) 例如:memset(a, 0, sizeof(a))
2.5.5 字符数组
2. 字符数组的输入输出
(1) scanf, printf
scanf:%c 用来输入单个字符,%s (不用加 & )用来输入一个字符串并存在字符数组。%c 格式能识别空格跟换行,并将其输入。%s 识别空格作为字符串的结尾。
(2) getchar, putchar 分别用来输入和输出 单个字符
(3) gets、puts
gets:用来输入一行字符串(识别换行符 "\n" 作为输入结束),scanf 完一个整数后,如果要使用 gets ,则需要先用getchar接收整数后的换行符
3. 字符数组的存放方式
字符数组的末尾都有一个 空字符 '\0' 以表示存放的字符串的结尾。
notes:在开字符数组时记得长度一定要比实际存储字符串的长度至少多 1 ,int 型数组不需要,只有 char 型数组需要。
如果不是使用 scanf 函数的 %s 格式 或 gets 函数输入字符串(例如使用 getchar ),一定要在输入的每个字符串后加入 '\0' , 否则 printf 和 puts 输出字符串时会因为无法识别字符串末尾而输出一大堆乱码。
2.5.6 string.h 头文件 包含的一些 用于 字符数组的 函数
1. strlen()——得到字符数组第一个 '\0' 前的字符个数,格式:strlen(字符数组)
2. strcmp()——返回两个字符串大小的比较结果,比较原则:字典序(类似于查英文词典,aaaa就在aab之前)。格式:strcmp(字符数组1,字符数组2),返回结果:< 负整数;== 0;> 正整数
3. strcpy()—— 格式:strcpy(字符数组1,字符数组2) 把 2 复制 给 1,包含结束符 '\0'
4. strcat()—— 格式:strcat(字符数组1,字符数组2) 把 2 接到 1 后面
2.5.7 sscanf sprintf
sscanf(str, "%d", &n) 把字符数组 str 中的内容以 "%d" 的格式写到 n 中
sprintf(str, "%d", n) 把 n 以 "%d" 的格式写到 str 字符数组中
2.6 函数
无参函数、有参函数
全局变量、局部变量
值传递
#include <stdio.h>
void change(int x){
x = x + 1;
}
int main(){
int x = 10;
change(x);
printf("%d\n", x);
return 0;
}
因为 change 函数的参数 x 为局部变量,仅在函数内部生效,通过 change(x) 传进去的 x 只是传进去的一个副本,也即 change 函数的参数 x 和 main 函数里的 x 是作用于两个不同函数的不同变量。这种参数传递的方式称为 值传递。
地址传递
指针类型也可以作为函数参数的类型,这时视为把变量的地址传入函数。如果在函数中对这个地址中的元素进行改变,原先的数据就会确实地被改变。
#include <stdio.h>
void swap(int *a, int *b){
int temp = *a;
*a = *b;
*b = temp;
}
int main(){
int m = 3, n = 2;
int *p = &m, *q = &n;
swap(p, q);
printf("%d %d", m, n);
return 0;
}
错误写法2:
void swap(int* a, int* b){
int *temp = a;
a = b;
b = temp;
}
思想:直接把两个地址交换。swap 函数里交换完地址之后 main 函数里地 a 与 b 的地址也被交换。函数参数的传送方式是单向一次性的, main 函数传给 swap 函数的“地址”其实是一个“无符号整型”的数,其本身也跟普通变量一样只是“值传递”,swap 函数对地址本身进行修改并不能对 main 函数里的地址修改,能够使 main 函数里的数据发生变化的只能是 swap 函数中对地址指向的数据进行的修改。对地址本身进行修改其实跟之前对传入的普通变量进行交换的函数是一样的作用,都只是副本,没法对数据进行实质性的影响,即相当于把 int* 看作一个整体,传入的 a 和 b 都只是地址的副本。
2.6.3 以数组作为函数参数
数组作为参数时,在函数中对 数组元素的修改 就等同于是 对原数组元素地修改
(参数中 数组的第一维不需要填写长度,如果是二维数组,那么第二维需要填写长度)
#include <stdio.h>
void change(int a[], int b[][5]){
a[0] = 1;
a[1] = 3;
a[2] = 5;
b[0][0] = 1;
}
int main(){
int a[3] = {0};
int b[5][5] = {0};
change(a, b);
for(int i = 0; i < 3; i++){
printf("%d\n", a[i]);
}
return 0;
}
2.6.4 函数的嵌套调用
2.6.5 函数的递归调用(函数自己调用自己)
#include <stdio.h>
int F(int n){
if(n == 0)
return 1;
else
return F(n-1) * n;
}
int main(){
int n;
scanf("%d", &n);
printf("%d\n", F(n));
return 0;
}
2.7 指针
在计算机中,每个字节(即房间)都会有一个地址(即房间号),而计算机就是通过地址找到某个变量的。
指针,就是变量的地址,在变量前面加上 '&',就表示变量的地址。
2.7.2 指针变量
int* p int* 是指针变量的类型,p 才是指针变量名,( * 是类型的一部分)
给指针变量赋值的方式 就是 把变量的地址取出来,然后赋给对应类型的指针变量。例:int* p = &a;
如何得到这个地址所指的元素:
把 * 视为一把开启房间的钥匙,将其加在 p 的前面,例:
int* p = &a; printf("%d", *p);
指针变量支持加减法,减法的结果就是两个地址偏移的距离。对一个 int* 型的变量来说, p+1 就是 p 所指的 int 型变量的下一个 int 型变量地址。
2.7.3 指针和数组
数组名称可以作为数组的首地址,而 a[0]的首地址为 &a[0],即 a = &a[0],推出 a+i = &a[i]
2.7.4 使用指针变量作为函数参数
把 变量的地址 传入函数,如果在函数中对 这个地址中的元素 进行改变,原先的数据 就会确实地被改变。例如:(地址传递)
#include <stdio.h>
void change(int* p){
*p = 233;
}
int main(){
int n = 1;
int* m = &n;
change(m);
printf("%d", n);
return 0;
}
2.7.5 引用
1. 引用的含义
不使用指针,达到修改传入参数的目的。引用不产生副本,而是给原变量起了个别名。引用变量的操作就是对原变量的操作。只需要在函数的参数类型后面加个 & 就可以。例如:
#include <stdio.h>
void change(int &a){
a = 1;
}
int main(){
int m = 5;
change(m);
printf("%d", m);
return 0;
}
2. 指针的引用
对指针变量本身的修改 无法作用到 原指针变量上。
#include <stdio.h>
void swap(int* &p1, int* &p2){
int* temp = p1;
p1 = p2;
p2 = temp;
}
int main(){
int a = 2, b = 1;
int* p = &a;
int* q = &b;
swap(p, q);
printf("%d %d", *p, *q);
return 0;
}
2.8 结构体的使用
2.9 补充
2.9.1 cin 与 cout
需要添加头文件 #include <iostream> 和 "using namespace std;" 不需要取地址运算符 &
2.9.2 浮点数的比较
2.9.3 复杂度
2.10 黑盒测试
1. 单点测试
2. 多点测试