跳转至

Pascal 轉 C++ 急救

用來急救,不多廢話。

藥方食用提示

本急救貼可以讓您充分了解以下內容(對應 C++ 語法快速提要):

  • 基本語法(塊語句、註釋、導入庫、簡單輸入輸出、聲明變量、賦值……)
  • C++ 的 Hello World 與 A+B Problem 寫法與解釋

    對應語法 部分較為緊湊,正式食用可能需要額外參考資料(已給出)。此部分不包括指針與 C 風格數組的介紹,也沒有結構體、運算符重載等等。

    重要不同之處 部分為 C++ 的語法特點,也是 Pascal 轉 C++ 時會碰到的坑。

如要快速查找,請見附錄:

C++ 快速安裝與環境配置

注意:這裏假設使用的系統是 Windows。

方式一:使用 IDE

以下 IDE 選擇一個即可:

方式二:使用 代碼編輯器 + 編譯器 + 調試器

  • VS Code

    Visual Studio Code 官方網站上有文檔解釋如何進行 C++ 的配置。一般而言 VS Code 搭配插件使用更方便,見 VS Code 的官方網站

    C++ 語法快速提要 Start Here

C++ 程序都是從 main 這個部分開始運行的。

大括號表示塊語句的開始與結束: { 就相當於 Pascal 裏面的 begin ,而 } 就相當於 end

注意,和 Pascal 一樣,C++ 每句話結束要加分號 ; ,不過大括號結尾不需要有分號,而且程序結束末尾不用打句號 .

// 表示行內註釋, /* */ 表示塊註釋。

按照慣例,看看 Hello World 吧。

Hello World:第一個 C++ 程序

1
2
3
4
5
6
7
8
#include <iostream>  // 導入 iostream 庫

int main()  // main 部分
{
  std::cout << "Hello World!" << std::endl;

  return 0;
}

然後編譯運行一下,看看結果。

簡要解釋

第一行, #include <iostream> 的意思是,導入 iostream 這個庫。

Pascal 的庫文件

Pascal 其實是有庫文件的,只不過,很多同學從來都沒有用過……

看到第三行的 main 嗎?程序從 main 開始執行。

接下來最重要的一句話是

1
std::cout << "Hello World!" << std::endl;

std::cout 是輸出( cout 即 C-out)的命令。你可能看過有些 C++ 程序中直接寫的是 cout

有關 std:: 前綴

有關 std:: 這個前綴的問題,請見 這節 底下的註釋「什麼是 std?」。

中間的 << 很形象地表示流動,其實它就是表示輸出怎麼「流動」的。這句代碼的意思就是, "Hello World!" 會先被推到輸出流,之後 std::endl 再被推到輸出流。

std::endl輸出 換行( endl 即 end-line)命令,這與 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[])

簡單練習

  1. 試着換個字符串輸出。
  2. 試着瞭解轉義字符。

A+B Problem:第二個 C++ 程序

經典的 A+B Problem。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#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), >> 也與輸出語法的類似。

這裏多出來的語句中最重要的是兩個,一個是變量聲明語句

1
int a, b, c;

你可能習慣於 Pascal 裏面的聲明變量

1
2
var
a, b, c: integer;

C++ 的聲明是直接以數據類型名開頭的,在這裏, int (整型)開頭表示接下來要聲明變量。

接着一個最重要的語句就是賦值語句

1
c = a + b;

這是 Pascal 與 C++ 語法較大的不同, 這是個坑 :Pascal 是 := ,C++ 是 = ;而 C++ 判斷相等是 ==

C++ 也可直接在聲明時進行變量初始化賦值

1
int a = 0, b = 0, c = 0;

簡單練習

  1. 重寫一遍代碼,提交到 OJ 上,並且 AC。
  2. 更多的輸入輸出語法參考 這節內容 ,並試着瞭解 C++ 的格式化輸出。

結束語與下一步

