docs/lang/pas-cpp.md
author: kexplorning, Ir1d, lvneg1
以下过程均在 Windows 系统中操作.
可以参考如下页面中内容:
可以参考 VS Code 页面中内容.Visual Studio Code 官方网站上有文档解释如何进行 C++ 的配置.一般而言 VS Code 搭配插件使用更方便,见 VS Code 的官方网站.
C++ 程序都是从 main 这个部分开始运行的.
大括号表示块语句的开始与结束:{ 就相当于 Pascal 里面的 begin,而 } 就相当于 end.
注意,和 Pascal 一样,C++ 每句话结束要加分号 ;,不过大括号结尾不需要有分号,而且程序结束末尾不用打句号 ..
对于注释,// 表示行内注释,/* */ 表示块注释.
按照惯例,看看 Hello World 吧.
#include <iostream> // 导入 iostream 库
int main() // main 部分
{
std::cout << "Hello World!" << std::endl;
return 0;
}
然后编译运行一下,看看结果.
第一行,#include <iostream> 的意思是,导入 iostream 这个库.
??? note "Pascal 的库文件" Pascal 其实是有库文件的,只不过,很多同学从来都没有用过……
看到第三行的 main 吗?程序从 main 开始执行.
接下来最重要的一句话是
std::cout << "Hello World!" << std::endl;
std::cout 是输出命令.你可能看过有些 C++ 程序中直接写的是 cout.
??? note "有关 std:: 前缀"
有关 std:: 这个前缀的问题,请见 这节 底下的注释「什么是 std?」.
中间的 << 很形象地表示流动,其实它就是表示输出怎么「流动」的.这句代码的意思就是,"Hello World!" 会先被推到输出流,之后 std::endl 再被推到输出流.
而 std::endl 是 输出 换行命令,这与 Pascal 的 writeln 类似,不过 C++ 里面可没有 coutln.Pascal 与 C++ 的区别在于,write('Hello World!') 等价于 std::cout << "Hello World!",而 writeln('Hello World!') 等价于 std::cout << "Hello World!" << std::endl.
此处 "Hello World!" 是字符串,Pascal 中字符串都是用单引号 ' 不能用双引号,而 C++ 的字符串必须用双引号.C++ 中单引号包围的字符会有别的含义,后面会再提及的.
好了,到这里 Hello World 应该解释的差不多了.
可能有同学会问,后面那个 return 0 是什么意思?那个 int main() 是啥意思?先别管它,一开始写程序的时候先把它当作模板来写吧(这里也是用模板写的).因为入门时并不会用到 main 中参数,所以不需要写成 int main(int argc, char const *argv[]).
经典的 A+B Problem.
#include <iostream>
int main() {
int a, b, c;
std::cin >> a >> b;
c = a + b;
std::cout << c << std::endl;
return 0;
}
注:代码空行较多,若不习惯可去掉空行.
std::cin 是读入(cin 即 C-in),>> 也与输出语法的类似.
这里多出来的语句中最重要的是两个,一个是变量声明语句.
int a, b, c;
你可能习惯于 Pascal 里面的声明变量:
var
a, b, c: integer;
C++ 的声明是直接以数据类型名开头的,在这里,int(整型)开头表示接下来要声明变量.
接着一个最重要的语句就是赋值语句.
c = a + b;
这是 Pascal 与 C++ 语法较大的不同:Pascal 的赋值是 :=,C++ 是 =;而 C++ 判断相等是 ==.
C++ 也可直接在声明时进行变量初始化赋值.
int a = 0, b = 0, c = 0;
好了,到现在为止,你已经掌握了一些最基本的东西了,剩下就是找 Pascal 和 C++ 里面对应的语法和不同的特征.
不过在此之前,强烈建议先看 变量作用域:全局变量与局部变量.
请善用<kbd>Alt</kbd>+<kbd>←</kbd>与<kbd>Alt</kbd>+<kbd>→</kbd>返回跳转.
C++ 与 Pascal 基本上差不多,常见的有
bool:布尔类型int:整型float:单精度浮点型double:双精度浮点型char:字符型void:无类型C++ 的单引号是专门用于表示单个字符的(字符型),比如 'a',而字符串(字符型数组)必须要用双引号.
C++ 还要很多额外的数据类型,请参考 基础类型 - cppreference.com.
const double PI = 3.1415926;
若不清楚有关宏展开的问题,建议使用常量,而不用宏定义.
请直接参考 运算 一文中内容.附录中也提供了运算符与数学函数语法对比表.
if 语句if (a = b) and (a > 0) and (b > 0) then
begin
b := a;
end
else
begin
a := b;
end;
if (a == b && a > 0 && b > 0) {
b = a;
} else {
a = b;
}
布尔运算与比较
and -> &&or -> ||not -> != -> ==<> -> !=注释:
and 与 C++ 中 && 优先级不同,C++ 不需要给判断条件加括号.=,赋值是 :=;C++ 中判断相等是 ==,赋值是 =.if 语句的括号内写了 a = b 而不是 a == b,程序不会报错,而是会把 b 赋值给 a,并使赋值表达式 a = b 整体具有 a 在赋值操作完成后的值.end 后面加分号.???+ warning "易错提醒"
特别注意:不要把 == 写成 =!
由于 C/C++ 比 Pascal 语法灵活,如果在判断语句中写了 `if (a=b)`,那么程序会顺利运行下去,因为 C++ 中 `a=b` 是有返回值的.
case 与 switch用到得不多,此处不详细展开.
需要注意:C++ 没有 1..n,也没有连续不等式(比如 1 < x < 2).
以下三种循环、六份代码实现的功能是一样的.
while 循环while 很相似.(C++ 此处并非完整程序,省略一些框架模板,后同)
var i: integer;
begin
i := 1;
while i <= 10 do
begin
write(i,' ');
inc(i); // 或者 i := i + 1;
end;
end.
int i = 1;
while (i <= 10) {
std::cout << i << " ";
i++;
}
for 循环C++ 的 for 语句非常不同.
var i: integer;
begin
for i:= 1 to 10 do
begin
write(i, ' ');
end;
end.
for (int i = 1; i <= 10; i++) {
std::cout << i << " ";
}
注释:
for (int i = 1; i <= 10; i++){ 这一行语句很多,for 中有三个语句.int i = 1; 此时声明一局部变量 i 并初始化.(这个设计比 Pascal 要合理得多.)i <= 10; 作为判断循环是否继续的标准.i++,在每次循环结尾执行,意思大约就是 Pascal 中的 inc(i),此处写成 ++i 也是一样的.i++ 与 ++i 的区别请参考其他资料.repeat until 与 do while 循环注意,repeat until 与 do while 是不同的,请对比以下代码
var i: integer;
begin
i := 1;
repeat
write(i, ' ');
inc(i);
until i = 11;
end.
int i = 1;
do {
std::cout << i << " ";
i++;
} while (i <= 10);
C++ 中 break 的作用与 Pascal 是一样的,退出循环.
而 continue 也是一样的,跳过当前循环,进入下一次循环(回到开头).
请参考 序列式容器 页面中内容.
C++ 标准库中提供了 vector,相当于不定长数组,调用前需导入库文件.
#include <iostream>
#include <vector> // 导入 vector 库
int main() {
std::vector<int> a; // 声明 vector a 并定义 a 为空 vector 对象
int n;
std::cin >> n;
// 读取 a
for (int i = 0; i < n; i++) {
int t;
std::cin >> t;
a.push_back(t); // 将读入的数字 t,放到 vector a 的末尾;该操作复杂度 O(1)
/* 这里不能使用下标访问来赋值,因为声明时,a 大小依然为空,
此处使用 `a[i] = t;` 是错误做法.
*/
}
// 将读入到 a 中的所有数打印出
for (int i = 0; i < n; i++) {
std::cout << a[i] << ", "; // !注意,a 中第一个数是 a[0];
// 如果下标越界,它会返回一个未知的值(溢出),而不会报错
}
std::cout << std::endl;
return 0;
}
C++ 访问数组成员,与 Pascal 类似,不过有很重要的区别:数组的第一项是 a[0],而 Pascal 中是可以自行指定的.
请参考 string 页面中内容.
C++ 标准库中提供了 string,与 vector 可以进行的操作有些相同,同样需要导入库文件.
#include <iostream>
#include <string>
int main() {
std::string s; // 声明 string s
std::cin >> s; // 读入 s;
// 读入时会忽略开头所有空格符(空格、换行符、制表符),读入的字串直到下一个空格符为止.
std::cout << s << std::endl;
return 0;
}
请参考 数组 页面中内容.
如果要用不定长的数组请用 vector,不要用 C 风格的数组.
C 风格的数组与指针有密切关系,所以此处不多展开.
C++ 几乎可以在 任何地方 声明变量.请参考 变量作用域.
在写 Pascal 过程/函数时,容易忘记声明局部变量 i 或者 j,而一般主程序里会有循环,于是大部分情况下 i 与 j 都是全局变量,于是,在这种情况下,过程/函数中对 i 操作极易出错.更要命的是,如果忘记声明这种局部变量,编译器编译不报错,程序可以运行.(有很多难找的 bug 就是这么来的.)
所以,在使用 C++ 时,声明变量,比如循环中使用的 i,不要用全局变量,能用局部变量就用局部变量.如果这么做,不用担心函数中变量名(比如 i)冲突.
??? note "额外注"
Pascal 可在某种程度上避免这个问题,仿照 C++ 的方法,主程序只有调用过程/函数,不声明 i,j 这类极易名称冲突的全局变量,如果需要循环,另写一个过程进行调用.
int i = 2;
if (i) { // i = 0 会返回 false,其余返回 true
std::cout << "true";
} else {
std::cout << "false";
}
不光是 int 转成 bool,还有 int 与 float 相互转换.在 Pascal 中可以把整型赋给浮点型,但不能反过来.C++ 没有这个问题.
int a;
a = 3.2; // 此时 a = 3
float b = a; // 此时 b = 3.0
区分 / 是整除还是浮点除法,是通过除数与被除数的类型判断的
float a = 32 / 10; // 32/10 的结果是 3(整除);a = 3.0
float b = 32.0 / 10; // 32.0/10 的结果是 3.2;b = 3.2
pow(a, b) 计算 $a^b$,该函数返回的是浮点型,如果直接用来计算整数的幂,由于有自动转换,不需要担心它会报错
int a = pow(2, 3); // 计算 2^3
还有 char 与 int 之间相互转换.
char a = 48; // ASCII 48 是 '0'
int b = a + 1; // b = 49
std::cout << (a == '0'); // true 输出 1
其实 C++ 中的 char 与 bool 本质上是整型.
详细内容请参考 隐式转换 - cppreference.com 一文.
有些时候需要读取到数据结束,比如,求一组不定数量的数之和(数据可以多行),直到文件末尾,实现方式是
??? note "文件末尾 EOF" EOF,文件末尾标识符,在命令行中 Windows 上以<kbd>Ctrl</kbd>+<kbd>Z</kbd>输入(还需按<kbd>Enter</kbd>),Unix-like 系统以<kbd>Ctrl</kbd>+<kbd>D</kbd>输入.
#include <iostream>
int main() {
int sum = 0, a = 0;
while (std::cin >> a) {
sum += a;
}
std::cout << sum << std::endl;
return 0;
}
实现原理:while (std::cin >> a) 中 std::cin >> a 若在输入有问题或遇到文件结尾时,会返回 false,使得循环中断.
C++ 只有函数没有过程但有 void,没有函数值变量但有 return.
Pascal 函数与 C++ 函数对比示例:
function abs(x:integer):integer;
begin
if x < 0 then
begin
abs := -x;
end
else
begin
abs := x;
end;
end;
int abs(int x) {
if (x < 0) {
return -x;
} else {
return x;
}
}
C++ 中函数声明 int abs,就定义了 abs() 函数且返回值为 int 型(整型),函数的返回值就是 return 语句给出的值.
如果不想有返回值(即 Pascal 的「过程」),就用 void.void 即「空」,什么都不返回.
var ans: integer;
procedure printAns(ans:integer);
begin
writeln(ans);
end;
begin
ans := 10;
printAns(ans);
end.
#include <iostream>
void printAns(int ans) {
std::cout << ans << std::endl;
return;
}
int main() {
int ans = 10;
printAns(ans);
return 0;
}
C++ 的 return 与 Pascal 中给函数变量赋值有一点非常大的不同.C++ 的 return 即返回一个值,执行完这个语句,函数就执行结束了;而 Pascal 中给函数变量赋值并不会跳出函数本身,而是继续执行.于是,如果 Pascal 需要某处中断函数/过程,就需要一个额外的命令,即 exit.而 C++ 则不需要,如果需要在某处中断,可以直接使用 return.比如(由于实在想不出来简短且实用的代码,所以就先这样)
#include <iostream>
void printWarning(int x) {
if (x >= 0) {
return; // 该语句在此处相当于 Pascal 中的 `exit;`
}
std::cout << "Warning: input a negative number.";
}
int main() {
int a;
std::cin >> a;
printWarning(a);
return 0;
}
而在某种意义上,前面的 abs 函数,这样才是严格等效的
function abs(x:integer):integer;
begin
if x < 0 then
begin
abs := -x; exit; // !注意此处
end
else
begin
abs := x; exit; // !注意此处
end;
end;
int abs(int x) {
if (x < 0) {
return -x;
} else {
return x;
}
}
???+ note "特别提醒"
C++ 中 exit 是退出程序;不要顺手把 exit 打上去,要用 return!
C++ 把函数和过程统统视作函数,连 main 都不放过,比如写 int main,C++ 视 main 为一个整型的函数,这里返回值是 0.它是一种习惯约定,返回 0 代表程序正常退出.
也许你已经猜到了,main(int argc, char const *argv[]) 中的参数就是 int argc 与 char const *argv[],不过意义请参考其他资料.
C++ 中没有 Pascal 的 var 关键字可以改变传递的参数,但是 C++ 可以使用引用和指针达到同样的效果.
var a, b: integer;
procedure swap(var x,y:integer);
var temp:integer;
begin
temp := x;
x := y;
y := temp;
end;
begin
a := 10; b:= 20;
swap(a, b);
writeln(a, ' ', b);
end.
// 使用指针的代码
#include <iostream>
void swap(int* x, int* y) {
int temp;
temp = *x;
*x = *y;
*y = temp;
}
int main() {
int a = 10, b = 20;
swap(&a, &b);
std::cout << a << " " << b;
return 0;
}
注意,此处 C++ 代码 涉及指针问题.指针问题还是很麻烦的,建议去阅读相关资料.
// 使用引用的代码
#include <iostream>
void swap(int& x, int& y) {
int temp;
temp = x;
x = y;
y = temp;
}
int main(int argc, char const* argv[]) {
int a = 10, b = 20;
swap(a, b);
std::cout << a << " " << b;
return 0;
}
注意,此处 C++ 代码涉及 引用相关类型问题.在用引用调用一些 STL 库、模板库的时候可能会遇到一些问题,这时候需要手动声明别类型.具体资料可以在《C++ Primer》第五版或者网络资料中自行查阅.
C++ 中函数传递参数还有其他方法,其中一种是 直接使用全局变量传递参数,如果不会用指针,可以先用这种方法.但是这种方法的缺陷是没有栈保存数据,没有办法在递归函数中传参.(除非手写栈,注意,手写栈也是一种突破系统栈限制的方法.)
千万不要重复造轮子(除非为了练习),想要自己动手写一个功能出来之前,先去看看有没有这个函数或者数据结构.
C++ 标准库中 <algorithm> 有很多有用的函数比如快排、二分查找等,可以直接调用.请参考 STL 算法 页面.
还有 STL 容器,比如数组、向量(可变大小的数组)、队列、栈等,附带很多函数.请参考 STL 容器简介 页面.
如果要找关于字符串操作的函数见
C/C++ 的指针是很灵活的东西,可以参考 指针 页面.如果想要彻底理解指针,建议找本书或者参考手册仔细阅读.
写到这里,很多同学会觉得这一点都不急救啊,有很多东西没有提到啊.那也是没办法的事情.
虽然是为了急救,但很多东西像怎么把字符串转化为数字,怎么搜索字符串中的字符,这些东西也不适合一篇精悍短小的急救帖,如果把这些都写出来,那就是 C++ 入门教程,所以请充分利用本 Wiki、参考手册与搜索引擎.
需要指出的一点是,上面说 C++ 的语法,其实有很多语法是从 C 语言来的,标题这么写比较好——《Pascal 转 C/C++ 急救帖》.
Pascal 在上个世纪后半叶是门很流行的语言,它早于 C 语言,不过随着 UNIX 系统的普及,微软使用 C 语言,现在 Pascal 已经成为历史了.Pascal 后期发展也是有的,比如 Free Pascal 这个开源编译器项目,增加面向对象的特性(Delphi 语言).Pascal 目前的用处除了在信息竞赛外,有一个特点是其他语言没有的——编译支持非常非常多老旧机器,比如 Gameboy 这种上个世纪的任天堂游戏机,还有一个用处就是以伪代码的形式(Pascal 风格的伪代码)出现在各种教科书中.
最后,Pascal 的圈子其实很小,C/C++ 的圈子很大,帮助手册与教程很多很全,一定要掌握好英语.世界上还有很多很多编程语言,而计算机这门学科与技术不光是信息竞赛和编程语言.
仅包括最常用的运算符与函数.
| Pascal | C++ | |
|---|---|---|
| 加法 | a + b | a + b |
| 减法 | a - b | a - b |
| 乘法 | a * b | a * b |
| 整除 | a div b | a / b |
| 浮点除法 | a / b | a / b |
| 取模 | a mod b | a % b |
| Pascal | C++ | |
|---|---|---|
| 非 | not(a) | !a |
| 且 | a and b | a && b |
| 或 | a or b | <code>a || b</code> |
| Pascal | C++ | |
|---|---|---|
| 相等 | a = b | a == b |
| 不等 | a <> b | a != b |
| 大于 | a > b | a > b |
| 小于 | a < b | a < b |
| 大于等于 | a >= b | a >= b |
| 小于等于 | a <= b | a <= b |
| Pascal | C++ |
|---|---|
a := b | a = b |
a := a + b | a += b |
a := a - b | a -= b |
a := a * b | a *= b |
a := a div b 或 a := a / b | a /= b |
a := a mod b | a %= b |
| Pascal | C++ | |
|---|---|---|
| 自增 | inc(a) | a++ |
| 自增 | inc(a) | ++a |
| 自减 | dec(a) | a-- |
| 自减 | dec(a) | --a |
使用需要导入 <cmath> 库.
| Pascal | C++ | |
|---|---|---|
| 绝对值 | abs(a) | abs(a)(整数) |
| 绝对值 | abs(a) | fabs(a)(浮点数) |
| $a^b$ | N/A1 | pow(a, b) |
| 截断取整 | trunc(a) | trunc(a) |
| 近似取整 | round(a) | round(a) |
其他函数请参考:
Extended Pascal 中有 a**b,不过需要导入 Math 库. ↩