C++ 基本语法

c++基础 › C++ 基本语法 (第 4 章)
文章工具

写 C++ 不是把命令排成一串,而是先描述出”一类东西长什么样”,再让这些具体的东西互相调用、协作。

学完你能做什么

  • 能独立写出、编译、运行一个最小 C++ 程序,并逐行说明它在做什么。
  • 能用自己的话讲清「类 / 对象 / 方法 / 实例变量」四者的关系,并写出一个含两个对象的程序。
  • 能区分语言规则与实现细节:比如分号是标准要求的,而可执行文件叫什么名字是编译器给你的默认值。
  • 能判断一个名字是否合法,并解释编译器为什么需要空格和分号来切分代码。

先建立直觉:图纸、实物、动作、状态

先别急着看语法。想象一家家具厂:

  • 类是图纸。图纸本身不是椅子,但它规定了”有四条腿、有靠背”。
  • 对象是按图纸做出来的那把具体的椅子。同一张图纸能做很多把,每把可以漆成不同颜色。
  • 方法是对象会做的动作,比如”摇尾巴”。
  • 实例变量是对象的状态,比如”颜色是灰色”。原文写作”即时变量”,它其实是英文 instance variable 的另一种译法,读作”实例变量”更准确:每个对象各有一份自己的副本,所以两只狗可以有不同年龄。

一句话:类描述”是什么样”,对象是”具体哪一个”,方法做动作,实例变量记状态。

从最简单的用法开始

任何语言都从最小的例子开始。把下面内容存为 hello.cpp:

C++ 示例

▶ 在线运行:改一改再点“运行”试试(本站沙箱执行,登录后可运行)。

编译并运行:

$ g++ hello.cpp -o hello
$ ./hello
Hello World

逐行看:

  • #include <iostream>:在编译前由预处理器把头文件的内容整段”拷贝”进来,于是我们才能用 cout。它不是 C++ 语句,所以不加分号。
  • using namespace std;:声明”下面的名字去 std 这个命名空间里找”。命名空间是一层标签,用来避免不同库里的同名东西撞车。
  • int main():主函数。标准规定程序从 main 开始执行,这是语言规则,不是某平台的习俗。
  • cout << "Hello World" << endl;:cout 是标准输出流对象。<< 在这里表示”把右边送进左边”,它实际调用了一个叫 operator<< 的函数——这正好印证了”对象通过方法交互”。
  • return 0;:把 0 交给操作系统,双方约定 0 表示成功。
再往下看:编译器到底做了什么(示意图)
图:再往下看:编译器到底做了什么
步骤做什么典型输入 → 输出常见报错
预处理展开 #include、展开宏、去掉注释源文件 + 头文件 → 单个翻译单元的纯文本找不到头文件:fatal error: iostream: No such file or directory
编译真正”读懂”语法,翻译成汇编预处理后的文本 → 汇编代码语法错误,如 error: expected ';',且行号常指向下一行
汇编把汇编变成机器码,产出目标文件汇编代码 → 目标文件 .o较少见,多为汇编语法/指令问题
链接把目标文件与标准库拼起来,解析未定义符号.o + 库 → 可执行文件只声明未定义:undefined reference to ...

口诀:预处理”抄”、编译”懂”、汇编”翻”、链接”拼”;报错行号只是线索,不是结论。

再往下看:编译器到底做了什么

你在命令行敲一次 g++,背后其实是四步:

  1. 预处理:展开 #include、去掉注释、处理宏。
  2. 编译:把预处理后的文本翻译成汇编代码,这一步才真正”读懂”语法。
  3. 汇编:把汇编变成机器码,得到目标文件(如 .o)。
  4. 链接:把目标文件和标准库拼在一起,生成可执行文件。

由此可以解释几个常见疑问:

  • 为什么少写分号,报错却指向下一行?因为编译器读到语句的一部分后还在等结束符号,直到读到下一个记号才发现不对,此时它已经翻页了。所以报错行号只是线索,不是结论。
  • 为什么输出文件名可以随便改?因为”叫什么名字”由 -o 决定,属于实现细节;而”语句以分号结束””标识符区分大小写”属于语言规则,换任何编译器都一样。
  • 为什么 int 和变量名之间必须有空格?因为编译器先把源码切成一个个记号(token)。intage 会被看成一个标识符,而不是两个词;空格在这里的作用是”划边界”。反过来,运算符两侧的空格只是给人看的,x=y+1; 与 x = y + 1; 完全等价。

