C++ 基本语法
写 C++ 不是把命令排成一串,而是先描述出”一类东西长什么样”,再让这些具体的东西互相调用、协作。
学完你能做什么
- 能独立写出、编译、运行一个最小 C++ 程序,并逐行说明它在做什么。
- 能用自己的话讲清「类 / 对象 / 方法 / 实例变量」四者的关系,并写出一个含两个对象的程序。
- 能区分语言规则与实现细节:比如分号是标准要求的,而可执行文件叫什么名字是编译器给你的默认值。
- 能判断一个名字是否合法,并解释编译器为什么需要空格和分号来切分代码。
先建立直觉:图纸、实物、动作、状态
先别急着看语法。想象一家家具厂:
- 类是图纸。图纸本身不是椅子,但它规定了”有四条腿、有靠背”。
- 对象是按图纸做出来的那把具体的椅子。同一张图纸能做很多把,每把可以漆成不同颜色。
- 方法是对象会做的动作,比如”摇尾巴”。
- 实例变量是对象的状态,比如”颜色是灰色”。原文写作”即时变量”,它其实是英文
instance variable的另一种译法,读作”实例变量”更准确:每个对象各有一份自己的副本,所以两只狗可以有不同年龄。
一句话:类描述”是什么样”,对象是”具体哪一个”,方法做动作,实例变量记状态。
从最简单的用法开始
任何语言都从最小的例子开始。把下面内容存为 hello.cpp:
▶ 在线运行:改一改再点“运行”试试(本站沙箱执行,登录后可运行)。
编译并运行:
$ 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++,背后其实是四步:
- 预处理:展开
#include、去掉注释、处理宏。 - 编译:把预处理后的文本翻译成汇编代码,这一步才真正”读懂”语法。
- 汇编:把汇编变成机器码,得到目标文件(如
.o)。 - 链接:把目标文件和标准库拼在一起,生成可执行文件。
由此可以解释几个常见疑问:
- 为什么少写分号,报错却指向下一行?因为编译器读到语句的一部分后还在等结束符号,直到读到下一个记号才发现不对,此时它已经翻页了。所以报错行号只是线索,不是结论。
- 为什么输出文件名可以随便改?因为”叫什么名字”由
-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 分号 | 换行只是空白;一行可写多条语句,一条语句也可跨多行 | 断句靠分号,不靠回车 |
实战:把”直觉”跑起来
编译运行:
$ 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 中被移除。这个例子真正值得记住的不是几个符号,而是:语言规则本身会随标准演进。以后遇到”书上和我这里不一样”,先问一句”用的哪个标准版本”。
自测练习
- 下面哪些是合法标识符?
_temp、2ndTry、move_name、double、my name。 - 把三条语句写到同一行,语义会变吗?为什么?
- 给
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/),并保留原文链接。
还没有评论,来说两句?