
OI-Wiki Pascal 转 C/C 急救指南从语法迁移到实战技巧【免费下载链接】OI-wiki:star2: Wiki of OI / ICPC for everyone. 某大型游戏线上攻略内含炫酷算术魔法项目地址: https://gitcode.com/GitHub_Trending/oi/OI-wiki导读本文是 OI-Wiki 面向Pascal 基础读者的 C/C 迁移速成指南覆盖从开发环境搭建、Hello World 与 AB 起步到变量、运算符、控制流、数组与字符串、函数、类型转换等核心语法的逐项对照迁移并附有完整的「Pascal 与 C 运算符与数学函数语法对比表」。读完本文你将能够在 OI/ICPC 竞赛中熟练地把 Pascal 代码翻译为 C 代码并避开/混用、全局变量滥用、类型隐式转换等典型的迁移陷阱。本文所有 C 语法的延伸讲解均可与 OI-Wiki 语言模块docs/lang中的对应专题文档互相印证。一、C 快速安装与环境配置以下过程均在 Windows 系统中操作。根据本地评测与 OI 训练的常见流程有两种主流配置路线使用 IDEDev-C轻量、开箱即用适合入门时直接「安装—编译—运行」。Code::Blocks自带项目管理与多编译器支持适合需要工程化一点的组织方式。两个页面均介绍了下载、安装以及新建项目/单文件编译运行的完整步骤直接照做即可。使用代码编辑器 编译器 调试器可以参考 VS Code 页面中的内容。Visual Studio Code 官方网站上有文档解释如何进行 C 的配置一般而言 VS Code 搭配 C/C 扩展插件使用更方便代码补全、断点调试一体编译后端仍需单独安装 MinGW 等编译器并在tasks.json/launch.json中配置好编译与调试命令。配置完成后代码编辑、编译、单步调试可以完全在编辑器内完成这也更贴近 OIer 日常「写代码 → 测样例 → 提交 OJ」的工作流。二、C 语法快速提要C 程序都是从main这个部分开始运行的。大括号表示块语句的开始与结束{就相当于 Pascal 里面的begin而}就相当于end。注意和 Pascal 一样C 每句话结束要加分号;不过大括号结尾不需要有分号而且程序结束末尾不用打句号.。对于注释//表示行内注释/* */表示块注释更多注释与代码框架内容见 docs/lang/basic.md。按照惯例看看 Hello World 吧。Hello World第一个 C 程序#include iostream // 导入 iostream 库 int main() // main 部分 { std::cout Hello World! std::endl; return 0; }然后编译运行一下看看结果。简要解释第一行#include iostream的意思是导入iostream这个库。#include本质是预处理命令编译时编译器会把头文件iostream的内容「复制」并「粘贴」到这条语句处这样你就可以使用其中提供的std::cin、std::cout、std::endl等对象了详见 docs/lang/basic.md。??? note Pascal 的库文件 Pascal 其实是有库文件的只不过很多同学从来都没有用过……看到第三行的main吗程序从main开始执行。main是每个 C/C 程序都必须有的函数由系统或外部程序在程序启动时调用在此之前会先完成全局变量的构造详见 docs/lang/func.md。接下来最重要的一句话是std::cout Hello World! std::endl;std::cout是输出命令。你可能看过有些 C 程序中直接写的是cout。??? note 有关 std:: 前缀 有关std::这个前缀的问题请见 docs/lang/basic.md 底下的注释「什么是 std」。简要来说std是 C 标准库所使用的命名空间使用命名空间是为了避免重名如果执行了using namespace std;就可以直接用cout代替std::cout但工程上并不推荐这样做详见 docs/lang/namespace.md。中间的很形象地表示流动其实它就是表示输出怎么「流动」的。这句代码的意思就是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 中单引号包围的字符会有别的含义表示单个char字符如a后面会再提及。可能有同学会问后面那个return 0是什么意思那个int main()是啥意思先别管它一开始写程序的时候先把它当作模板来写吧这里也是用模板写的。因为入门时并不会用到main中参数所以不需要写成int main(int argc, char const *argv[])。关于main返回值return 0表示程序运行成功默认情况下程序结束时返回 0 表示一切正常否则返回值表示错误代码在 C 或 C 中程序返回值不为 0 会导致运行时错误RE——详见 docs/lang/basic.md。简单练习试着换个字符串输出。试着了解转义字符\n表示换行、\t表示制表符、\\表示反斜杠等见 docs/lang/basic.md。AB Problem第二个 C 程序经典的 AB 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也与输出语法的类似std::cin x y表示把输入流中的值依次「流入」变量x和y。这里多出来的语句中最重要的是两个一个是变量声明语句。int a, b, c;你可能习惯于 Pascal 里面的声明变量var a, b, c: integer;C 的声明是直接以数据类型名开头的在这里int整型开头表示接下来要声明变量。C 的类型系统由基础类型void、bool、int、char、float、double等与复合类型组成且int在绝大多数平台上为 32 位C 标准只保证至少 16 位详细类型说明见 docs/lang/var.md。接着一个最重要的语句就是赋值语句。c a b;这是 Pascal 与 C 语法较大的不同Pascal 的赋值是:C 是而 C 判断相等是。C 也可直接在声明时进行变量初始化赋值。int a 0, b 0, c 0;注意C 中定义在花括号包裹处的变量是局部变量定义在没有花括号包裹处的变量是全局变量定义时没有初始化值的全局变量会被初始化为 0而局部变量不会必须手动赋初值否则可能引起难以发现的 bug见 docs/lang/var.md。简单练习重写一遍代码提交到 OJ 上并且 AC。更多的输入输出语法参考 docs/lang/basic.md并试着了解 C 的格式化输出scanf/printf的速度通常比cin/cout更快且能方便地控制格式%d、%s、%c、%lf、%lld等格式符及%.6lf保留小数位的用法详见该页。结束语与下一步好了到现在为止你已经掌握了一些最基本的东西了剩下就是找 Pascal 和 C 里面对应的语法和不同的特征。不过在此之前强烈建议先看「变量作用域全局变量与局部变量」一节。请善用Alt←与Alt→返回跳转。三、语法对照迁移变量基本数据类型C 与 Pascal 基本上差不多常见的有bool布尔类型取值只能为true或false一般占 1 字节见 docs/lang/var.mdint整型float单精度浮点型IEEE-754 binary32 格式double双精度浮点型IEEE-754 binary64 格式实际应用中常优先使用double因为float表示范围较小且精度不高char字符型底层存储仍是整数通过 ASCII 编码实现字符与整数的一一对应void无类型不能声明void类型的变量但函数返回值可以是voidC 的单引号是专门用于表示单个字符的字符型比如a而字符串字符型数组必须要用双引号。C 还有很多额外的数据类型short、long、long long、unsigned修饰符以及 C11 起的定宽整数类型int32_t等请参考 docs/lang/var.md 与 cppreference 的「基础类型」页面。常量声明const double PI 3.1415926;若不清楚有关宏展开的问题建议使用常量而不用宏定义。使用const声明常量后其值在定义后不能被修改若试图修改会在编译环节直接报错error: assignment of read-only variable而#define本质是文本替换的预处理宏作用域是整个程序可能被意外替换因此工程上更推荐const。OI 中#define的一些特殊用法如#define int long long的利与弊见 docs/lang/basic.md常量专题见 docs/lang/const.md。运算符运算符的完整专题见 docs/lang/op.md其内容覆盖算术运算符含/与%的类型转换规则、位操作符、自增/自减、复合赋值、条件运算符、比较运算符与逻辑运算符以及 C 运算符优先级总表。本文附录中也提供了运算符与数学函数语法对比表。条件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 - ! - - !注释Pascal 中and与 C 中优先级不同C 不需要给判断条件加括号。Pascal 中判断相等是赋值是:C 中判断相等是赋值是。如果在if语句的括号内写了a b而不是a b程序不会报错而是会把b赋值给a并使赋值表达式a b整体具有a在赋值操作完成后的值。C 不需要思考到底要不要在end后面加分号。C 布尔运算中非布尔值可以自动转化为布尔值。??? warning 易错提醒 特别注意不要把写成由于 C/C 比 Pascal 语法灵活如果在判断语句中写了 if (ab)那么程序会顺利运行下去因为 C 中 ab 是有返回值的。if (op1) 是在对 op 进行赋值只要赋值为非 0 值表达式即为真条件永远满足无法起到判断作用而 if (op1) 才是对 op 的值进行判断[docs/lang/op.md](https://link.gitcode.com/i/03d80f758f49b62dbfc61d686a0a3370#比较运算符) 也专门强调了这一区别。case与switch用到得不多此处不详细展开。需要注意C 没有1..n也没有连续不等式比如1 x 2。switch与分支结构的完整介绍见 docs/lang/branch.md。循环以下三种循环、六份代码实现的功能是一样的。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的区别前缀先自增后取表达式值、后缀先取表达式值后自增请参考 docs/lang/op.md。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);两者的区别在于Pascal 的repeat until是「直到条件为真才停止」until条件成立即退出而 C 的do while是「当条件为真时继续」while条件成立才继续循环。上面两份代码虽然在i 11/i 10的取值下效果等价但语义方向正好相反迁移时务必转换条件。循环结构的完整讲解见 docs/lang/loop.md。循环控制C 中break的作用与 Pascal 是一样的退出循环。而continue也是一样的跳过当前循环进入下一次循环回到开头。数组与字符串不定长数组标准库类型 Vector完整内容请参考 docs/lang/csl/sequence-container.md。C 标准库中提供了vector相当于不定长数组内存连续、长度可变的数组调用前需导入库文件。#include iostream #include vector // 导入 vector 库 int main() { std::vectorint 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 中是可以自行指定的。vector的常用操作还包括at()越界抛出异常、front()/back()、size()/empty()、resize()、reserve()/capacity()、insert()/erase()/clear()等且支持列表初始化C11 起与整体赋值拷贝详见 docs/lang/csl/sequence-container.md。字符串标准库类型 String完整内容请参考 docs/lang/csl/string.md。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; }C 风格数组完整内容请参考 docs/lang/array.md。如果要用不定长的数组请用vector不要用 C 风格的数组。C 风格的数组与指针有密切关系所以此处不多展开。C 风格数组的下标从 0 开始越界访问是未定义行为可能产生段错误或修改预期以外的变量较大的数组建议声明为全局变量以避免爆栈详见 docs/lang/array.md。四、重要不同之处变量作用域全局变量与局部变量C 几乎可以在任何地方声明变量。作用域的详细讲解见 docs/lang/var.md。在写 Pascal 过程/函数时容易忘记声明局部变量i或者j而一般主程序里会有循环于是大部分情况下i与j都是全局变量于是在这种情况下过程/函数中对i操作极易出错。更要命的是如果忘记声明这种局部变量编译器编译不报错程序可以运行。有很多难找的 bug 就是这么来的。所以在使用 C 时声明变量比如循环中使用的i不要用全局变量能用局部变量就用局部变量。如果这么做不用担心函数中变量名比如i冲突。??? note 额外注 Pascal 可在某种程度上避免这个问题仿照 C 的方法主程序只有调用过程/函数不声明ij这类极易名称冲突的全局变量如果需要循环另写一个过程进行调用。C 可以自动转换类型int i 2; if (i) { // i 0 会返回 false其余返回 true std::cout true; } else { std::cout false; }不光是int转成bool零值转false、非零值转true即布尔转换还有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.2b 3.2对于双目算术运算符当参与运算的两个变量类型相同时不发生类型转换结果用参与运算的变量类型容纳否则会进行类型转换使两者一致如x/3.0的结果是double而x/3的结果是int规则详见 docs/lang/op.md 与 docs/lang/var.md。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本质上是整型由于历史原因C 中布尔类型和字符类型会被视作特殊的整型小整数类型参与运算时会发生整数提升详见 docs/lang/var.md。详细内容请参考 docs/lang/var.md 与 cppreference 的「隐式转换」一文。C 很多语句有返回值以如何实现读取数量不定数据为例有些时候需要读取到数据结束比如求一组不定数量的数之和数据可以多行直到文件末尾实现方式是??? note 文件末尾 EOF EOF文件末尾标识符在命令行中 Windows 上以CtrlZ输入还需按EnterUnix-like 系统以CtrlD输入#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 中很多语句/表达式是有返回值的——这正是 C 与 Pascal 在语言设计上的重要差异之一。函数C 只有函数没有过程但有void没有函数值变量但有return。函数function是若干语句的集合声明函数需要返回值类型、函数名与参数列表声明与定义分离、return语句立即结束当前函数等规则详见 docs/lang/func.md。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打上去要用returnC 把函数和过程统统视作函数连main都不放过比如写int mainC 视main为一个整型的函数这里返回值是0。它是一种习惯约定返回0代表程序正常退出。也许你已经猜到了main(int argc, char const *argv[])中的参数就是int argc与char const *argv[]通过这两个参数可以获得外界传给程序的命令行参数详见 docs/lang/func.md不过意义请参考其他资料。在函数中传递参数C 中没有 Pascal 的var关键字可以改变传递的参数但是 C 可以使用引用和指针达到同样的效果。默认情况下C 的参数传递是「按值」的在函数内部修改形参并不会影响调用处的变量docs/lang/func.md 中有明确演示。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 代码涉及指针问题。指针问题还是很麻烦的建议去阅读相关资料OI-Wiki 的指针专题见 docs/lang/pointer.md其中还包含空指针与std::nullptr_t的内容。// 使用引用的代码 #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》第五版或者网络资料中自行查阅。引用的详细说明可参考 docs/lang/reference.md。C 中函数传递参数还有其他方法其中一种是直接使用全局变量传递参数如果不会用指针可以先用这种方法。但是这种方法的缺陷是没有栈保存数据没有办法在递归函数中传参。除非手写栈注意手写栈也是一种突破系统栈限制的方法。五、C 标准库与参考资料千万不要重复造轮子除非为了练习想要自己动手写一个功能出来之前先去看看有没有这个函数或者数据结构。C 标准库C 标准库中algorithm有很多有用的函数比如快排、二分查找等可以直接调用。请参考 docs/lang/csl/algorithm.md 页面。还有 STL 容器比如数组、向量可变大小的数组、队列、栈等附带很多函数。请参考 docs/lang/csl/container.md 页面。如果要找关于字符串操作的函数见 docs/lang/csl/string.md。C/C 的指针是很灵活的东西可以参考 docs/lang/pointer.md 页面。如果想要彻底理解指针建议找本书或者参考手册仔细阅读。错误排查与技巧常见错误常见技巧C 语言资料学习资源cppreference.com最重要的 C/C 参考资料C 与其他常用语言的区别OI-Wiki 中关于 C 与其他语言差异的专题页Google C Style Guide六、后记写到这里很多同学会觉得这一点都不急救啊有很多东西没有提到啊。那也是没办法的事情。虽然是为了急救但很多东西像怎么把字符串转化为数字怎么搜索字符串中的字符这些东西也不适合一篇精悍短小的急救帖如果把这些都写出来那就是 C 入门教程所以请充分利用本 Wiki、参考手册与搜索引擎。需要指出的一点是上面说 C 的语法其实有很多语法是从 C 语言来的标题这么写比较好——《Pascal 转 C/C 急救帖》。Pascal 在上个世纪后半叶是门很流行的语言它早于 C 语言不过随着 UNIX 系统的普及微软使用 C 语言现在 Pascal 已经成为历史了。Pascal 后期发展也是有的比如 Free Pascal 这个开源编译器项目增加面向对象的特性Delphi 语言。Pascal 目前的用处除了在信息竞赛外有一个特点是其他语言没有的——编译支持非常非常多老旧机器比如 Gameboy 这种上个世纪的任天堂游戏机还有一个用处就是以伪代码的形式Pascal 风格的伪代码出现在各种教科书中。最后Pascal 的圈子其实很小C/C 的圈子很大帮助手册与教程很多很全一定要掌握好英语。世界上还有很多很多编程语言而计算机这门学科与技术不光是信息竞赛和编程语言。本文 Pascal 语言的参考文献Lazarus wikiFree Pascal Reference guide附录Pascal 与 C 运算符与数学函数语法对比表仅包括最常用的运算符与函数。基本算术PascalC加法a ba b减法a - ba - b乘法a * ba * b整除a div ba / b浮点除法a / ba / b取模a mod ba % b说明C 中/究竟是整除还是浮点除法取决于操作数的类型见上文「C 可以自动转换类型」一节这也是 Pascal 的div与/在 C 中合二为一的原因。逻辑| | Pascal | C | | - | --------- | --------------------- | | 非 |not(a)|!a| | 且 |a and b|a b| | 或 |a or b|a || b|比较PascalC相等a ba b不等a ba ! b大于a ba b小于a ba b大于等于a ba b小于等于a ba b赋值PascalCa : ba ba : a ba ba : a - ba - ba : a * ba * ba : a div b或a : a / ba / ba : a mod ba % b自增/自减| | Pascal | C | | -- | -------- | ----- | | 自增 |inc(a)|a| | 自增 |inc(a)|a| | 自减 |dec(a)|a--| | 自减 |dec(a)|--a|数学函数使用需要导入cmath库。PascalC绝对值abs(a)abs(a)整数绝对值abs(a)fabs(a)浮点数$a^b$N/A1pow(a, b)截断取整trunc(a)trunc(a)近似取整round(a)round(a)其他函数请参考 docs/lang/op.md 及 cppreference 的「常用数学函数」页面。结语迁移后的下一步至此你已经完成了从 Pascal 到 C/C 的核心语法迁移从环境搭建、两个入门程序到变量、运算符、条件、循环、数组与字符串、函数与参数传递、类型自动转换再到标准库的使用。接下来可以在 OI-Wiki 的 docs/lang 目录中按需深入输入输出优化可看 docs/contest/io.md竞赛中的常见错误与技巧可看 docs/contest/common-mistakes.md 与 docs/contest/common-tricks.md。把本文的示例代码亲手编译运行、提交到 OJ 并 AC是最快的巩固方式。Extended Pascal 中有a**b不过需要导入Math库。↩【免费下载链接】OI-wiki:star2: Wiki of OI / ICPC for everyone. 某大型游戏线上攻略内含炫酷算术魔法项目地址: https://gitcode.com/GitHub_Trending/oi/OI-wiki创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考