# C/C++の语法笔记

# 写在前面

这大概将会是我写过的最长的一篇大水文

准备同步目前C语言课的学习进度,自己出一篇教程...

本文会持续更新(大概)

主要是想记录给自己看的,所以本文部分表述可能只有我自己能理解

so...有什么问题在页面最下面的评论区问吧!(或者去主页找我的联系方式)

另外最近开了算法的坑。。。也可以来康康

还要再补充一下...这里面为了省事掺了一部分C++...选择性吸收!

# 虚假的准备工作

一双手

大佬:“有手就行!”

# 真实的准备工作

(推荐)以下软件可任选其一(附下载地址):

Code::Blocks(含编译器):https://dl.flymc.cc/cpp/codeblocks-20.03mingw-setup.exe(opens new window)

Bloodshed Dev-C++:https://dl.flymc.cc/cpp/Dev-Cpp_5.11_TDM-GCC_4.9.2_Setup.exe(opens new window)

Visual Studio 2019(仅下载器):https://dl.flymc.cc/cpp/vs_Community.exe(opens new window)

其他的你觉得好用的随意...

软件安装过程略...有空补上

# 从经典的Hello World!开始

Hello World!在编程中的经典程度如同“九镑十五便士”,是初学者从printf()走向大佬的第一步。

尝试以下程序:

#include <stdio.h>                //#为预处理标志,加载了stdio.h头文件
int main()                        //main()函数为程序入口,C语言程序总是从main()开始执行
{
    printf("hello world!\n");     //printf(),注意加分号(一定要英文分号!!)
    return 0;                     //返回0值,代表程序正常运行(返回-1为异常)
}
1
2
3
4
5
6

输出:

hello world!
1

这里,我们使用了printf()直接输出了双引号内的内容"hello world!"并打出了一个换行,其中用于换行的“\n”是一种“转义字符”,并不会在打印的内容中直接输出。

更多转义字符

这个可以提前记着以后用~

转义字符 意义
\a 响铃(BEL)
\b 退格(BS),将当前位置移到前一列
\f 换页(FF),将当前位置移至下一页开头
\n 换行(LF),将当前位置移至下一行开头
\r 回车(CR),将当前位置移到本行开头
\t 水平制表(HT)(跳到下一个TAB位置)
\v 垂直制表(VT)
\\ 代表一个反斜线字符"\"
' 代表一个单引号(撇号)字符
" 代表一个双引号字符
\0 空字符(NULL)
\ddd 1到3位八进制所代表的任意字符
\xhh 1到2位十六进制所代表的任意字符

注:C++中,printf与cout性能差异不大,没有特别要求的话,为了省事可以用cout代替printf


# 变量声明

C/C++中,要使用一个变量,必须先声明它的类型

int a;         //声明一个整型变量a
long int b;    //声明一个长整型变量b
float c;       //声明一个单精度浮点变量c
double d;      //声明一个双精度浮点变量d
char e;        //声明一个字符型变量e(只能一个字符)
string f;      //声明一个字符串星变量f
1
2
3
4
5
6

这样后面的程序才能够使用这些变量。

小注:

int变量最大值为2147483647

unsigned int 取值范围 0~4294967295

unsigned long 取值范围 0~4294967295

long 取值范围-2147483648~2147483647

long long的最大值:9223372036854775807

long long的最小值:-9223372036854775808

unsigned long long的最大值:18446744073709551615

__int64的最大值:9223372036854775807 __int64的最小值:-9223372036854775808 unsigned __int64的最大值:18446744073709551615

另外,整形变量在取整时会直接抹掉小数点后的数字,不会四舍五入**


# 用scanf()为你的变量录入信息

scanf()函数用于接收输入信息并将其存储到变量中

基本用法:

scanf("%*",&x)   //其中%*代表格式字符中的任意一种,x为变量。&x为取变量的内存地址
1

简单示例,定义变量a,输入a的值并打印出来:

int a;            //定义一个整形变量a
scanf("%d",&a);   //手动输入a的值
printf("&d",a);   //输出a的值
1
2
3

