字节换算:
1B=8b
1KB=1024B
1MB=1024KB
1GB=1024MB
1TB=1024GB
PS:B byte;b bit
进制转换
进制:2 B,8 O,10 D,16 H
K进制数到十进制数的转换:按权展开
十进制数到K进制数的转换:短除法
取余数 倒序
K进制小数:乘权去整数
取整数 正序
PS:有时会无法转换,例如 0.1D无法转换为二进制
一位十六进制数对应四位二进制数
1 1 1 1
8 4 2 1 =15
一位八进制数对应三位二进制数
1 1 1
4 2 1 =7
C++程序
#include
#include
using namespace std;
int main() {
printf("第一个C++程序:\n");
printf("Hello World!");
printf("\n第二个C++程序:输出更多\n");
int a = 3;
printf("I have %d dollars.\n", a);
printf("I want to buy:\na book.");
printf("\n第三个C++程序:如何输入\n");
int b,c;
scanf("%d%d",&b,&c);
printf("%d", b + c);
//scanf对于回车、空格 不会赋给字符串,但会赋给字符。(知识点)
//可以在输入之前用getchar()来获取回车的/n来避免将/n赋给字符
printf("\n第四个C++程序:输入字符\n");
char d, e, f;
getchar();
scanf("%c%c%c", &d, &e, &f);
printf("%c%c%c", d, e, f);
return 0;
}
使用形式
数据类型 变量名;
变量命名规则:字母/下划线 + 数字/字母/下划线
无法和关键字重合
数据类型
类型名 | 含义 | 字节数 | 取值范围 |
---|---|---|---|
int | 整型,表示整数 | 4 | -231 ~231 -1 |
long | 长整型,表示整数 | 4 8 | -231 ~231 -1 |
short | 短整型,表示整数 | 2 | -215 ~215 -1 |
unsigned int | 无符号整型,表示非负整数 | 4 | 0 ~232 -1 |
unsigned long | 无符号长整型,表示非负整数 | 4 8 | 0 ~232 -1 |
unsigned short | 无符号短整型,表示非负整数 | 2 | 0 ~216 -1 |
long long | 64位整型,表示整数 | 8 | -263 ~263 -1 |
unsigned long long | 64位整型,表示整数 | 8 | 0 ~264 -1 |
float | 单精度实数型,表示实数 | 4 | 3.4×10-38 ~3.4×1038 |
double | 双精度实数型,表示实数 | 8 | 1.7×10-308 ~1.7×10308 |
char | 字符型,表示字符 | 1 | -128~127 |
unsigned char | 无符号字符型,表示字符 | 1 | 0~255 |
bool | 布尔类型,表示真假 | 1 | true/false |
sizeof(变量名/类型名):可以获取变量/类型占用的字节数
没有初始化的值,其值一般是不确定的。
可以自定义变量
有符号整数(二进制):最高位为符号位 0为正 1位负
正数:绝对值为其除去符号位的其余位
负数:绝对值为其除去符号位的其余位取反再加1
类型转换
double转为int自动舍去小数
char和int可以互相转换:例如’0’ 48;‘A’ 65;‘a’ 97
其中int类型转为char类型时只保留其最低8位,高位部分舍弃。
0x开头的整数为十六进制数;
0开头的整数为八进制数
#include
#include
using namespace std;
int main() {
int a = 01234567;
int b = 04567;
int c = 0x1234567;
int d = 0x4567;
printf("%c %c %c %c", a, b, c, d);
return 0;
}
字符型常量
转义字符 | 含义 | ASCII码 |
---|---|---|
\n | 换行,将输出位置移到下一行开头 | 10 |
\r | 回车,将输出位置移到本行开头 | 13 |
\t | 制表符,输出位置跳到下一个制表位置,制表位置一般是8的倍数加1 | 9 |
\b | 退格,输出位置回退一格字符 | 8 |
\\ | 反斜杠“\” | 92 |
\’ | 单引号“’” | 39 |
\0 | 0字符 | 0 |
\ddd | ddd是个八进制数,代表字符的ASCII码 | ddd(八进制) |
\xhh | hh是个十六进制数,代表字符的ASCII码 | hh(十六进制) |
常量
宏定义:
#define 常量名 常量值
宏替换:使用常量名时用对应的常量值(串)来替换它
#include
#include
using namespace std;
//数值常量 字符串常量 表达式常量
#define MAX_NUM 1000
#define UNIVERSITY_NAME "Peking University"
#define MYINT i=5
int main() {
printf("%d\n%s\n", MAX_NUM, UNIVERSITY_NAME);
int i;
MYINT;
printf("%d", i);
return 0;
}
PS:以#为前缀的代码,则为预处理代码,它会在程序编译前先行处理,所以在编译前会进行宏替换,将常量名转化为对应的串,再行编译。
PS:优点 便于修改,增加程序的可维护性
输入输出控制符
printf和scanf中可以使用以"%"开头的控制符,指明要输入或输出的数据类型及格式。
常用格式控制符 | 作用 |
---|---|
%d | 读入或输出int变量 |
%c | 读入或输出char变量 |
%f | 读入或输出float变量,输出时保留小数点后6位 |
%lf | 读入或输出double变量,输出时保留小数点后6位 |
%x | 以十六进制读入或输出整型变量 |
%lld | 读入或输出long long变量(64位整数) |
%nd(如%4d,%12d) | 以n字符宽度输出整数。宽度不足时用空格填充 |
%0nd(如%04d,%012d) | 以n字符宽度输出整数。宽度不足时用0填充 |
%#nd(如%#4d,%#12o) | 先输出进制标识,随后以n字符宽度输出整数。宽度不足时用空格填充 |
%.nf(如%.4f,%3f) | 输出浮点数,精确到小数点后n位 |
#include
#include
#include
using namespace std;
int main()
{
int a = 0b10; //二进制
int b = 010; //八进制
int c = 10; //十进制
int d = 0x10; //十六进制
printf("10对应的值 二进制 八进制 十进制 十六进制\n");
printf("%15d %6d %6d %6d\n", a, b, c, d);
printf("10对应的进制 八进制 十进制 十六进制\n");
printf("%#15d %#6o %#6d %#6x\n", c, c, c, c); //用#输出不同进制的10的标识
int n=31;
char e='o';
double f=1.2345;
printf("n=%d c=%c f=%lf\n", n, e, f);
printf("%%x n=%x\n", n);
printf("%%10.3lf f=%10.3lf\n", f); //保留小数时是默认四舍五入,而不是double转int时直接剔除小数
printf("%%04d n=%04d\n\n", n);
printf("C++的输入输出:");
int n1, n2;
char c1;
//cin输入
cin >> n1 >> c1 >> n2;
//cout输出
cout << n1 << endl; //endl自带换行功能
cout << c1 << endl;
cout << n2 << endl;
system("pause");
return 0;
}
可以观察到读入char类型时没读入空格,这时需要的话可以用cin.get()来读入空格。
#include
#include
using namespace std;
int main() {
int n;
//cin.get()返回值是int类型,所以用int类型接收,读取时也需要转成char类型
while ((n = cin.get()) != EOF)
{
cout << (char)n;
}
return 0;
}
cin,cout输入输出速度比scanf,printf慢,输入输出数据量大时用后者;不要混用。
常用算术运算符:=,+=,-=,*=,/=,%=
a = b
其中=为赋值运算符 将b的值赋值给a
a += b
等同于a = a+b
其中a + b时以操作数精度高的类型为准
精度:浮点型>整型>字符型
即 double > long long > int > short > char
例如:2 * 0.5 => 1.0
其余类似
补充说明:
/= 取商
%= 取余
5 /= 2 => 2
5 %= 2 => 1
溢出:整数溢出部分舍弃;实数溢出部分,不易预测
例如:
char类型 127 + 1 = ?
unsigned char类型 255 + 1 =?
#include
using namespace std;
int main() {
char c = 127;
unsigned char c1 = 255;
c += 1;
c1 += 1;
printf("%d\n", c);
printf("%d\n", c1);
}
第二个显然:1111 1111B + 0000 0001B =1 0000 0000B 舍去溢出部分为0000 0000B所以值为0
第一个原因如下:
事先说明:char类型二进制由1个符号位 + 7个数值位组成
0111 1111B + 1B = 1000 0000B
因为-0D=1000 0000B 原码转为补码 数值位取反+1 二进制为1 0000 0000B 溢出
补码转为原码规则相同 所以 1000 0000B找不到对应的原码 所以将其视为 -128。
在计算机中,数值一律用补码来进行表示和存储。
关于原码补码转换例子:
1010 1010B(原) = 1101 0110B(补)
1101 0110B(补) = 1010 1010B(原)
可见转换规则相同
1.4部分补充:
127D = 0111 1111B(原) = 0111 1111B(补)
-127D = 1111 1111B(原) = 1000 0001(补)
所以127D在计算机存储的二进制是0111 1111;-127D在计算机中存储的二进制是1000 0001
自增自减运算符:++ –
a++;先赋值后运算
++a;先运算后赋值
eg:
int a,b,c = 3;
a = ++c;
b = c++;
则a = 4,b = 4,c = 5
关系运算符:
相等 ==
不等 !=
大于 >
小于 <
大于等于 >=
小于等于 <=
比较的结果是bool类型成立为true反之为false
其中false等价于0,true等价于非0整型值
逻辑运算符和逻辑表达式
&& 与
a && b 当且仅当a和b都为真(或非0)时,结果为true
|| 或
a || b 当且仅当a和b都为假(或0)时,结果为false
! 非
!a a真值取反(true变false,false变true)
其中&&为短路与,也就是说a的真值为false时,直接返回结果false,不会执行b语句。||也类似。
eg:
int a = 0,b = 2;
int n = a++ && ++b;
cout << n << “,” << a << “,” << b;
输出:0,1,2
因为a++先赋值后运算。所以 n = 0 && ++b; n值直接变为0,&&后面的语句没有执行,所以n值为0,a值为1,b值为2。
强制类型转换符:类型名本身就是一个运算符,用于将操作数转换为指定类型
eg:
int n = 9;
double f;
f = n / 2; // f = 4
f = (double)n / 2; // 将int类型的n强制转换为double。f = 4.5
运算符优先级
由高到低:
++ – !
* / %
+ -
< <= > >= == !=
&&
||
= += -= *= /= &=
需要留心的是:
算术运算符优先级高于关系运算符;
关系运算符优先级高于&&;
&&优先级高于||;
赋值运算符和算术运算符的简写优先级最低
eg:
问题:
5 + 4 || 0 && 1 的值是?
3 *= 3 + 2 的值是?
1 >= 2 && 0 <= 3 的值是?
答案:
1
15
0
if(表达式){
语句组1
}
else if(表达式2){
语句组2
}
…//可以有多个else if
else{
语句组n
}
PS:语句组如果只有一条语句则{}可以省略。
例如:
if(100)
pritnf(100);
就会输出100
PS:0为false 非0为true 常用于整数能否被整除,比如说是n为奇数则为true则表达式可以写成n%2;n为偶数则为true则表达式可写成!(n%2)
PS:else总是和它最近的if配对
switch(表达式){
case 常量1:
语句组1
break;
case 常量2:
语句组2
break;
…//可以有多个case
default: //case都不匹配则执行其中语句组,default也可不写
语句组n+1
}
PS:因为switch不会主动跳出,只会匹配到相应的常量后,顺序执行,所以需要执行完相应语句块后,使用break跳出。(如果某几个case中语句块相同,则可将它们写一块,并将它们之间的break省略)
PS:显然 区间的判断用if 值的判定用switch
for(表达式1;表达式2;表达式3){
语句块
}
执行流程:
while(表达式){
语句组
}
do{
语句组
}while(表达式);
PS:先执行后判断用do while;先判断后执行用while
PS:确定次数的用for;必须先执行的用do while;其余情况用while
break关键字 可跳出当前循环
continue关键字 立即结束本次循环,并回到循环体的开头判断是否要执行下一次循环
(在for语句中会执行表达式3再执行表达式2再执行语句块)
PS:在多重循环嵌套中,它们跳出/结束的都是它们所在的循环
scanf()表达式的返回值为int类型,表示成功读入的变量个数
scanf()返回值为EOF(-1)时说明输入数据已经结束
cin >> n >> m… 成功读入所有变量时为true,否则为false
可用于while中表达式
eg:
#include
#include
using namespace std;
int main() {
int n, m;
while (scanf("%d%d",&n,&m) != EOF)
{
printf("%d\n", n + m);
}
return 0;
}
#include
#include
using namespace std;
int main() {
int n, m;
while (cin >> n >> m)
{
printf("%d\n", n + m);
}
return 0;
}
不断的输入两个整数的和再敲回车,则不停地输出它们的和,直到单独一行输入Ctrl+Z然后回车 程序就会结束
PS:\是拿来转义用的字符(常见的如\n 换行),所以需要输出单纯的\时需要\把\转义成单纯的\。
类型名 数组名[元素个数];
eg:int number[10];
PS:char类型自动转为对应的ASCLL码所以也可用作下标
eg:int number[‘A’] 即是 int number[65]
以T a[N];为例
思路:
自然语言
空间换时间,加快了计算速度
流程:
代码:
#include //筛法求素数
#include
using namespace std;
#define MAX_NUM 10000
char isPrime[MAX_NUM + 1]; //要将MAX_NUM的值也放入判断,又因下标从0开始,所以需要元素个数+1
int main() {
for (int i = 2; i <= MAX_NUM; i++) //开始假设所有数是素数
isPrime[i] = 1;
for (int i = 2; i <= MAX_NUM; i++) { //每次将一个素数的所有倍数标记为非素数
if (isPrime[i]) //只标记素数的倍数
for (int j = i * 2; j <= MAX_NUM; j += i)
isPrime[j] = 0; //将素数的倍数标记为非素数
}
for (int i = 2; i <= MAX_NUM; i++)
if (isPrime[i])
cout << i << endl;
return 0;
}
在定义一个一维数组的同时,就可以给数组中的元素赋初值
规则如下:
演示如下
#include
int main() {
int a1[] = { 0,1,2,3,4,5,6,7,8,9 }; //根据数组内个数自动填充数组容量
int a2[10] = { 0,1,2,3,4,5 }; //自动补0
for (int i = 0; i < 10; i++) {
printf("%d\t", a1[i]);
}
printf("\n");
for (int i = 0; i < 10; i++) {
printf("%d\t", a2[i]);
}
printf("\n");
printf("a1的元素个数=%d", sizeof(a1)/sizeof(a1[0]));
return 0;
}
用数组取代复杂分支结构
其实就是switch里case常量对应的语句组都是单个值的输出或者改变时
就可以转化为数组里面下标和值的对应
例如:输入数字1~7 输出对应的星期几的英语单词,输入其他数,则输出Illegal
#include
#include //使用string必须引用此头文件
#include
using namespace std;
string weekdays = { "Monday" ,"Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday" }; //字符串类型 可存放字符串常量
int main() {
int n;
cin >> n;
if (n > 0 && n < 8)
cout << weekdays[n];
else
cout << "Illegal";
return 0;
}
数组越界不会导致编译错误,但是可能导致程序运行出错。
因为写入了别的变量的内存空间,或者写入指令的内存空间。
#include
#include
using namespace std;
int a[2];
int main() {
a[-2] = 1;
a[3] = 1;
cout << a[-2] << a[3] << a[-99];
return 0;
}
数据类型 变量名[容量][容量];
形似线代中的矩阵
初始化
#include
int main() {
int a[][3] = { 1,2,3,4,5,6,7,8 };
//等同于 int a[3][3] = {{1,2,3},{4,5,6},{7,8,0}}
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++)
printf("%d ", a[i][j]);
printf("\n");
}
}
矩阵行列不会超过8
#include
#include
using namespace std;
#define ROWS 8
#define COLS 8
int a[ROWS][COLS];
int b[ROWS][COLS];
int c[ROWS][COLS];
int main() {
int m, n, p, q;
cin >> m >> n;
for (int i = 0; i < m; i++)
for (int j = 0; j < n; j++)
cin >> a[i][j];
cin >> p >> q;
for (int i = 0; i < p; i++)
for (int j = 0; j < q; j++)
cin >> b[i][j];
for (int i = 0; i < m; i++) {
for (int j = 0; j < q; j++) {
c[i][j] = 0;
for (int k = 0; k < n; k++)
c[i][j] += a[i][k] * b[k][j];
}
}
for (int i = 0; i < m; i++) {
for (int j = 0; j < q; j++)
cout << c[i][j] << " ";
cout << endl;
}
return 0;
}
函数定义
返回值类型 函数名(参数1类型 参数1名称,参数1类型 参数1名称···){
语句组
}
eg:int add(int a,int b){
return a+b;
}
PS:
#include
#include
using namespace std;
static int as[10] = { 0 };
int add(int, int);
void change(int qweqweqw[]);
int main()
{
int a = 3, b = 5;
cout << add(a, b) << endl;
change(as);
for (int i = 0; i < sizeof(as) / sizeof(as[0]); i++) {
cout << as[i] << " ";
}
return 0;
}
//a+b
int add(int a, int b) {
return a + b;
}
//把数组a元素置位1
void change(int a[]) {
for (int i = 0; i < 10; i++) {
a[i] = 1;
}
}
一个函数自己调用自己,就是递归
//阶乘
int Factorial(int n){
if(n<2)
return 1;
else
return n * Factorial(n-1);
}
//斐波那契数列
int Fib(int n){
if( n ==1 || n == 2)
return 1;
else
return Fib(n-1)+Fib(n-2);
}
阶乘:当n=5时
5 * Factorial(4);//Factorial(4)再调用
4 * Factorial(3);//Factorial(3)再调用
3 * Factorial(2);//Factorial(2)再调用
2 * Factorial(1);//Factorial(1)再调用
返回1
最后逐个返回
1
2 * 1
3 * 2 * 1
4 * 3 * 2 * 1
5 * 4 * 3 * 2 * 1
#include<头文件>
库函数:C/C++标准规定的编译器自带的函数
头文件:C++编译器提供许多头文件,如iostream cmath string等;头文件内部包含许多库函数的声明以及其他信息,如iostream头文件中cin,cout的定义
用于对整数类型变量中的某一位(bit),或者若干位进行操作。
C/C++语言提供了六种位运算符来进行位运算操作:
位运算符 | 操作 |
---|---|
& | 按位与(双目) |
| | 按位或(双目) |
^ | 按位异或(双目) |
~ | 按位非(取反)(单目) |
<< | 左移(双目) |
>> | 右移(双目) |
eg:
与操作 都为1则为1 否则为0
21 & 18
即 0001 0101 & 0001 0010 = 0001 0000
所以 21 & 18 = 16
PS:\ ^ ~操作符类似,都是先转为二进制,然后按操作符性质进行位操作,然后转回十进制
按位与 & (有0就为0)
按位或 | (有1就为1)
按位异或 ^ (不同则为1)
按位非 ~
左移运算符 <<
a< 将a各二进位全部左移b位后得到的值。左移时,高位丢弃,低位补0。a的值不因运算而改变。
右移运算符 >>
a>>b;
将a各二进位全部右移b位后得到的值。右移时,移出最右边的位就被丢弃。a的值不因运算而改变。
位运算符优先级
高到低:
~
<< >>
&
^
|
(a>>n)&1
先将a的第n位移到最右边;然后将其 & 1
(a & (1<
先将1左移n位 得到一个除了第n位是1 其余位为0的数 ;然后 & a 得到a的第n位是几 结果就是几;然后右移n位
PS:位运算速度快 能提高运算效率
三种形式:
PS:
用一维char数组存放字符串
#include
int main() {
char a[]="Hello World";
char b[]="Hello W\0orld";
printf("sizeof(a)=%d\n",sizeof(a));
printf("%s\n%s",a,b);
return 0;
}
前置知识:char类型占一个字节
其中字符数组a里面包含的字符总共11个。但是字符数组a的空间大小是12,所以说明了它会自动在字符数组末尾加上结尾字符’\0’。
在字符数组b中,中间加了结尾字符’\0’,从输出看果然就结尾到’\0’部分。
顺带一提,设定字符数组a容量为11的话,会报错。因为他需要12个空间。
读入到空格就会停止读入,解决方式
前置知识:
库函数:
strlen糟糕用法
char s[100]="test";
for(int i=0;i<lenstr(s);i++)
s[i]=s[i]+1
执行lenstr本身需要时间,且时间和字符串长度成正比。所以每次调用strlen函数,这是效率上的浪费。
char s[100]="test";
int len=strlen(s);
for(int i=0;i<len;i++)
s[i]=s[i]+1
所以应在循环之前用常数储存字符串的长度
或者用其s[4]=’\0’=0的特性来遍历
char s[100]="test";
for(int i=0;s[i];i++)
s[i]=s[i]+1
int Strstr(char s1[],char s2[]);
如果s2不是s1的子串,返回-1
如果s2是s1的子串,返回其在s1中第一次出现的位置
空串是任何串的子串,且出现位置为0
int Strstr(char s1[],char s2[]){
if(!s2[0])
return 0;
for(int i=0;s1[i];i++){
int k=i,j=0;
for(;s2[j];j++,k++){
if(s1[k]!=j[j])
break;
}
if(!s2[j]){
return i;
}
}
return -1;
}
基本概念
指针定义:类型名 * 指针变量名;
例如:int * p;
简单来说:p是内存地址;*p是储存在内存地址的内容
#include
int main() {
int a=10;
int * p = &a;
printf("*p=%d\np=%d\n",*p,p);
return 0;
}
因为我用的是网页C语言编译器,所以地址是0。但总的来说,还是可以看出*p和p的不同的。
指针一般用来指向变量。然后传递参数时用指针传地址。
指针大小
#include
using namespace std;
int main()
{
int i = 3;
int *pn = &i;
char *pc;
float *pf;
cout << sizeof(*pn) << sizeof(pc) << sizeof(pf);
return 0;
}
显然:指针本身占据8个字节;指向的变量占据的内存大小还是看其本身的数据类型。
指针的作用:有了指针,就有了自由访问内存空间的手段。
PS:可以通过对变量的地址进行加减操作来访问变量前面或后面的内存区域。
指针的互相赋值:不同类型的指针,如果不经过强制类型转换,不能互相赋值
前置知识:int占4个字节;char占1个字节
int类型指针pn指向c;
*pn中只有第一个字节的值可以确定为0x65,后面三个字节的值不确定。所以执行int n = * pn;时n值不确定;
同理,执行 * pn = 0x12345678时因为不知那三个字节的值能否更改,所以编译不会报错,但是执行时候,可能会报错。
#include
#include
using namespace std;
int main() {
int a[10] = { 0 };
printf("%d", &a[10]-a);
return 0;
}
空指针:地址0不能访问。指向地址0的指针就是空指针。
可以使用NULL关键字对任何类型的指针进行赋值。NULL实际上就是整数0,值为NULL就是空指针。可用作条件表达式。
因为传的是地址,所以交换*p1和*p2地址后,也会影响到主函数中变量m和n的值。
#include
#include
using namespace std;
void f(int a[]);
int main() {
int a[10] = { 1,2,3,4,5,6,7,8,9,0 };
cout << *a << endl;
cout << sizeof(a) << endl;
f(a);
return 0;
}
void f(int a[]) {
cout << *a << endl;
cout << sizeof(a) << endl;
}
简单来说,二维数组是顺序存储在内存地址中的,所以在内存地址中二维数组的表现形式是多个一维数组首尾衔接。
例如char a[2][3]那么a[0][3]后面的地址就是a[1][0]的地址
指向指针的指针
T ** p;
p是指向指针的指针,p指向的地方1应该存放着一个类型为T *的指针。
*p的类型是T*
读入字符串
char name[20];
int n;
scanf("%d%s",&n,name); //scanf读入
cin >> n >> name; //cin读入
void指针
void * p
* p无定义
* 对于p的相关操作如:p++ 等均无定义
内存操作函数
因为是逐字节拷贝,所以转为char类型然后遍历拷贝。
如果两个指针内存范围有重叠,会出错。
定义形式: 类型名(* 指针变量名)(参数类型1,参数类型2,…);
例如:int(*pf)(int,char)
使用方法: 函数指针名(实参表);
#include
void PrintMin(int a, int b) {
if (a < b)
printf("%d", a);
else
printf("%d", b);
}
int main() {
void(*p)(int, int);
int a = 1, b = 2;
p = PrintMin;
p(a, b);
return 0;
}
要用多个不同类型的变量来描述一个事物,则可以用struct来自定义新的数据类型来存放这些变量。
定义方式
struct 结构名
{
类型名 成员变量名;
类型名 成员变量名;
类型名 成员变量名;
…
};
eg:
struct Student
{
unsigned ID;
char szName;
float fGPA;
}
Student是自定义的新的数据类型,自然可以用来定义变量
Student s1,s2;
特性
访问结构变量的成员变量:结构变量名.成员变量名
结构变量的初始化:结构变量名 变量名 = {成员变量1,成员变量2,…};
结构数组:结构变量名 变量名[容量];
指向结构变量的指针:结构名 * 指针变量名;
总的来说上面三者的结构变量的使用方法和普通的数据类型的使用方法是一样的。
指向结构变量的指针
#include
struct StudentEx {
unsigned int ID;
int Stu;
double fGPA;
};
int main() {
StudentEx Stu;
StudentEx * pStu = &Stu;
pStu->ID = 12345;
(*pStu).fGPA = 3.48;
printf("%d\n%f", Stu.ID, Stu.fGPA);
return 0;
}
全局变量和局部变量
静态变量
静态变量应用:strtok的实现
其中strchr(char * s1,char * s2)是用来判断s2是否在s1中,如果出现在里面则返回非空指针则为true。
标识符的作用域
感觉都挺显然…不过概念还是需要了解的
void SelectionSort(int a[],int size){
for(int i=0;i<size-1;i++){
int tmpMin=i;
for(int j=i+1;j<size;j++){
if(a[j]<a[tmpMin])
tmpMin=j;
}
int tmp = a[i];
a[i] = a[tmpMin];
a[tmpMin] = tmp;
}
}
插入排序
void InsertionSort(int a[], int size) {
for (int i = 1; i < size; i++) {
for (int j = 0; j < i; j++) {
if (a[j] > a[i]) {
int t = a[i];
for (int k = i; k > j; k--)
a[k] = a[k - 1];
a[j] = a[i];
break;
}
}
}
}
void BubbleSort(int a[], int size) {
for (int i = size - 1; i > 0; i--) {
for (int j = 0; j < i; j++) {
if (a[j] > a[j + 1]) {
int t = a[j];
a[j] = a[j + 1];
a[j + 1] = t;
}
}
}
}
时间复杂度
常用复杂度
部分程序\算法复杂度
STL概述
排序算法 sort:
排序规则结构的定义方式
struct 结构名
{
bool operator()(ocnst T & a1,const T & a2) const {
//若a1应该在a2前面,则返回true
//否则返回false
}
}
eg:
对数组进行sort
#include
#include
#include
using namespace std;
struct Rule1{//从大到小排序 a1应该在a2之前
bool operator()(const int & a1, const int & a2) const {
return a1 > a2;
}
};
struct Rule2 {//按个位数从小到大排序
bool operator()(const int & a1, const int & a2) const {
return a1 % 10 < a2 % 10;
}
};
void Print(int a[], int size) {
for (int i = 0; i < size; i++)
cout << a[i] << ",";
cout << endl;
}
int main() {
int a[] = { 1,6,4.2,76,8,6,4,3,5,7 };
sort(a, a + sizeof(a) / sizeof(int));//从小到大
cout << "1)"; Print(a, sizeof(a) / sizeof(int));
sort(a, a + sizeof(a) / sizeof(int), Rule1());//从大到小
cout << "2)"; Print(a, sizeof(a) / sizeof(int));
sort(a, a + sizeof(a) / sizeof(int), Rule2());//按个位数从小到大
cout << "3)"; Print(a, sizeof(a) / sizeof(int));
}
对结构体进行sort
#include
#include
#include
using namespace std;
struct Student {
unsigned int ID;
int Grade;
};
struct Rule3 {//按照成绩从大到小排序
bool operator()(const Student & a1, const Student & a2) const {
return a1.Grade > a2.Grade;
}
};
void Print(Student a[], int size) {
for (int i = 0; i < size; i++)
cout << a[i].ID << " " << a[i].Grade << endl;
}
int mainr1112() {
Student a[] = { {2020030901,50},{2020030902,70},{2020030903,10},{2020030904,100} };
sort(a, a + sizeof(a) / sizeof(Student), Rule3());//按成绩从大到小
Print(a, sizeof(a) / sizeof(Student));
}
STL提供在排好序的数组上进行二分查找算法
binary_search
用binary_search进行二分查找
lower_bound
用lower_bound二分查找下界
upper_bound
用lower_bound二分查找上界
有两个变量a和b,在执行了如下代码后: a = 32768; b = a; printf("%d %d\n", a, b); 输出两个数:32768 -32768。 请问a和b分别是以下哪种类型?(提示:b和a的二进制形式是一样的。无符号数用%d输出结果必然是非负的。b输出为负数,说明其符号位为1)
A. 其他三个选项都不对
B. unsigned short,short
C. int,short
D. unsigned int, int
32768=215
int -231 ~ 231 -1
short -215 ~215 -1
unsigned short 0 ~ 216 -1
unsigned int 0 ~ 232 -1
输出时a值没变,所以32768在a的取值范围内;
输出时b值变为-32768,所以32768肯定在b的取值范围之外,且b的左右区间必定是-32768 ~ 32767 也就是b变量为short类型时。原因可以参考2.2里的溢出部分例子讲解。
所以选 B C
写出下面程序的输出结果:
unsigned int x = 0xffffffff;
unsigned int b = x + 2;
int y = x;
printf("%x,%d,%x,%d",y,y,b,b);
A.其他三个选项都不对
B.ffffffff,4294967295,1,1
C.ffffffff,4294967295,100000001,4294967297
D.ffffffff,-1,1,1
0xffffffff = 232
unsigned int b = x+2; //b=232 -1+2-(232 -1)+0-1=1
int y = x; //y=232 -1-(231-1)+(-231)-1= -1
所以后面三个是-1 1 1那为什么前面的是ffffffff呢?因为-1D=1000 0000 0000 0001B(原码)转为补码则是1111 1111 1111 1111B所以在计算机中十六进制的表示就是ffffffff
所以选D
总时间限制: 1000ms 内存限制: 65536kB
描述
雇佣兵的体力最大值为M,初始体力值为0、战斗力为N、拥有X个能量元素。
当雇佣兵的体力值恰好为M时,才可以参加一个为期M天的战斗期,战斗期结束体力值将为0。在同一个战斗期内,雇佣兵每连续战斗n天,战斗力就会上升1点,n为当前战斗期开始时的战斗力。
一个战斗期结束后,雇佣兵需要用若干个能量元素使其体力恢复到最大值M,从而参加下一个战斗期。每个能量元素恢复的体力值不超过当前的战斗力。每个能量元素只能使用一次。
请问:雇佣兵的战斗力最大可以到达多少。
输入
一行包括三个整数M、N、X,相邻两个整数之间用单个空格隔开。M、N、X均为不超过10000的正整数。
输出
输出一个整数,为雇佣兵的最大战斗力。
样例输入
5 2 10
样例输出
6
#include
#include
using namespace std;
int main() {
int m, n, x;
cin >> m >> n >> x;
while (true) {
x = x - m / n;
if (m % n)
x--;
if (x < 0)
break;
n += m / n;
}
cout << n;
return 0;
}
仔细读题就没问题…没认真读,错了几次,就写在这里引以为戒。
其实也可以写的再简短些,利用&&的性质,来把x值的修改,整合成一条语句,然后放进while的表达式里。
#include
#include
using namespace std;
int main() {
int m, n, x;
cin >> m >> n >> x;
while ((x -= m / n + (m%n && 1)) >= 0) {
n += m / n;
}
cout << n;
return 0;
}
描述
给定n行m列的图像各像素点的灰度值,要求用如下方法对其进行模糊化处理:
四周最外侧的像素点灰度值不变;
中间各像素点新灰度值为该像素点及其上下左右相邻四个像素点原灰度值的平均(舍入到最接近的整数)。
输入
第一行包含两个整数n和m,表示图像包含像素点的行数和列数。1 <= n <= 100,1 <= m <= 100。
接下来n行,每行m个整数,表示图像的每个像素点灰度。相邻两个整数之间用单个空格隔开,每个元素均在0~255之间。
输出
n行,每行m个整数,为模糊处理后的图像。相邻两个整数之间用单个空格隔开。
样例输入
4 5
100 0 100 0 50
50 100 200 0 0
50 50 100 100 200
100 100 50 50 100
样例输出
100 0 100 0 50
50 80 100 60 0
50 80 100 90 200
100 100 50 50 100
#include
#include
using namespace std;
static int a[255][255];
static int b[255][255];
int mainR033()
{
int n, m;
cin >> n >> m;
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++)
cin >> a[i][j];
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (i == 0 || i == n - 1 || j == 0 || j == m - 1)
b[i][j] = a[i][j];
else
b[i][j] = (int)(1.0*(a[i - 1][j] + a[i + 1][j] + a[i][j + 1] + a[i][j - 1] + a[i][j]) / 5 + 0.5);
}
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++)
cout << b[i][j] << " ";
cout << endl;
}
return 0;
}
简单是挺简单的,但是四舍五入的方式,我觉得还是有记录的必要。
第一位小数的四舍五入,只需将其值*1.0+0.5随后转为int类型就行。
第二位小数的四舍五入,只需将其值*10.0+0.5随后转为int类型,随后除以10再转为double类型就行
由此可以推导出对小数点后第n位小数四舍五入,只需将其值*pow(10,n-1)*1.0+0.5随后转为int类型,随后除以pow(10,n-1)再转为double类型就行
PS:
因为小数部分≥0.5的话加上0.5就进1,<0.5就不进1,再利用int类型舍去小数的特性就达到了四舍五入的效果。
描述
Pell数列a1, a2, a3, …的定义是这样的,a1 = 1, a2 = 2, … , an = 2 * an − 1 + an - 2 (n > 2)。
给出一个正整数k,要求Pell数列的第k项模上32767是多少。
输入
第1行是测试数据的组数n,后面跟着n行输入。每组测试数据占1行,包括一个正整数k (1 ≤ k < 1000000)。
输出
n行,每行输出对应一个输入。输出应是一个非负整数。
样例输入
2
1
8
样例输出
1
408
#include
#include
using namespace std;
static long long a[1000001];//用来保存递归结果 避免重复调用
int pell(int);
int main()
{
int n, t;
cin >> n;
for (int i = 0; i < n; i++) {
cin >> t;
cout << pell(t) << endl;
}
return 0;
}
int pell(int n) {
if (n<3)
return n;
if (!a[n])
a[n] = (2 * pell(n - 1) + pell(n - 2)) % 100000;
return a[n] % 32767;
}
使用数组保存递归结果,避免了重复调用,所以略作记录。
描述
写出函数中缺失的部分,使得函数返回值为一个整数,该整数的第i位和m的第i位相同,其他位和n相同。
请使用【一行代码】补全bitManipulation1函数使得程序能达到上述的功能
#include
using namespace std;
int bitManipulation1(int n, int m, int i) {
// 在此处补充你的代码
}
int main() {
int n, m, i, t;
cin >> t;
while (t--) {
cin >> n >> m >> i;
cout << bitManipulation1(n, m, i) << endl;
}
return 0;
}
输入
第一行是整数 t,表示测试组数。
每组测试数据包含一行,是三个整数 n, m 和 i (0<=i<=31)
输出
对每组输入数据,每行输出整型变量n变化后的结果
样例输入
1
1 2 1
样例输出
3
提示
二进制的最右边是第0位
return (1 << i & m) | (n & ~(1<<i));
思路:
PS:A | 0 = A;A & 0 = 0;A | 1 = 1;(A=0,1)
同理 如果将两者按位与的话
return (~(1 << i) | m) & (n | (1 << i));
描述
写出函数中缺失的部分,使得函数返回值为一个整数,该整数的第i位是n的第i位取反,其余位和n相同
请使用【一行代码】补全bitManipulation2函数使得程序能达到上述的功能
#include
using namespace std;
int bitManipulation2(int n, int i) {
// 在此处补充你的代码
}
int main() {
int t, n, i;
cin >> t;
while (t--) {
cin >> n >> i;
cout << bitManipulation2(n, i) << endl;
}
return 0;
}
输入
第一行是整数 t,表示测试组数。
每组测试数据包含一行,是两个整数 n 和 i (0<=i<=31)。
输出
输出整型变量n中的第i位取反的结果
样例输入
1
1 0
样例输出
0
提示
二进制的最右边是第0位
return (1 << i) ^ n;
思路:
PS:A ^ 1 = ~A;A ^ 0 = A;(A=0,1)
描述
输入1行句子(不多于200个单词,每个单词长度不超过100),只包含字母、空格和逗号。单词由至少一个连续的字母构成,空格和逗号都是单词间的间隔。
试输出第1个最长的单词和第1个最短单词。
输入
一行句子。
输出
两行输出:
第1行,第一个最长的单词。
第2行,第一个最短的单词。
样例输入
I am studying Programming language C in Peking University
样例输出
Programming
I
提示
如果所有单词长度相同,那么第一个单词既是最长单词也是最短单词。
#include
#include
using namespace std;
int main() {
char c[10000], maxs[1000], mins[100], t[1000];
int max, min;
max = min = -1;
cin.getline(c, 1000000);
int k = -1, len = strlen(c);
for (int i = 0; i < len; i++) {
k++;
t[k] = c[i];
if (c[i] == ' ' || len == i + 1) { //获取空格前面的单词||获取最后一个没有空格的单词
if (max == -1) { //第一次赋值,将第一个单词的长度和字符串赋给max/min和maxs/mins
max = k;
min = k;
strcpy(maxs, t);
strcpy(mins, t);
maxs[k + 1] = 0; //拷贝字符串后追加结尾字符'\0'
mins[k + 1] = 0;
}
else if (k > max) {
max = k;
strcpy(maxs, t);
maxs[k + 1] = 0;
}
else if (k < min) {
min = k;
strcpy(mins, t);
mins[k + 1] = 0;
}
k = -1;//清空 因为会先k++,所以清为-1
strcpy(t, ""); //将字符数组t清为空串
}
}
cout << maxs << endl;
cout << mins << endl;
return 0;
}
难度一般但是strcpy不会把结尾字符也拷贝过去,所以需要自行追加。这点需要注意一下。
描述
程序填空,使得程序按要求输出
#include
using namespace std;
void Memcpy( void * src, void * dest, int size)
{
// 在此处补充你的代码
}
void Print(int * p,int size)
{
for(int i = 0;i < size; ++i)
cout << p[i] << ",";
cout << endl;
}
int main()
{
int a[10];
int n;
cin >> n;
for(int i = 0;i < n; ++i)
cin >> a[i];
int b[10] = {0};
Memcpy(a,b,sizeof(a));
Print(b,n);
int c[10] = {1,2,3,4,5,6,7,8,9,10};
Memcpy(c,c+5,5*sizeof(int)); //将c的前一半拷贝到后一半
Print(c,10);
char s[10] = "123456789";
Memcpy(s+2,s+4,5); //将s[2]开始的5个字符拷贝到s[4]开始的地方
cout << s << endl;
char s1[10] = "123456789";
Memcpy(s1+5,s1+1,4); //将s1[5]开始的4个字符拷贝到s1[1]开始的地方
cout << s1 << endl;
return 0;
}
输入
第一行是整数n (1<=n<=10)
第二行是 n个整数
输出
先原序输出输入数据中的n个整数
然后再输出:
1,2,3,4,5,1,2,3,4,5,
123434567
167896789
样例输入
10
15 25 35 45 55 65 75 85 95 105
样例输出
15,25,35,45,55,65,75,85,95,105,
1,2,3,4,5,1,2,3,4,5,
123434567
167896789
// 在此处补充你的代码
char * pDest = (char *)dest;
char * pSrc = (char *)src;
char t[10000];
for (int i = 0; i < size; i++) {
t[i] = pSrc[i];
}
for (int i = 0; i < size; i++) {
pDest[i] = t[i];
}
解决了拷贝过程中有重叠的情况,只有用数组作为中转就行。
#include
using namespace std;
// 在此处补充你的代码
int Compare1(void * n1,void * n2)
{
int * p1 = (int * )n1;
int * p2 = (int * )n2;
return ((*p1)%10) - ((*p2)%10);
}
int Compare2(void * n1,void * n2)
{
int * p1 = (int * )n1;
int * p2 = (int * )n2;
return *p1 - *p2;
}
#define eps 1e-6
int Compare3(void * n1,void * n2)
{
float * p1 = (float * )n1;
float * p2 = (float * )n2;
if( * p1 - * p2 > eps)
return 1;
else if(* p2 - * p1 > eps)
return -1;
else
return 0;
}
int main()
{
int t;
int a[10];
float d[10];
cin >> t;
while(t--) {
int n;
cin >> n;
for(int i = 0;i < n; ++i)
cin >> a[i];
for(int i = 0;i < n; ++i)
cin >> d[i];
int * p = (int *) MyMax(a,sizeof(int),n,Compare1);
cout << * p << endl;
p = (int *) MyMax(a,sizeof(int),n,Compare2);
cout << * p << endl;
float * pd = (float * )MyMax(d,sizeof(float),n,Compare3);
cout << * pd << endl;
}
return 0;
}
输入
第一行是测试数据组数 t
对每组数据:
第一行是整数n (1<=n<=10)
第2行是 n个整数
第3行是n个浮点数
输出
对每组数据:
先输出n个整数中个位数最大的数(答案保证唯一)
再输出n个整数中最大的数
再输出n个浮点数中最大的数
样例输入
2
5
31 20 100 7 8
30.1 100.2 2.5 9.8 48.4
2
1 2
0.1 0.2
样例输出
8
100
100.2
2
2
0.2
// 在此处补充你的代码
void * MyMax(void * a, int size, int n, int(*f)(void * n1, void * n2)) {
int * max = (int *)a;
for (int i = 1; i < n; i++) {
if (f(max, (int *)a + i) < 0) {
max = (int *)a + i;
}
}
return max;
}
如果要替换成char,那么只需要在传参和赋值的时候让i乘4即可,代码如下:
void * MyMax(void * a, int size, int n, int(*f)(void * n1, void * n2)) {
char * max = (char *)a;
for (int i = 1; i < n; i++) {
if (f(max, (char *)a + i*4) < 0) {
max = (char *)a + i*4;
}
}
return max;
}
描述
填写内存交换函数 SwapMemory,使得程序输出指定结果
#include
using namespace std;
void SwapMemory(void * m1,void * m2, int size)
{
// 在此处补充你的代码
}
void PrintIntArray(int * a,int n)
{
for(int i = 0;i < n; ++i)
cout << a[i] << ",";
cout << endl;
}
int main()
{
int a[5] = {1,2,3,4,5};
int b[5] = {10,20,30,40,50};
SwapMemory(a,b,5 * sizeof(int));
PrintIntArray(a,5);
PrintIntArray(b,5);
char s1[] = "12345";
char s2[] = "abcde";
SwapMemory(s1,s2,5);
cout << s1 << endl;
cout << s2 << endl;
return 0;
}
输入
无
输出
10,20,30,40,50,
1,2,3,4,5,
abcde
12345
样例输入
无
样例输出
10,20,30,40,50,
1,2,3,4,5,
abcde
12345
// 在此处补充你的代码
char t[1];
char * pM1 = (char *)m1;
char * pM2 = (char *)m2;
for (int i = 0; i < size; i++) {
*t = *(pM1 + i);
*(pM1 + i) = *(pM2 + i);
*(pM2 + i) = *t;
}
借用一个指针进行中转,然后进行内存交换就行。
为什么使用数组?
因为数组的话,它会在内存自动开辟对应容量大小的空间来存放数据。所以可以直接拿来中转。使用指针的话,还需要指向某个变量才能获得一个地址然后使用地址里的空间,比较繁琐,所以我使用了数组。
优先级【高到低】:
第一级:圆括号【()】、下标运算符【[]】、分量运算符的指向结构体成员运算符【->】、结构体成员运算符【.】。
第二级:逻辑非运算符【!】、按位取反运算符【~】、自增自减运算符【++ --】、负号运算符【-】、类型转换运算符【(类型)】、指针运算符和取地址运算符【*和&】、长度运算符【sizeof】。
第三级:乘法运算符【*】、除法运算符【/】、取余运算符【%】。
第四级:加法运算符【+】、减法运算符【-】。
第五级:左移动运算符【<<】、右移动运算符【>>】。
第六级:关系运算符【< > <= >= 】。
第七级:等于运算符【==】、不等于运算符【!=】。
第八级:按位与运算符【&】。
第九级:按位异或运算符【^】。
第十级:按位或运算符【|】。
第十一级:逻辑与运算符【&&】。
第十二级:逻辑或运算符【||】。
第十三级:条件运算符【?:】。
第十四级:赋值运算符【= += -= *= /= %= >>= <<.= &= |= ^=】。
第十五级:逗号运算符【,】。 //逗号表达式的值取逗号中最右表达式的值