C/C++基础语法
文章目录
- C/C++基础语法
- 头文件
- 经典问题
- 秒数转换
- 闰年
- 斐波那契数列
- 打印n阶菱形
- 曼哈顿距离
- 菱形图案的定义
- 大数计算
- 输入输出
- 格式化输入输出
- `getline()`函数解决`cin`只读入一个单词的问题
- 运算符
- 赋值运算符
- Switch
- 循环
- 处理未知数量输入的几种常见方法
- for-each
- 字符串String
- 字符串连接
- 字符串长度
参考资料:
- AcWing
- https://www.w3schools.com/cpp/
- https://cui-jiacai.gitbook.io/c+±stl-tutorial/
头文件
#include <cmath>
经典问题
秒数转换
输入一个整数,表示时间,单位是秒。输出一个字符串,用”时:分:秒”的形式表示这个时间。
#include <iostream>
#include <cstdio>
using namespace std;
int main()
{
int t;
cin >> t;
int hours = t / 3600;
int minutes = t % 3600 / 60;
int seconds = t % 60;
printf("%d:%d:%d\n", hours, minutes, seconds);
return 0;
}
int hours = t / 3600;
计算总秒数 t 包含多少完整的小时。因为1小时 = 3600秒,所以使用整数除法 t / 3600 可以得到完整小时数。整数除法的结果是去掉小数部分,仅保留整数部分。int minutes = t % 3600 / 60;
首先,t % 3600 计算出除去完整小时后剩余的秒数。然后,将这个剩余的秒数除以60(因为1分钟 = 60秒)得到完整分钟数。这里也使用了整数除法,因此结果是剩余秒数中包含的完整分钟数。int seconds = t % 60;
使用取模运算 t % 60 计算出除去完整分钟后剩余的秒数。因为1分钟是60秒,所以这个操作会得到小于60的秒数,即最后剩余的秒数部分。
闰年
判断闰年。闰年有两种情况:
(1) 能被100整除时,必须能被400整除;
(2) 不能被100整除时,被4整除即可。
- 输入一个年份,如果是闰年输出yes,否则输出no。
#include <iostream>
#include <cstdio>
using namespace std;
int main()
{
int year;
cin >> year;
if (year % 100 == 0)
{
if (year % 400 == 0) cout << "yes" << endl;
else cout << "no" << endl;
}
else
{
if (year % 4 == 0) cout << "yes" << endl;
else cout << "no" << endl;
}
return 0;
}
- 用一条if语句,判断闰年。
#include <iostream>
#include <cstdio>
using namespace std;
int main()
{
int year;
cin >> year;
if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0)
cout << "yes" << endl;
else
cout << "no" << endl;
return 0;
}
斐波那契数列
求斐波那契数列的第n项。f(1) = 1, f(2) = 1, f(3) = 2, f(n) = f(n-1) + f(n-2)。
#include <iostream>
using namespace std;
int main()
{
int n;
cin >> n;
int a = 1, b = 1, i = 1;
while (i < n)
{
int c = a + b;
a = b;
b = c;
i ++ ;
}
cout << a << endl;
return 0;
}
打印n阶菱形
输入一个n,打印n阶菱形。n是奇数。
#include <iostream>
using namespace std;
int main()
{
int n;
cin >> n;
int cx = n / 2, cy = n / 2;
for (int i = 0; i < n; i ++ )
{
for (int j = 0; j < n; j ++ )
if (abs(i - cx) + abs(j - cy) <= n / 2)
cout << '*';
else cout << ' ';
cout << endl;
}
return 0;
}
曼哈顿距离
理解 abs(i - cx) + abs(j - cy) <= n / 2
这个条件的关键在于理解曼哈顿距离(Manhattan Distance)的概念,以及如何通过这个距离来定义一个菱形(或者说是正方形对角线方向的正方形)图案。
曼哈顿距离是在格点坐标系中,两点在标准的直角坐标系中的绝对轴距总和(只能横行走和纵向走,走到中心点要几步)。对于任意两点 (x1, y1)
和 (x2, y2)
,它们的曼哈顿距离是 |x1 - x2| + |y1 - y2|
。
在这个程序中,(i, j)
表示当前遍历到的网格点的坐标,(cx, cy)
是图案中心点的坐标。abs(i - cx) + abs(j - cy)
计算的就是当前点 (i, j)
到中心点 (cx, cy)
的曼哈顿距离。
菱形图案的定义
这个条件 abs(i - cx) + abs(j - cy) <= n / 2
用于判断当前点是否在菱形图案内部(包括边界)。这里的 n / 2
实际上定义了菱形的“半径”,即从中心点到边界的最大曼哈顿距离。
- 当
abs(i - cx) + abs(j - cy)
的值小于或等于n / 2
时,这意味着当前点(i, j)
距离中心点的曼哈顿距离在菱形的半径范围内,因此,它位于菱形内部或边界上,程序应该在这个位置打印一个星号*
。 - 当这个距离大于
n / 2
时,当前点(i, j)
在菱形外部,程序在这个位置打印一个空格
-
大数计算
计算 2 的 N 次方。N≤10000
#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
int a[10000], size = 1, n;
a[0] = 1;
cin >> n;
while (n -- )
{
int t = 0;
for (int i = 0; i < size; i ++ )
{
t += a[i] * 2;
a[i] = t % 10;
t /= 10;
}
if (t) a[size ++ ] = t;
}
for (int i = size - 1; i >= 0; i -- ) cout << a[i];
cout << endl;
return 0;
}
这段代码是一个计算2
的N
次方的程序,特别适用于处理大数运算,即当N
很大时,直接使用常规数据类型(如int
、long
)无法存储结果的情况。这里N
可以达到10000
。程序通过模拟手工乘法的方式来计算结果,将每一位数字存储在一个数组a
中,数组的每个元素代表结果数的一位,a[0]
是结果的最低位,a[size-1]
是结果的最高位。
下面是代码的详细解释:
-
初始化:
int a[10000]
:定义一个数组a
,用于存储计算过程中的每位数字。size = 1
:初始化结果数字的大小为1,因为2
的0
次方等于1
,所以起始时数组只有一位数字1
。a[0] = 1
:将结果的最低位初始化为1
。
-
读取输入:
cin >> n
:从标准输入读取N
的值。
-
计算
2
的N
次方:while (n -- )
:循环N
次,每次循环相当于将当前结果乘以2
。- 在每次循环中,使用变量
t
来存储进位,初始时进位为0
。 for (int i = 0; i < size; i ++ )
:遍历数组的每一位,模拟乘法运算。t += a[i] * 2
:当前位乘以2
加上前一位的进位(如果有)。a[i] = t % 10
:更新当前位的值为新计算结果的个位数。t /= 10
:计算新的进位值。- 如果最后一位运算完后还有进位(
t
不为0
),则将这个进位作为新的最高位添加到数组的末尾,并增加size
。
-
输出结果:
- 由于数组中存储的是倒序的结果(即最低位在数组的开始位置),因此输出时需要从
size - 1
开始倒序遍历数组。 for (int i = size - 1; i >= 0; i -- ) cout << a[i];
:倒序输出数组的每一位,即输出计算得到的2
的N
次方的结果。cout << endl;
:在结果后输出换行符。
- 由于数组中存储的是倒序的结果(即最低位在数组的开始位置),因此输出时需要从
输入输出
格式化输入输出
#include <iostream>
#include <cstdio>
using namespace std;
int main()
{
float b = 3.12345678;
double c = 3.12345678;
printf("%.4f\n", b);
printf("%.3lf\n", c);
printf("%5d\n", a);
printf("%8.4f\n", b);
printf("%7.3lf\n", c);
printf("%-5d!\n", a);
printf("%-8.4f!\n", b); // 如果整个数字(包括小数点和小数部分)不足8个字符,则在右侧用空格填充,实现左对齐。
printf("%-7.3lf!\n", c);
printf("%05d\n", a);
printf("%08.4f\n", b);
printf("%07.3lf\n", c);
return 0;
}
- float, double等输出保留若干位小数时用:
%.4f
,%.3lf
%8.3f
, 表示这个浮点数的最小宽度为8,保留3位小数,当宽度不足时在前面补空格。%-8.3f
,表示最小宽度为8,保留3位小数,当宽度不足时在后面补上空格%08.3f
, 表示最小宽度为8,保留3位小数,当宽度不足时在前面补上0
getline()
函数解决cin
只读入一个单词的问题
cin
将空格(空格、制表符等)视为终止字符,这意味着它只能存储单个单词(即使您键入许多单词)
在处理字符串时,我们经常使用 getline() 函数来读取一行文本。它以 cin 作为第一个参数,字符串变量作为第二个参数:
string fullName;
cout << "Type your full name: ";
getline (cin, fullName);
cout << "Your name is: " << fullName;
运算符
赋值运算符
这些赋值运算符是C++(以及C和其他语言)中的复合赋值运算符,它们将算术或位运算和赋值操作结合在一起。每个运算符的作用如下:
&=
(按位与赋值运算符)
- 语法:
x &= 3
- 等效于:
x = x & 3
- 作用: 将
x
与3
进行按位与操作,然后将结果赋值给x
。按位与操作对应位都为1时结果为1,否则为0。
|=
(按位或赋值运算符)
- 语法:
x |= 3
- 等效于:
x = x | 3
- 作用: 将
x
与3
进行按位或操作,然后将结果赋值给x
。按位或操作对应位有一个为1时结果为1,都为0时结果为0。
^=
(按位异或赋值运算符)
- 语法:
x ^= 3
- 等效于:
x = x ^ 3
- 作用: 将
x
与3
进行按位异或操作,然后将结果赋值给x
。按位异或操作对应位不同为1,相同为0。
>>=
(右移赋值运算符)
- 语法:
x >>= 3
- 等效于:
x = x >> 3
- 作用: 将
x
的二进制表示向右移动3
位,然后将结果赋值给x
。右移操作会将高位丢弃,对于无符号数,低位补0;对于有符号数,低位补充依赖于具体实现(通常是补符号位,即算术右移)。
<<=
(左移赋值运算符)
- 语法:
x <<= 3
- 等效于:
x = x << 3
- 作用: 将
x
的二进制表示向左移动3
位,然后将结果赋值给x
。左移操作会在低位补0,高位丢弃,相当于将x
乘以2
的移动位数次方(这里是2^3
或8
)。
二进制位移操作解释
Switch
int day = 4;
switch (day) {
case 1:
cout << "Monday";
break;
case 2:
cout << "Tuesday";
break;
case 3:
cout << "Wednesday";
break;
case 4:
cout << "Thursday";
break;
case 5:
cout << "Friday";
break;
case 6:
cout << "Saturday";
break;
case 7:
cout << "Sunday";
break;
}
// Outputs "Thursday" (day 4)
循环
处理未知数量输入的几种常见方法
-
while(cin >> x)
在C++中,cin >> x
尝试从标准输入(通常是键盘输入或重定向的文件输入)读取一个值到变量x
中。如果读取成功,表达式的值为true
;如果遇到输入结束(如文件结束或遇到不匹配类型的输入),表达式的值为false
。因此,while(cin >> x)
循环会持续读取输入直到遇到输入结束。 -
while(scanf("%d", &x) != -1)
和while(~scanf("%d", &x))
在C语言中,scanf("%d", &x)
尝试从标准输入读取一个整数到变量x
中。scanf
函数返回成功读取的项目数量。如果读取成功,返回1
;如果遇到输入结束(EOF),返回-1
。
while(scanf("%d", &x) != -1)
循环会持续读取输入,直到scanf
返回-1
,即遇到输入结束。while(~scanf("%d", &x))
利用了位运算符~
(按位取反)。在大多数系统上,EOF
(即-1
)按位取反后的结果是0
,而0
是逻辑假。因此,这个循环同样会持续读取输入,直到遇到输入结束。这是一种更为简洁的写法。
while(cin >> x && x)
和while(cin >> x, x)
这两种形式都用于处理当读入的最后一个值为0
且这个0
不需要处理的情况。
while(cin >> x && x)
循环会持续读取并处理非零的输入。如果x
为0
或遇到输入结束,循环停止。while(cin >> x, x)
使用了逗号运算符,该运算符执行其两边的表达式,并返回右侧表达式的结果。这里先尝试从输入读取一个值到x
,然后通过逗号运算符返回x
的值作为while
循环的条件。这个用法较为少见,且可能引起混淆,其效果与while(cin >> x && x)
相同。
以上这些方法提供了在未知数量输入的情况下从标准输入读取数据的有效手段,以及如何根据特定条件(如输入的值为 0
)结束输入的方式。
for-each
“for-each 循环”(在 C++ 版本 11 (2011) 中引入,专门用于循环数组(或其他数据集合)中的元素
int myNumbers[5] = {10, 20, 30, 40, 50};
for (int i : myNumbers) {
cout << i << "\n";
}
字符串String
字符串连接
使用+
号或者append()
string firstName = "John";
string lastName = "Doe";
string fullName = firstName + " " + lastName;
fullName = firstName.append(lastName);
cout << fullName;
字符串长度
使用length()
或size()
string txt = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
cout << "The length of the txt string is: " << txt.length();
cout << "The length of the txt string is: " << txt.size();