第二章 C/C++快速入门
cin和cout消耗的时间比scanf和printf多得多。 不能把字符串常量赋值给字符变量 布尔型变量在C++可以直接使用,但在C中必须天界stdbool.h的头文件
2.1.4 符号常量和const常量
用一个标识符来替代常量,又称为宏定义或宏替换,格式为*#define pi 3.14* 另一种定义常量的方法是使用const,格式为const double pi = 3.14; 这两种写法都是常量,常量一旦确定其值后就无法改变。pi=pi+1这种写法是不行的。 define除了可以定义常量,可以定义任何语句或片段。例如可以定义函数:#define ADD(a,b) ((a)+(b)),这样就可以用ADD(a,b)来代替a+b的功能 必须要加许多括号的原因是宏定义是直接将对应的部分替换,然后才进行编译和运行,宏定义把替换的部分原封不动的替换进去,不会自己加括号什么的。
2.1.5 运算符
1. 算术运算符
若除数是0,会导致程序异常退出; 取模运算符的优先级和除法运算相同。 自增和自减运算中,i++是先使用i再自增,--i是先自减,在使用自减之后的i
2. 关系运算符
3. 逻辑运算符
&& || ! 与或非
4. 条件运算符(唯一的三目运算符)
A?B:C 即,若A成立,则执行B,否则执行C。
5. 位运算符
位运算符的优先级没有算术运算符高,因此时常需要加括号。 eg. 循环左移 (1<<31)-1,这样就是int型所能表示的最大值2^31-1,即0x3fffffff; << >> & | ^ ~分别是,位左移,右移,位相与,位相或,位异或,位取反
2.2 顺序结构
2.2.1 赋值表达式
等号右边可以是常量,或者是结果是常量的一个表达式;赋值运算符可以通过将其他运算符放在前面来实现赋值操作的简化。例如n+=2表示n=n+2,n*=2表示n=nx2。 复合赋值运算可以加快编译速度,提高代码的可读性
2.2.2 使用scanf和printf输入输出
scanf("格式控制",变量地址); 即 scanf("%d\n",&n);,表示输入一个int型的变量,存在n的地方。&是取地址的运算符,即“&变量名”指的是取这个变量名所在的地址; scanf的双引号内的内容其实就是整个输入,只不过把数据换成它们对应的格式符并把变量的地址按次序写在后面。但是如果要输入用空格隔开的两个数字,两个%d之间可以不加空格,因为scanf除了%c外,对其他格式符的输入是以空白符(即空格、Tab)为结束判断标志的。空格的输入可以用%c读入,其他情况会把空格直接跳过;字符数组使用%s读入的时候,以空格和换行为读入结束的标志!!!! printf中如果想要输出‘%’或‘\’,需要在前面再加一个%或\,即%%或\ %md可以使不足m位的int型变量以m位进行右对齐输出,其中高位用空格补齐;如果变量本身超过m位,则保持原样。 %0md就是高位用0补齐,其余与%md一样 %.mf可以让浮点数保留m位小数
2.2.3 使用getchar和putchar输入/输出字符
getchar和putchar分别是用来输入和输出 单个字符,这两个函数都可以识别空格和换行和Tab
2.2.4 注释
2.2.5 typedef
给复杂的数据类型起一个别名; typedef long long LL;//给long long起个别名LL
2.2.6 常用math函数
- fabs(x) //取绝对值
- floor(x)和ceil(x),分别用于double型变量的向下取整和向上取整,返回类型为double型
- pow(double r, double p)返回r^p,r和p都必须是double,返回值是double型
- sqrt(double x),返回x的算术平方根
- log(double x),返回x以自然对数为底的对数。C语言中没有对任意底数求对数的函数,因此必须使用换底公式转换为两个两个以e为底的对数的商
- sin,cos,tan,参数要求是弧度制,即多少多少个pi;asin,acos,atan返回反正弦,反余弦和反正切
- round(x),用来将double型变量四舍五入,返回类型是double
2.3 选择结构
2.3.1 if语句
2.3.2 if语句的嵌套
2.3.3 switch语句
switch(表达式){
case 常量表达式1:
...
break;
case 常量表达式2:
...
break;
...
case 常量表达式n:
...
break;
default:
...
}
switch括号里面的表达式是需要判断的条件表达式,default是当所有的case都不满足时默认进行的操作。 case本身默认把两个case 之间的内容全部作为上一个case的内容,因此不用加大括号。每个case 的最后一个语句都是break,如果删去break,则程序将会从第一个匹配的case开始执行语句,直到其下面的所有语句都执行完毕才会退出s
2.4 循环结构
2.4.1 while语句
2.4.2 do... while语句
do...while语句会先执行省略号中的内容一次,然后才判断条件A是否成立,因此while可能一次也不执行,但do...while至少会执行一次。
2.4.3 for语句
for(表达式A;表达式B;表达式C){...} 其中A是循环开始前赋值语句,B是判断循环是否执行的判断表达式,C是一次循环结束之后执行的内容,即循环变量的改变。 当for语句下面只有一个语块是可以不加大括号。需要注意的是,C语言中不允许在for语句的表达式1里定义变量,例如不能写int i=...),但C++中可以。
2.4.4 break和continue语句
break就是退出循环,而continue是结束循环的当前轮回,然后进入下一个轮回
2.5 数组
2.5.1 一维数组
数组大小必须是整数常量,不可以是变量; 一维数组的初始化,需要给出用逗号隔开的从第一个元素开始的若干个元素的初值,并用大括号括住。后面未被赋初值的元素将会由不同编译器内部实现的不同而被赋以不同的初值,一般情况默认初值为0;
2.5.2 冒泡排序
2.5.3 二维数组
如果数组大小较大(大概10^6级别),则需要将其定义在主函数外面,否则会使程序异常退出,原因是函数内部申请的局部变量来自系统栈,允许申请的空间较小;而函数外部申请的全局变量来自静态存储区,允许申请的空间较大 多维数组,没啥特别的,就多了一维
2.5.4 memset————对数组中每一个元素赋相同的值
memset(数组名,值,sizeof(数组名)); 使用memset需要在程序开头添加string.h头文件 但是memset使用的是按字节赋值,即对每个字节赋相同的值,当赋值0或-1时,组成int型的4个字节就会被赋成相同的值(因为补码是全0或全1)
2.5.5 字符数组
字符数组初始化时可以通过直接赋值字符串来初始化,但仅限于初始化,程序其他位置不允许这样直接赋值整个字符串
2.字符数组的输入输出
(1)scanf、printf
其中%c格式能够识别空格,而%s格式通过空格或换行识别一个字符串的结束
(2)getchar、putchar
用来输入和输出单个字符,所以注意在输入有换行字符的时候用一个空的getchar()吸收掉
(3)gets输入,puts输出
gets用来输入一行字符串,并将其存放于一维数组中,识别中以换行符\n作为输入结束;puts用来输出一行字符串,即将一维数组在界面上输出并紧跟一个换行。 字符数组的末尾都有一个空字符\0,以表示存放的字符串的结尾,占用一个字符位。需要注意的是,空字符的ASCII码为0,而空格字符的ASCII码为32,这两个字符是不一样的。
2.5.6 string.h头文件
(1) strlen()//可以得到字符数组第一个\0前的字符的个数。 (2)strcmp()//返回两个字符串大小的比较结果,比较原则是按字典序 (3)strcpy()//把一个字符串复制给另一个字符串,复制包括结束符\0 (4)strcat(str1,str2),把一个字符串接到另一个字符串后面
2.5.7 sscanf与sprintf
2.6 函数
2.6.1 函数的定义
2.6.2 main函数
2.6.3 以数组作为函数参数
数组作为参数时,参数中数组的第一维不需要填写长度(若是二维数组,第二维需要填写长度) 数组作为参数时,在函数中对数组元素的修改就等同于时对原数组元素的修改(这与普通的局部变量是不同的) 但是,数组可以作为参数,但不允许作为返回类型出现,如果想要返回数组,则只能把想要返回的数组作为参数传入。
2.6.4 函数的嵌套调用
2.6.5 函数的递归调用
指的是一个函数调用函数自身,递归是函数自己调用自己的过程
2.7 指针
2.7.1 什么是指针
计算机中,每个字节都会有一个地址,计算机就是通过地址找到某个变量的,变量的地址一般指它占用的字节中第一个字节的地址。即一个地址指向一个变量,指针就用来指向内存地址。& 是取地址运算符,只要在变量的前面加上&,就表示变量的地址。 指针实际上是一个整数,是一个unsigned类型的整数
2.7.2 指针变量
指针变量用来存放指针 *int *p = &a; 指针变量存放的是地址,而&则是取地址运算符,因此给指针变量赋值的方式一般是把变量的地址取出来,然后赋给对应类型的指针变量。 需要注意的是,int 是指针变量的类型,而后面的p才是变量名,用来存储地址,因此地址&a是赋值给p而不是 p的 。 星号可以取地址内存放的值,即取指针所指向的变量的值。 指针变量也可以进行加减法,减法的结果是两个地址偏移的距离,对一个int型的指针变量p来说,p+1是指p所指的int型变量的下一个int型变量地址。 这个所谓的下一个是跨越了一整个int型,指针变量支持自增和自减操作,指针变量的加减法一般用于数组之中 对指针变量来说,其存储的地址的类型称作“基类型”,基类型必须和指针变量存储的地址类型相同
2.7.3 指针与数组
a[0]的地址为&a[0]。C语言中,数组名称也作为数组的首地址使用 在数组中,a+i和&a[i]是等价的!!!!,即*(a+i)和a[i]是等价的 两个int型的指针相减,等价于在求两个指针之间相差了几个int
2.7.4 使用指针变量作为函数参数
指针类型作为函数参数的类型时,视为把变量的地址传入函数。如果在函数中对这个地址中的元素进行改变,那么原先的数据就会确实的改变。这种传递方式被称为地址传递 而值传递过程中,函数在接收参数时是单向一次性的。只有使用指针变量作为参数,灾祸地址的情况下对元素进行操作,才能真正的修改变量。 在定义指针变量时,若没有初始化,则指针变量中存放的地址是随机的,若该随即地址指向的时系统工作区间,那么就会出现错误。
2.7.5 引用
是一种不使用指针也能达到修改传入的参数的方法。是C++中的语法;引用相当于给原来的变量取了个别名,对引用变量的操作就是对原变量的操作。
void change(int &x){
x = 1;
}
需要注意的是,不管是否引用 ,函数的参数名和实际传入的参数名是可以不同的 要注意区分引用和取地址
2.指针的引用
由于引用时产生变量的别名,因此常量不可使用引用,所以代码中不可以写成swap(&a,&b),而必须用指针变量存放&a和&b,然后把指针变量作为参数传入。
2.8 结构体(struct)的使用
结构体里面能定义除了自己本身(这样会引起循环定义的问题)之外的任何数据类型。虽然不能定义自己本身,但可以定义自身类型的指针变量。
2.8.2 访问结构体内的元素
两种方法:“.”操作和“->”操作,其中->只适用于指针指向它的某个元素
2.8.3 结构体的初始化
构造函数的方法进行初始化。构造函数就是用来初始化结构体的一种函数,直接定义在结构体中。构造函数不需要写返回类型,且函数名与结构体名相同。
struct studentInfo{
int id;
char gender;
studentInfo(int_id,char_gender): id(_id), gender(_gender) {}
};
struct studentInfo{
int id;
char gender;
studentInfo(int_id,char_gender){
//赋值
id = _id;
gender = _gender;
}
};
2.9 补充
2.9.1 cin与cout
是C++中的语法,需要添加头文件“#include”和“using namespace std”才能使用
1.cin
int n;
cin>>n;
或连续的读入
cin>>n>>db>>c //即是读入int型,double型和char型
如果想要读入一整行,则需要使用getline函数
char str[100];
cin.getline(str, 100);
2.cout
cout << n << db << c << str <<endl;
cout <<"haha"<< "\n" <<" "<<endl;
2.9.2 浮点数的比较
浮点数在计算机中的存储不总是精确的,因此引入极小数eps对误差进行修正,只要当a与b相差的误差在eps之内,就判断a==b成立。一般来说eps取1e-8. 圆周率Π可以直接写成常量 acos(-1.0)
2.10 黑盒测试
C语言中使用EOF来表示-1 单点测试和多点测试,在多点测试中,每一次循环都要重置一下变量和数组,否则在下一组数据来临的时候变量和数组的状态就不是初始状态了