好了,到現在為止,你已經掌握了一些最基本的東西了,剩下就是找 Pascal 和 C++ 裏面對應的語法和不同的特徵。

不過在此之前,強烈建議先看 變量作用域:全局變量與局部變量 ,也可使用 附 B:文章檢索 查閲閲讀。

請善用Alt+Alt+返回跳轉。

對應語法 Syntax

變量 Variable

基本數據類型 Fundamental types

C++ 與 Pascal 基本上差不多,常見的有

  • bool Boolean 類型
  • int 整型
  • 浮點型
    • float
    • double
  • char 字符型
  • void 無類型

C++ 的單引號是專門用於表示單個字符的(字符型),比如 'a' ,而字符串(字符型數組)必須要用雙引號。

C++ 還要很多額外的數據類型,請參考更多資料。

擴展閲讀:

常量聲明 Constant

1
const double PI = 3.1415926;

若不清楚有關宏展開的問題,建議使用常量,而不用宏定義。

運算符 Operator

請直接參考

條件

if 語句

1
2
3
4
5
6
7
8
if (a = b) and (a > 0) and (b > 0) then
    begin
        b := a;
    end
else
    begin
        a := b;
    end;
1
2
3
4
5
if (a == b && a > 0 && b > 0) {
  b = a;
} else {
  a = b;
}

布爾運算與比較

  • and -> &&
  • or -> ||
  • not -> !
  • = -> ==
  • <> -> !=

註釋:

  1. Pascal 中 and 與 C++ 中 && 優先級不同,C++ 不需要給判斷條件加括號。
  2. Pascal 中判斷相等是 = ,賦值是 := ;C++ 中判斷相等是 == ,賦值是 =
  3. 如果在 if 語句的括號內寫了 a = b 而不是 a == b ,程序不會報錯,而且會把 b 賦值給 aa = b 這個語句的返回結果是 true
  4. C++ 不需要思考到底要不要在 end 後面加分號。
  5. C++ 布爾運算中,非布爾值可以自動轉化為布爾值。
易錯提醒

特別注意: 不要把 == 寫成 =