上文所指的%*为任意格式字符,%d为十进制带符号整形的格式字符

常见格式字符整理:

常用格式字符 含义
%s 输出一个字符串
%c 输出一个字符
%d 以十进制输出一个有符号整型
%u 以十进制输出一个无符号整型
%o 以八进制输出一个整数
%x 以十六进制输出一个整数,字母小写
%X 以十六进制输出一个整数,字母大写
%f 以十进制输出一个浮点数
%e 以科学计数法输出一个小写浮点数
%E 以科学计数法输出一个大写浮点数

注1:

限制%f的输出小数点后位数,可用%.xf进行限制,其中“x”代表限制的位数数字

如:%.1f就是限制在小数点后一位输出,%.2f就是限制小数点后两位输出

例子:printf("%.4f",a); //输出变量a,强制小数点后4位输出

注2:

在Visual Studio中,编译器在编译前会进行安全检查,如果直接使用scanf会导致C4996安全报错在目前阶段中,可以直接关闭安全检查,或在代码中加入 #define _CRT_SECURE_NO_WARNINGS,scanf_s没啥必要

注3:

即便是写C++程序,输入时也建议使用scanf而不是cin,因为scanf的性能比cin好了不止一点半点...

# 简单的运算符与math.h

# 简单算术操作

符号 用途
+ 求和
- 求差
* 求积
/ 求商
% 求余

# 一些赋值运算符

这些看看就行,不用记...代替的方式有很多种

运算符 运算
= 赋值
+= 加等于
-= 减等于
*= 乘等于
/= 除等于
%= 模等于

比如现在有a与b两个变量,加等于(+=)这个操作相当于给a赋值:a = a + b,其他的赋值运算符同理

说真的,只要不是对代码大小长度有很严格的要求,这玩意真的可以不记...

注:

还有类似的 i++ 相当于 i = i+1 , i-- 相当于 i = i-1

怎么写真的区别不大,如果真的记不住或者怕用错,干脆就直接 i = i + 1也可

# 关系运算符

主要用于两个变量或常量间的比较,比较的结果以 0(False) 或 1(True)的形式返回

运算符 运算
== 等于
!= 不等于
< 小于
> 大于
<= 小于等于
>= 大于等于

如何理解“比较的结果以 0(False) 或 1(True)的形式返回”呢

如:

printf("%d" , 1 < 2);
cout << endl;    //这个就是个换行,跟\n差不多不用管它...为了输出结果严谨
printf("%d" , 1 > 2);
1
2
3

输出:

1
0
1
2

由于 1 < 2 的判断为真,所以输出为 1(True)

由于 1 > 2 的判断为假,所以输出为 0(False)

# 单目运算符

C/C++中,若遇到自加/自减,如 i = i + 1 或 j = j -1,可用 i++/++i 或 j--/--j 代替,前后二者效果相同

So , i++ 与 ++i 有什么区别呢?

首先,i++不可作为左值,而++i可以作为左值(左值指可以放到赋值符号 = 左边的变量)使用,如:

int i = 1;
++i = 6;
cout << i;
1
2
3

输出的值为 6 ,因为我们在第二行将 i 赋值为了6,++i的自加没有起到作用

而表达式 i++ = 6; 运行会直接报错。

其次,i++会返回原来的值(没有+1),++i会返回+1后的值

测试如下:

int i , j;
i = j = 100;
cout << i++ << endl << ++j;   //这里为了省事代替了printf
//printf代替cout输出:
//printf("%d\n%d" , i++ , ++j);
//代替后效果不变
1
2
3
4
5
6

输出结果:

100
101
1
2

这里,i++直接返回了未+1的值100,而++j返回了101

另外,自增、自减运算优先级高,仅次于括号

# 三目运算符

到现在我也没真正理解它的奥义,你说直接用if它不香么...

不管怎么样,先把它记下来吧...

用法:

表达式a ? 表达式b : 表达式c
1

这个东西它是怎么工作的呢...我直接画了张图:

这样应该更容易理解...

我们将其翻译为if的形式,以后可以回来看看这个,加深理解:

