晴神宝典之C /C++快速入门
OJ
补充:runtime error通常原因是数组越界,除零,异常调用,堆栈溢出
尽可能远离TLE
- 选择c++
- 输入输出使用printf和scanf
basis
- 变量名取名:
(1)不能是c语言标识符
(2)首字符必须是字母或下划线,之后的字符必须是字母、下划线或数字
(3)区分大小写 - 变量类型
(1)基本类型分为整型、浮点型、字符型,C++中又有布尔型
c中1表示true,0表示false。在c99中 添加了_Bool类型,用于表示布尔值。实际上_Bool类型也是中整数类型
`
(2)对于浮点型数据而言,推荐主要使用double来存储
(3)在C中,字符常量使用ASCII码统一编码。标准ASCII码的范围是0~127
包含了控制字符或通信专用字符(不可显示)和常用的可现实字符。
eg:
ASCII码 | 对应字符 |
---|---|
48 ~57 | 0 ~9 |
65~90 | A~Z |
97~122 | a~z |
*: 小写字母比大写字母的ASCII码值大32 | |
(4)字符常量必须用单引号标注起来,以区分是作为字符变量还是常量出现。 |
#include<cstdio>
int main()
{
char a = 'b' ;
printf("%c\n",a);//b
printf("%c",'c');//c
}
(5)转义字符:\0代表空字符NULL,他滴ASCII码为0,不是空格哦
(6)不要把字符串常量赋值给字符变量-_-||
(7)布尔型在C中必须加上stdbool.h头文件才可以使用。其中整型常量在赋值给布尔型变量时会自动转换为true(非零)或者false(零),计算机在存储时true和false分别为1和0
#include<cstdio>
int main()
{
bool t = true , f = 0 ;
int a = 0, b = 0 ;
printf("t:%d\nf:%d\na==b:%d",t,f,a==b); // 1 0 1
return 0 ;
}
- 强制类型转换,主要复习隐式转换
- 宏定义语句末尾不加分号。
PS:define还可以定义任何语句或片段
#define 标识符 任何语句或片段
eg:
#include<stdio.h>
#define SUB(a)((a)*2-1)//括号不可省,因为是整体代入,可能会在优先级上发生错误
int main()
{
int test = 2 ;
printf("%d",SUB(test + 1));
return 0 ;
}
//宏定义是把替换的部分原样替换,存在局限性,所有不要使用宏定义做定义常量之外的事,毕竟加各种括号麻烦的一批
//但是哩,常量的写法也是推荐const的方法~ 宏定义有点惨兮兮~
7.运算符
(1)当除数与被除数都是整型时,结果向下取整,即直接舍弃小数部分
(2)鉴于int型的上限为231 - 1 ,则程序中无穷大的数INF可设置为(1<<31)-1,因为位运算符的优先级没有算术运算符高,设置时必须加括号。
但更常用的是230-1,避免和超过int上限
8.输入输出
(1)&取地址运算符
*:数组名称本身就代表了这个数组第一个元素的地址,故数组不需要再加取地址符&
*:scanf的双引号内的东东就是整个输入,而数据换成了他们对应的格式符并把变量的地址按次序写在后面;scanf对其他格式符的输入是以空白符(即空格、Tab)为结束判断标志的,字符数组使用%s读入的时候以空格跟 换行 为读入结束的标志,而scanf的%c格式是能够读入空格和换行的
(2)三种实用的输出格式:
①%md : 使不足m位的int型变量以m位进行右对齐输出,其中高位用空格补齐;若变量本身超过m位,原样输出
②%.mf : 把浮点数保留m位小数输出,四舍六入五成双作为保留规则。
四舍六进;若五前一位数是偶数舍去,奇数进位
适合辣些保留(或精确到)XX位小数,且没有要求四舍五入的//四舍五入用round()函数
- 1 getchar 输入单个字符
putchar 输出单个字符
且getchar可以识别换行符 - typedef 用于给数据类型起个新名字
typedef和宏定义有丢丢相似,但typedef是在编译时被解释
#include<cstdio>
typedef long long LL ;
int main()
{
LL t = 9e11;
printf("%lld",t);
return 0 ;
}
- 加math.h头文件后,可使用各种使用的数学函数
(1)fabs(double x) : 把double变量取绝对值
(2)floor(double x) : 向下取整
ceil(double x) : 向上取整
返回类型都是double型
#include<stdio.h>
#include<cmath>
int main()
{
double d1 = -12.34 ;
double d2 = 12.34 ;
printf("%.2f\n",fabs(d1));//12.34 fabs(double x)取绝对值
printf("%.2f\n",floor(d1)) ; //-13.00 floor(double x)向下取整
printf("%.2f\n",floor(d2)) ;//12.00
printf("%.2f\n",ceil(d1)) ;//-12.00
printf("%.2f\n",ceil(d2)) ;//13.00
printf("%f\n",ceil(d2)) ;//13.000000
return 0 ;
}
(3)pow(double r , double p) :返回rp
#include<stdio.h>
#include<math.h>
int main()
{
double a = 2.0 , b = 3.0 ;
printf("%.1f",pow(a,b)) ; //8.0 pow()函数:求a的b次方
return 0 ;
}
(4)sqrt(double x) : 返回算术平方根
#include<cstdio>
#include<cmath>
int main()
{
double d1 = 4.0 ;
double d2 = 2.0 ;
printf("%.2f",sqrt(d1));//2.00
printf("\n%.2f",sqrt(d2));//1.41
return 0 ;
}
(5)log(double x) : 返回double型变量的以e为底的对数
C中没有对任意求对数的函数,因此需要使用换底公式logeb = loge/logea
(6)sin(double x) : 返回double型变量的正弦值
cos(double x) : ~余弦值
tan(double x) : ~正切值
参数必须是弧度制
#include<cstdio>
#include<cmath>
const double pi = acos(-1.0) ; //因为cos(pi) = -1
int main()
{
double db = 45 ;
printf("%f\n",sin(db * pi/ 180)); //0.707107
printf("%f\n",cos(db * pi/ 180)); //0.707107
printf("%f\n",tan(db * pi/ 180)); //1.000000
return 0 ;
}
(7)asin(double x) : ~反正弦值
acos(double x) : ~反余弦值
atan(double x) : ~反正切值
(8)round(double x) : 把double型变量x四舍五入,返回类型仍为double型
#include<stdio.h>
#include<math.h>
int main()
{
double db1 = 1.52 , db2 = 1.16 ;
printf("%d\n%d",(int)round(db1),(int)round(db2));//2 1 round(double x) 四舍五入取整
return 0 ;
}
- switch语句中,每个case下属的语句可以不用花括号{}括起来,因为case本身默认把两个case之间的内容作为上一个case的内容;
若漏掉break语句之后,会执行符合条件之后的所有语句。
#include<stdio.h>
int main()
{
int a = 1 , b = 3 ;
switch(a + b){
case 2 :
printf("%d" , b);
break;
case 3 :
printf("%d", a + b);
break;
case 4 :
printf("%d", b);
break;
default :
printf("sad legend");
}
return 0 ;
}
- for语句的第一个表达式,在C中不允许定义变量,而C++可以
- break退出循环
continue 退出本次循环,然后进入下一循环 - 数组:把相同数据类型的变量组合而成的数据集合。
(1)每个变量在内存中都有对应的存放位置,而数组就是从某个地址开始连续若干位置形成的元素集合。
(2)数组大小必须是整数常量,不可以是变量
(3)若数组较大时(大约106级别),需要把ta定义在主函数之外,不然会使程序异常退出。
因为函数内部申请的局部变量来自系统栈,允许申请的空间较小;函数外部申请的全局变量来自静态存储区,允许申请的空间较大。
#include<stdio.h>
int a[1000000] ;
int main()
{
for(int i=0 ; i<10e6 ; i++)
{
a[i] = i ;
}
return 0 ;
}
- 冒泡排序
#include<stdio.h>
int main()
{
int a[5] = { 4,2,6,3,1 } ;
for(int i=0 ; i<5 ; i++)
{
printf("%d ",a[i]);
}
printf("\n");
for(int i=1 ; i<5 ; i++)//进行n-1趟交换
{
int flag = 0 ;
for(int j=0 ; j<5-i ; j++)//从a[0]到a[n-1-i]进行比较
{
if(a[j]>a[j+1])//从小到大
{
int t = a[j] ;
a[j] = a[j+1] ;
a[j+1] = t ;
flag = 1 ;
}
}
if(!flag)//如果已经排好序就不用继续
{
break ;
}
}
for(int i=0 ; i<5 ; i++)
{
printf("%d ",a[i]);
}
return 0 ;
}
- memset(数组名,值,sizeof(数组名)) : 对数组中每一个元素赋相同的值
fill()函数也可以哦~
(1)使用memset函数需要添加string.h头文件
(2)建议小白用memset赋0或-1
鉴于memset使用的是按字节赋值,即对每个字节赋同样的值,为了组成int型的四个字节赋 成相同的值。由于0的二进制补码为全0,-1的二进制补码为全1,不容易混淆。
当对数组赋其他数字时,建议使用fill函数
#include<stdio.h>
#include<string.h>
int main()
{
int a[6] = {1,2,3,4,5,6} ;
memset(a,0,sizeof(a)) ;
for(int i=0 ; i<6 ; i++)
{
printf("%d ",a[i]) ;
}
printf("\n");
memset(a,1,sizeof(a)) ;
for(int i=0 ; i<6 ; i++)
{
printf("%d ",a[i]) ;
}
printf("\n");
return 0 ;
}
(3)对于二维数组及多维数组的赋值方法一样色的~
- 字符数组就是char数组,当维度是一维时可以当做“字符串”;当维度是二维时可以当作字符串数组
(1)字符数组也可以通过直接赋值字符串来初始化(仅限于初始化,程序其他位置不允许这种直接赋值整个字符串)
(2)字符数组的输入除了使用scanf外,还可以用getchar或者gets;输出除了使用printf外,还可以用putchar或者puts
(3)%c格式能够识别空格更换行并将其输入;%s通过空格或换行来识别一个字符串的结束
#include<stdio.h>
int main()
{
char str[4][3] ;
for(int i=0 ; i<4 ; i++)
{
for(int j=0 ; j<3 ; j++)
{
str[i][j] = getchar() ;
}
getchar() ;//记得加哦,来吸收换行或空格
}
for(int i=0 ; i<4 ; i++)
{
for(int j=0 ; j<3 ; j++)
{
putchar(str[i][j]);
}
putchar('\n') ;//输完一维换个行哈
}
return 0 ;
}
(4)get识别换行符\n作为输入结束,故scanf整数之后,要先使用getchar接收整数后的换行符,才能使用gets;puts输出一行字符串,将一维数组输出,并紧跟一个换行
(5)字符数组的存放方式:在一维字符数组(或二维数组的第二位)的末尾都有一个空字符\0以表示存放的字符串的结尾。
空字符\0在使用gets或scanf输入字符时会自动添加在输入的字符串之后,并占用一个字符位;puts和printf就是通过识别\0作为字符串的结尾来输出的
- 结束符\0的ASCII码为0,即空字符NULL,占用一个字符位,所以开字符数组时要注意字符数组的长度要比实际存储字符串的长度至少多1。
而int型数组的末尾不需要加\0,只有char型数组需要。
且\0和空格不是同一个东东,空格的ASCII码为32- 如果不是使用scanf函数的%s格式或gets函数输入字符串,一定要在每个字符串后加入\0,否则printf和puts输出字符串会因无法识别字符串而输出一大堆乱码
- string.h头文件中包含了许多用于字符数组的函数,在程序开头添加string.h头文件才能使用。
(1)strlen(字符数组) 返回字符数组中第一个\0前的字符的个数
#include<stdio.h>
#include<string.h>
int main()
{
char str[10] ;
gets(str) ;//输入lalala
//str[0] = '\0';//此时strlen(str)返回值为0
int len = strlen(str) ;//返回字符数组第一个\0前的字符的个数
printf("%d",len) ;//6
return 0 ;
}
(2)strcmp(字符数字1,字符数组2) 返回两个字符串大小的比较结果,比较原则是按字典序
字典序:字符串在字典中的顺序。
eg1:‘a’的字典序小于‘b’;
eg2: “aaa"小于"aac”
eg3:“ab"小于"abc”
(3)strcpy(字符数组1,字符数组2) 把字符数组2赋值给字符数组1(结束符\0也在复制内容之内)
(4)strcat(字符数组1,字符数组2) 把字符数组2接到字符数组1屁股后面
- sscanf 和sprintf
基础eg1:
#include<stdio.h>
int main()
{
int i ;
char str[10] = "123" ;
sscanf(str,"%d",&i) ;//把str以%d的格式输入到i中
printf("%d",i) ; //123
return 0 ;
}
基础eg2:
#include<stdio.h>
int main()
{
int i = 123 ;
char str[10] ;
sprintf(str,"%d",i) ; //把i以%d的格式写入到str中
printf("%s",str) ;//123
return 0 ;
}
eg3:
eg4:
sscanf还支持正则法则哦~
main函数返回0的意义:告诉系统程序正常终止
- 数组作为参数时,参数中数组的第一维不需要填写长度;若是二维数组,第二维需要填写长度
- 数组作为参数时,在函数中对数组元素的修改等同于对数组元素的修改
-
在计算机中,每个变量都会存放在内存中分配的一个空间。根据不同的数据类型,占不同个字节。而每个字节都会有一个地址,即变量存放的位置,而计算机就是通过地址找到某个变量的。
变量的地址一般指它占用的字节中第一个字节的地址,eg:一个int型变量的地址就是它占用的4byte当中第一个字节的地址。
指针是一个unsigned类型的整数,亦即无符号整数
(1)在C中,指针表示内存地址(或指针指向了内存地址),计算机也通过地址找到相应变量
(2)指针变量 :在相应数据类型后加星号*来表示,用来存放指针。
注意,*只会和第一个变量名结合,需要定义多个指针变量,则每人分别加一个小星星
星号*的位置在数据类型之后或之前都可以,编译器不会对此进行区分。
但C中一般把星号放在变量名之前 eg:double *p;C++中一般把星号放在数据类型之后
int a , b ;
int *p1 = &a,*p2 , p3 ; // p1,p2都是整型指针int*,p3是整型int
p2 = &b; //两种赋值方法,注意p1,p2才是变量名,而int*是数据类型
给指针变量赋值的方式一般是把变量的地址取出来,然后赋给对应类型的指针变量
(3)指针变量也可以进行加减法,减法的结果就是两个地址偏移的距离
eg:对int*型的指针 变量p来说,p+1是指int型变量的下一个int型变量地址
对指针变量来说,把其存储的地址的类型称为基类型。
- 在C中,数组名称也作为数组的首地址使用
eg:
int a[5] = {1,2,3};
int *p = a ;
printf("%d", *p ) ;//*p表示a[1]
-
引用:变量的 别名
C++中的引用:给原变量起个别名,而对引用变量的操作就是对原变量的操作!注意是变量0 ,常量不能使用引用
引用方式:在函数的参数类型后面加&即可,&加在int后面或变量名前面都可以!此处的&木得取地址的意思
其中,引用vs指针
-
不存在空引用。引用必须连接到一块合法的内存。(给人起外号当然要先有倒霉蛋当对象啦~)
-
- 引用只能在定义时被初始化一次,之后不可变;指针可变(引用的专一性,多损的引用呐,逮着一个人不放)
-
引用必须在创建时被初始化。指针可以在任何时间被初始化。
-
指针是一个实体,而引用仅是个别名;
-
引用使用时无需解引用(*),指针需要解引用;
-
引用没有 const,指针有 const;const修饰的指针不可变;
-
引用不能为空,指针可以为空;
-
“sizeof 引用”得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本身(所指向的变量或对象的地址)的大小;
-
指针和引用的自增(++)运算意义不一样;
-
从内存分配上看:程序为指针变量分配内存区域,而引用不需要分配内存区域。
- (1)结构体:把若干不同数据类型的变量或数组封装在一起,以存储自定义的数据结构,来存储复合数据
struct Name{
//相应的数据结构或者除自身之外的自定义数据类型(包括自己的话,就成循环定义啦),但是可定义自身数据类型的指针变量
}
(2)访问结构体内元素的两种方法:".“操作和”->"操作
struct Student{
int id ;
char name[20] ;
Student *next ;//指向下一个学生的地址
} stu , *p ;//普通变量stu和指针变量p
访问stu中变量:
stu.id
stu.name
stu.next
访问指针变量p中元素:
(*p).id
(*p).name
(*p).next
or
p->id
p->name
p->next
- cin和cout是C++中的输入和输出函数,需要添加头文件iostream和"usint namespace std;"才能使用。但cin和cout不像C中的scanf和printf函数一样要加指定输入输出格式,也不用取地址运算符&,可以直接进行输入/输出
- 浮点数的比较:
由于不是所有的十进制都可以用二进制表示==》浮点数并非总是精确表示==》会有误差
- 对应一般的OJ(Online Judge)系统来说,一秒能承受的运算次数大概是107~108