由於 C/C++ 比 Pascal 語法靈活,如果在判斷語句中寫了 if (a=b) { ,那麼程序會順利運行下去,因為 C++ 中 a=b 是有返回值的。

caseswitch

用到得不多,此處不詳細展開。

需要注意:C++ 沒有 1..n ,也沒有連續不等式(比如 1 < x < 2 )。

循環 Loop

以下三種循環、六份代碼實現的功能是一樣的。

while 循環

while 很相似。(C++ 此處並非完整程序,省略一些框架模板,後同)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
var i: integer;

begin
    i := 1;
    while i <= 10 do
        begin
            write(i,' ');
            inc(i); // 或者 i := i + 1;
        end;
end.
1
2
3
4
5
int i = 1;
while (i <= 10) {
  std::cout << i << " ";
  i++;
}

for 循環

C++ 的 for 語句非常不同。

1
2
3
4
5
6
7
8
var i: integer;

begin
    for i:= 1 to 10 do
        begin
            write(i, ' ');
        end;
end.
1
2
3
for (int i = 1; i <= 10; i++) {
  std::cout << i << " ";
}

註釋:

  1. for (int i = 1; i <= 10; i++){ 這一行語句很多, for 中有三個語句。
  2. 第一個語句 int i = 1; 此時聲明一局部變量 i 並初始化。(這個設計比 Pascal 要合理得多。)
  3. 第二個語句 i <= 10; 作為判斷循環是否繼續的標準。
  4. 第三個語句 i++ ,在每次循環結尾執行,意思大約就是 Pascal 中的 inc(i) ,此處寫成 ++i 也是一樣的。 i++++i 的區別請參考其他資料。

repeat untildo while 循環

注意, repeat untildo while 是不同的,請對比以下代碼

1
2
3
4
5
6
7
8
9
var i: integer;

begin
    i := 1;
    repeat
        write(i, ' ');
        inc(i);
    until i = 11;
end.
1
2
3
4
5
int i = 1;
do {
  std::cout << i << " ";
  i++;
} while (i <= 10);

循環控制 Loop Control

C++ 中 break 的作用與 Pascal 是一樣的,退出循環。

continue 也是一樣的,跳過當前循環,進入下一次循環(回到開頭)。

數組與字符串 Array and String

不定長數組:標準庫類型 Vector

C++ 標準庫中提供了 vector ,相當於不定長數組,調用前需導入庫文件。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#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 可以進行的操作有些相同,同樣需要導入庫文件。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#include <iostream>
#include <string>

int main() {
  std::string s;  // 聲明 string s

  std::cin >> s;  // 讀入 s;
  // 讀入時會忽略開頭所有空格符(空格、換行符、製表符),讀入的字串直到下一個空格符為止。

  std::cout << s << std::endl;

  return 0;
}

擴展閲讀:

C 風格數組 Array

如果要用不定長的數組請用 Vector,不要用 C 風格的數組。

C 風格的數組與指針有密切關係,所以此處不多展開。

擴展閲讀:

重要不同之處 Differences

變量作用域 Scope:全局變量與局部變量

C++ 幾乎可以在 任何地方 聲明變量。

以下對於 C++ 的變量作用域的介紹摘自 變量作用域 - OI Wiki

作用域是變量可以發揮作用的代碼塊。

變量分為全局變量和局部變量。在所有函數外部聲明的變量,稱為全局變量。在函數或一個代碼塊內部聲明的變量,稱為局部變量。

全局變量的作用域是整個文件,全局變量一旦聲明,在整個程序中都是可用的。

局部變量的作用域是聲明語句所在的代碼塊,局部變量只能被函數內部或者代碼塊內部的語句使用。

由一對大括號括起來的若干語句構成一個代碼塊。

1
2
3
4
5
6
int g = 20;  // 聲明全局變量
int main() {
  int g = 10;         // 聲明局部變量
  printf("%d\n", g);  // 輸出 g
  return 0;
}

在一個代碼塊中,局部變量會覆蓋掉同名的全局變量,比如上面的代碼輸出的 g 就是 10 而不是 20 。為了防止出現意料之外的錯誤,請儘量避免局部變量與全局變量重名的情況。

在寫 Pascal 過程/函數時,容易忘記聲明局部變量 i 或者 j ,而一般主程序裏會有循環,於是大部分情況下 ij 都是全局變量,於是,在這種情況下,過程/函數中對 i 操作極易出錯。更要命的是,如果忘記聲明這種局部變量,編譯器編譯不報錯,程序可以運行。(有很多難找的 bug 就是這麼來的。)

所以,在使用 C++ 時,聲明變量,比如循環中使用的 i不要用全局變量,能用局部變量就用局部變量 。如果這麼做,不用擔心函數中變量名(比如 i )衝突。

額外注

Pascal 可在某種程度上避免這個問題,仿照 C++ 的方法,主程序只有調用過程/函數,不聲明 i j 這類極易名稱衝突的全局變量,如果需要循環,另寫一個過程進行調用。

C++ 可以自動轉換類型

1
2
3
4
5
6
int i = 2;
if (i) {  // i = 0 會返回 false,其餘返回 true
  std::cout << "true";
} else {
  std::cout << "false";
}

不光是 int 轉成 bool ,還有 intfloat 相互轉換。在 Pascal 中可以把整型賦給浮點型,但不能反過來。C++ 沒有這個問題。

1
2
3
int a;
a = 3.2;      // 此時 a = 3
float b = a;  // 此時 b = 3.0

區分 / 是整除還是浮點除法,是通過除數與被除數的類型判斷的

1
2
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\) ,該函數返回的是浮點型,如果直接用來計算整數的冪,由於有自動轉換,不需要擔心它會報錯

1
int a = pow(2, 3);  // 計算 2^3

還有 charint 之間相互轉換。

1
2
3
char a = 48;              // ASCII 48 是 '0'
int b = a + 1;            // b = 49
std::cout << (a == '0');  // true 輸出 1

其實 C++ 中的 charbool 本質上是整型。

擴展閲讀:

C++ 很多語句有返回值:以如何實現讀取數量不定數據為例

有些時候需要讀取到數據結束,比如,求一組不定數量的數之和(數據可以多行),直到文件末尾,實現方式是

文件末尾 EOF

EOF,文件末尾標識符,在命令行中 Windows 上以Ctrl+Z輸入(還需按Enter),*unix 系統以Ctrl+D輸入。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#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,使得循環中斷。

函數 Function:C++ 只有函數沒有過程但有 void ,沒有函數值變量但有 return

Pascal 函數與 C++ 函數對比示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
function abs(x:integer):integer;
begin
    if x < 0 then
        begin
            abs := -x;
        end
    else
        begin
            abs := x;
        end;
end;
1
2
3
4
5
6
7
int abs(int x) {
  if (x < 0) {
    return -x;
  } else {
    return x;
  }
}

C++ 中函數聲明 int abs ,就定義了 abs() 函數且返回值為 int 型(整型),函數的返回值就是 return 語句給出的值。

如果不想有返回值(即 Pascal 的「過程」),就用 voidvoid 即「空」,什麼都不返回。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
var ans: integer;

procedure printAns(ans:integer);
begin
    writeln(ans);
end;

begin
    ans := 10;
    printAns(ans);
end.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#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 。比如(由於實在想不出來簡短且實用的代碼,所以就先這樣)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#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 函數,這樣才是嚴格等效的

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
function abs(x:integer):integer;
begin
    if x < 0 then
        begin
            abs := -x; exit; // !注意此處
        end
    else
        begin
            abs := x;  exit; // !注意此處
        end;
end;
1
2
3
4
5
6
7
int abs(int x) {
  if (x < 0) {
    return -x;
  } else {
    return x;
  }
}
特別提醒

C++ 中 exit 是退出程序;不要順手把 exit 打上去,要用 return

C++ 把函數和過程統統視作函數,連 main 都不放過,比如寫 int main ,C++ 視 main 為一個整型的函數,這裏返回值是 0 。它是一種習慣約定,返回 0 代表程序正常退出。

也許你已經猜到了, main(int argc, char const *argv[]) 中的參數就是 int argcchar const *argv[] ,不過意義請參考其他資料。

在函數中傳遞參數 Passing Parameters to Functions

C++ 中沒有 Pascal 的 var 關鍵字可以改變傳遞的參數,但是 C++ 可以使用引用和指針達到同樣的效果。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
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.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// 使用指針的代碼
#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++ 代碼 涉及指針問題 。指針問題還是很麻煩的,建議去閲讀相關資料。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// 使用引用的代碼
#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++ 標準庫與參考資料 Reference

千萬不要重複造輪子(除非為了練習),想要自己動手寫一個功能出來之前,先去看看有沒有這個函數或者數據結構。

C++ 標準庫

C++ 標準庫中 <algorithm> 有很多有用的函數比如快排、二分查找等,可以直接調用。請參考這個頁面: STL 算法 - OI Wiki

還有 STL 容器,比如數組、向量(可變大小的數組)、隊列、棧等,附帶很多函數。請參考這個頁面: STL 容器簡介 - OI Wiki

如果要找關於字符串操作的函數見

C/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 語言的參考文獻

附 A:Pascal 與 C++ 運算符與數學函數語法對比表 Pascal vs C++ Operator Syntax Table

僅包括最常用的運算符與函數。

基本算術

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 a || b

比較

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 ba := 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/A (*) pow(a, b)
截斷取整 trunc(a) trunc(a)
近似取整 round(a) round(a)

*Extended Pascal 中有 a**b 不過需要導入 Math 庫。

其他函數請參考:

附 B:文章檢索 Index

按 C++ 語句語法索引。