这里,设:

表达式a为 a > 0

表达式b为b = 666

表达式c为 c = 233

#include <bits/stdc++.h>
using namespace std;
int main()
{
    int a; int b = 666; int c = 233;
    scanf("%d" , &a);   //输入一个a的值用于下面的判断
    if (a > 0)          //若表达式a为真
        cout << b;      //那么输出表达式b的值
    else                //否则(表达式a为假)
        cout << c;      //那么输出c的值
    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12

# math.h

用法:

#include <math.h>
1

这个库中包含着多种更高级的数学运算,我直接复制粘贴过来了

1、 三角函数

double sin(double);正弦

double cos(double);余弦

double tan(double);正切

2 、反三角函数

double asin (double); 结果介于[-PI/2,PI/2]

double acos (double); 结果介于[0,PI]

double atan (double); 反正切(主值),结果介于[-PI/2,PI/2]

double atan2 (double,double); 反正切(整圆值),结果介于[-PI,PI]

3 、双曲三角函数

double sinh (double);

double cosh (double);

double tanh (double);

4 、指数与对数

double frexp(double value,int exp);这是一个将value值拆分成小数部分f和(以2为底的)指数部分exp,并返回小数部分f,即f2^exp。其中f取值在0.5~1.0范围或者0。

double ldexp(double x,int exp);这个函数刚好跟上面那个frexp函数功能相反,它的返回值是x*2^exp

double modf(double value,double *iptr);拆分value值,返回它的小数部分,iptr指向整数部分。

double log (double); 以e为底的对数

double log10 (double);以10为底的对数

double pow(double x,double y);计算x的y次幂

float powf(float x,float y); 功能与pow一致,只是输入与输出皆为浮点数

double exp (double);求取自然数e的幂

double sqrt (double);开平方

5 、取整

double ceil (double); 取上整,返回不比x小的最小整数

double floor (double); 取下整,返回不比x大的最大整数,即 高斯函数(opens new window) [x]

6 、绝对值

int abs(int i); 求整型的绝对值

double fabs (double);求实型的绝对值

double cabs(struct complex znum);求复数的绝对值

7 、标准化浮点数

double frexp (double f,int *p); 标准化浮点数,f = x * 2^p,已知f求x,p (x介于[0.5,1])

double ldexp (double x,int p); 与frexp相反,已知x,p求f

8 、取整与取余

double modf (double,double*); 将参数的整数部分通过指针回传,返回小数部分

double fmod (double,double); 返回两参数相除的余数

9 、其他

double hypot(double x,double y);已知直角三角形两个直角边长度,求斜边长度

double ldexp(double x,int exponent);计算x*(2的exponent次幂)

double poly(double x,int degree,double coeffs []);计算多项式

int matherr(struct exception *e);数学错误计算处理程序

参考来源: 《C & C++ Code Capsules》

这个看不懂或者用不明白没关系,现阶段还用不到很多...留个坑以后慢慢填emmm

# 位运算

这是一个可以对一个bit(比特,8bit为一个字节Byte)的位置进行操作的方式。

C语言中提供了六种可用的位运算符

运算符 说明
& 按位与
^ 按位异或
~ 取反
<< 左移
>> 右移

TIP

这上面的 << 和 >> 与前面代码写过的cout << / cin >> 不同。。。C++的输入/输出操作就这样写的而已,不用管

# 按位与&

举例 结果
0 & 0 0
1 & 0 0
0 & 1 0
1 & 1 1

符号两边的数,均为1的位置结果为1,其余为0,如:

0000 0101 & 0000 1100 = 0000 0100

# 按位或|

举例 结果
0 | 0 0
1 | 0 1
0 | 1 1
1 | 1 1

符号两边的数相同位上任意一个或均为1,则结果为1,如:

0000 1110 | 0000 1011 = 0000 1111

# 按位异或^

举例 结果
0 ^ 0 0
0 ^ 1 1
1 ^ 0 1
1 ^ 1 0

符号两边相同位上不同时,结果为1,若相同结果为0,如:

0000 1100 ^ 0000 0101 = 0000 1001

# 取反

这个简单,0位变1,1位变0,就是反过来。。。

# 左移

用法:符号左边位数字,右边为移动的位数,移动后的空位用0补上

如:

1100 1010 << 2 = 0010 1000

# 右移

用法同左移,但空出来的符号位补0或1(负数补1,正数补0),其他空位补0

如:

1001 1100 >> 2 = 1010 0111

# 一个小案例

现在我们知道,将浮点数强行作为整数输出,会无视小数点后的数字,那么,如何做到四舍五入呢?

这个好办,只需要将每个小数+0.5再取整即可四舍五入:

为了细化这个过程以便于理解,示例代码如下:

#include <bits/stdc++.h>
using namespace std;
int main()
{
    float a;
    scanf("%f",&a);
    a = a + 0.5;
    printf("%d",(int)(a));    //(int)(a)为将a强制转换为整型变量输出
    return 0;
} 
1
2
3
4
5
6
7
8
9
10

输入:

1.3
1

输出:

1
1

输入:

3.8
1

输出:

4
1

# 对“值”的理解

先举一个比较简单的例子,首先观察以下代码:

int a , b , c;
a = 6;
b = 6;
c = 6;
1
2
3
4

在这里我们定义了a,b,c三个整型,并将每个变量赋值为6

那么

int a , b;
a = b = 6;
1
2

此时我们的定义了两个个整型变量,但在这里并非使a,b同时赋值为6,也不是将b赋值为6后使a = b,而是将6赋值给了b,再将b的值(等同于数字6)赋值给了a

同理,a = b = 6 整个表达式的值也是6

由此可得,在以上条件下,运行

    printf("%d" , a = b = 6);
1

的输出结果为6

综上所述,表达式 a = b = 6 等同于为6的值

因此,若定义一个整型变量c , c = a = b = 6 等同于 c = 6

# 详解:原码,反码,补码

需要了解:机器数,真值

机器数:

一个数字,使用二进制表示,那么这个二进制数被称为机器数,最高位为符号位,正数为0,负数为1

真值:

带符号位的机器数对应的真正数值称为机器数的真值。

# 原码

原码 = 符号位 + 真值的绝对值,用八位二进制数举例如下:

+1 = 0000 0001

-1 = 1000 0001

由此可得,8位二进制数可表示的十进制范围为

[-127 , 127] (二进制:[1111 1111 , 0111 1111])

# 反码

正数的反码为该数本身(不变),负数的反码为该数的原码除符号位不变外,其余各位取反(除符号位不变,1为0,0为1)

用八位二进制数举例如下:

+1 的原码为 0000 0001,反码为 0000 0001 (不变)

-1 的原码为 1000 0001,反码为 1111 1110(除符号位,其余各位取反)

# 补码

正数的补码为该数本身(不变),负数的补码为该数的反码再加1

用八位二进制数举例如下:

+1 的补码为 0000 0001

-1 的反码为 1111 1110,加1后的 1111 1111为该数补码

# 深入探究

(这里先开一个坑,以后再写)

# 开始判断!(if...else)

swith分支结构我觉得实在是没啥用所以先不写了

if...else基本用法:

if (判断条件)
{
    若满足该条件执行的语句
}
else
{
    不满足该条件执行的语句
}
1
2
3
4
5
6
7
8

多次判断就可以酱:

if (判断条件)
{
    执行语句
}
else if (判断条件)
{
    执行语句
}
else if (判断条件)
{
    执行语句
}
else
{
    执行语句
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

这个好像没什么好写的?(雾)

直接举个例子。。。判断输入a , b两个整数的大小并输出最大的:

#include <bits/stdc++.h>
using namespace std;
int main()
{
    int a , b;
    cin >> a >> b;
    if (a > b)
    {
        cout << a;
    }
    else
    {
        cout << b;
    }
    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 人类的本质...(循环!)

# while循环

基本用法:

while (循环条件)
{
    执行语句
}
1
2
3
4

while循环内的语句会一直执行直到循环条件的值为0(也就是不满足条件)为止。

emmm举个计数的例子:

#include <bits/stdc++.h>
using namespace std;
int main()
{
    int step = 0;
    while (step < 8)
    {
        step++;
    }
    cout << step;
    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12

这样初始值为0的变量step会从第0次到第7次共自增1八次...

输出:

8
1

或者更直观的表达出循环的过程,可以这样写:

#include <bits/stdc++.h>
using namespace std;
int main()
{
    int step = 0;
    while (step < 8)
    {
        step++;
        cout << step << " ";
    }
    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12

输出:

1 2 3 4 5 6 7 8
1

这样每循环一次,step都会+1并打印

# do...while循环

基本用法:

do
{
    执行语句
}
while (循环条件); //注意这里的分号...
1
2
3
4
5

跟while的区别就在于,do...while会先执行后判断循环条件,而while会先判断循环条件后执行。

# for循环

个人觉得是目前最好用又有用还容易上手的循环(雾

基本用法:

for (初始化表达式 ; 循环条件 ; 操作表达式)
{
    执行语句
}
1
2
3
4

先举个例子再解释:

for (i = 1 ; i <= 10 ; i++)
{
    语句
}
1
2
3
4

在这里,for循环将从i = 1开始执行循环,每次循环都将i自增1(i++)直到i到10为止(i <= 10),每次循环都将执行语句,共执行十次

这里客串一下while同意义写法以便结合理解...

int i = 1;
while (i <= 10)
{
    i++;
    语句
}
1
2
3
4
5
6

啊差不多就可以这样...

简单的举个例子:遍历1 - 100所有可以被3整除的数字并输出!

#include <bits/stdc++.h>
using namespace std;
int main()
{
    int i;
    for (i = 1 ; i <= 100 ; i++)
    {
        if (i % 3 == 0) //判断是否能被3整除
        {
            cout << i << " ";
        }
    }
    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 数组

一维数组和二维数组直接放一起了解吧。。。

主要是这俩只是会被“用到”...了解了解以后能用就行...

# 一维数组

关于我为什么不先写函数和更多的例题....

个人觉得应该先知道这些才能更好的写函数

另外题这东西...有时候真的可有可无,能理解代码的功能,什么题都不怕!(bushi)

还记得高中学物理用的那些带下标的变量吗...

比如 力F1,F2,F合 这样的东西

一位数组就跟这个差不多,表达形式是将下标用中括号括起来,比如这样:

a[10]
1

但是这里有必要说一下,定义时中括号内的是数组大小而非最大下标

比如这样定义:

int a[100];
1

这样你开的a的数组大小有100个,但实际的最大下标为99,多了就是数组越界...

因为它是从0开始计数的...也就是这100个a是a0 - a99而不是a1 - a100

所以这样的操作为了防越界建议定义成 a[101] 酱...

关于这玩意有什么用,我觉得可以参考一下计数排序:C/C++の算法笔记 | FLY_MCの笔记(opens new window)

另外后面用到的估计会挺多的...这里先做了解!如果能理解上面的计数排序那就最好了!daisuki(雾)!

# 二维数组

这个跟一位数组差不多就是多了个下标,如:

a[5][10]
1

二维数组与一位数组相比,是一种“从线变成面”的亚子,比如一维数组只能这样:

a[0] a[1] a[2] a[3] a[4]
1 2 3 4 5

二维数组就可以这样!

a[0][0] a[1][0] a[2][0]
a[0][1] 1 2
a[0][2] 3 4

数组先做了解就行以后用的时候会慢慢理解,这里推一道题:HDU - 2023

# 函数(功能)

啊...function这个东西其实在这里真的只是功能的意思...

所以C/C++中所谓的函数,是一个功能块

函数基本写法(格式):

返回值类型 函数名(括号里可能有变量可能为空)
{
    函数体,用于描述功能
}
1
2
3
4

例子:实现a + b的函数:

int add(int a , int b) //定义一个返回类型为int,名字为add的函数,函数的用法为add(a , b)
{
    return a + b; //返回 a + b 的值
}
1
2
3
4