标识符的规则同理:必须以字母或下划线开头,后面才可以是字母、数字、下划线。允许数字但不能开头,是因为 3x 会和数字字面量打架;不允许 @ $ %,是因为它们要么在别处另有含义,要么根本不在源字符集里。

项目属于哪一类换个编译器/标准版本会变吗
程序从 main 开始执行,且 main 返回 int语言规则不变,任何编译器都一样
语句以分号结束;换行只是普通空白语言规则不变
标识符区分大小写、不能以数字开头、不能占用关键字语言规则不变
可执行文件叫什么名字(由 -o 决定;不写时默认 a.out)实现细节会变,属于编译器给的默认值
void main() 能否通过编译实现细节(非标准扩展)会变,标准只保证 int main
三字符组(如 ??= 表示 #)曾是语言规则,C++17 起已被移除随所用标准版本而变

要点:先问一句”这是标准要求的,还是编译器给的默认值?”——规则不会变,细节随环境变。

对比:几组最容易混的概念

容易混的一对区别口诀
类 vs 对象类是描述,对象是实例,一个类可以造出很多对象图纸不是椅子
语句 vs 块语句以分号结束;块用 { } 把若干语句捆成一组分号断句,花括号打包
关键字 vs 标识符关键字由语言独占(如 class、return),标识符是你自己起的名字公家的名字不能占
换行 vs 分号换行只是空白;一行可写多条语句,一条语句也可跨多行断句靠分号,不靠回车

实战:把”直觉”跑起来

C++ 示例

编译运行:

$ g++ dog.cpp -o dog && ./dog
woof
1 5

请留意 1 5:改 b.age 没有影响 a.age,因为实例变量是”每个对象一份”,这正是”对象拥有自己状态”的含义。

易错点(先错后对)

  • 错:cout << "hi" 后面忘记分号。对:cout << "hi";。
  • 错:只用 cout,却不写 using namespace std;,也不加 std::。对:写成 std::cout,或加上 using namespace std;。错误信息说”未声明”,其实是在说”这个名字我找不到”。
  • 错:void main()。对:int main()。标准规定 main 返回 int,void main 只是某些编译器额外容忍。
  • 错:把变量命名为 class 或 2name。对:换一个不以数字开头、也不是关键字的名字,如 className。

再深入一点:三字符组,以及”规则会变”

早期键盘常打不出 #、{ 这类符号,于是 C++ 允许用三个字符的组合代替一个字符,这种组合叫三字符组,例如 ??= 表示 #,??< 表示 {。

它的副作用很容易咬人:??/ 会被替换成反斜杠 \。若它恰好出现在 // 注释末尾,反斜杠会让这行注释”续”到下一行,把下一行代码一起注释掉——代码凭空消失,而你完全看不出原因。

好消息是:三字符组已在 C++17 中被移除。这个例子真正值得记住的不是几个符号,而是:语言规则本身会随标准演进。以后遇到”书上和我这里不一样”,先问一句”用的哪个标准版本”。

自测练习

  1. 下面哪些是合法标识符?_temp、2ndTry、move_name、double、my name。
  2. 把三条语句写到同一行,语义会变吗?为什么?
  3. 给 Dog 类加一个 std::string name,让 bark() 输出”名字 + woof”,并说明为什么两个对象互不影响。
参考答案

1. 合法的是 _temp 和 move_name。2ndTry 以数字开头,不允许;double 是关键字;my name 含空格,会被切成两个记号。

2. 不会变。例如 x = y; y = y + 1; add(x, y); 写在一行里,仍从左到右依次执行,因为分号才是分隔符,换行只是空白。

3. 例如在类里加 std::string name;,把 bark() 改成 std::cout << name << ": woof\n";。每个对象内部各自保存一份 name,a.name 与 b.name 是两块不同的存储,所以互不影响。

一句话记住

类是图纸,对象是实物;方法管动作,实例变量管状态;分号断句,花括号打包——这些细节最终都能回到同一件事:编译器要先把源码切成记号,再翻译成机器码。

延伸阅读

⚠️ 版权声明

本文为免费文章,欢迎转载。转载请注明出处:一木科技(https://www.pingquan.vip/2026/09/24/cpp-basic-syntax/),并保留原文链接。