菜单

爱码士
发布于 2026-08-06 / 6 阅读
0
0

语言通用语法基础

这是爱码士的第 6 篇原创文章

这篇文章帮你建立 跨语言的语法直觉,以后学任何新语言,看一眼就能上手。

一. 前言:学语言,先学思维

刚入门的同学常问:"学长,我该先学 Python 还是 C?"
这个问题本身就问错了。语言是工具,语法是思维。你真正要学的不是某个语言怎么写,而是所有语言共通的那套语法骨架
拿这套骨架去学任何语言,无非是查查"这个语言的 if 怎么写"、"它的数组叫什么"——十分钟上手,一小时写代码
这篇文章就是你的"语法骨架"。我会尽量用多种语言对照展示,让你在刚接触就建立这种全局观。

二. 数据类型

变量:给内存起个名字

变量就是一个命名了的箱子,你把值放进去,随时可以拿出来、换掉。
不同语言的变量声明差异很小,本质都一样:

# Python
name = "小明"
age = 20
// JavaScript
let name = "小明";
const age = 20;  // const = 不可变
// Java(静态类型,需要标注类型)
String name = "小明";
int age = 20;
// Go
name := "小明"    // 短声明,自动推断类型
var age int = 20  // 也可以显式写类型
// C(必须声明类型,所有变量集中在函数开头声明)
char name[] = "小明";
int age = 20;

核心认知:无论哪种语言,你都只是在做两件事——声明(告诉编译器有这个变量)+ 赋值(把值放进去)。所谓"强类型语言"不过是在声明时多写一个类型标注罢了。C 语言的变量声明一定要放在函数体的最前面(C99 之后放宽了,但老教材都是这么教的),这是 C 的历史痕迹。

数据类型:语言认识的"几种东西"

所有语言都认这几类数据,名字可能不同,本质一样:

类别

含义

C 示例

Python 示例

Java 示例

JS 示例

整数

没有小数点的数

int x = 42;

42

int x = 42;

let x = 42;

浮点数

带小数点的数

double x = 3.14;

3.14

double x = 3.14;

let x = 3.14;

字符串

文本

char s[] = "hello";

"hello"

String s = "hello";

let s = "hello";

字符

单个字符

char c = 'A';

没有(用长度为 1 的字符串)

char c = 'A';

let c = 'A';

布尔

真/假

int b = 1;(C 没有真正的 bool,用 0/1 代替)

True / False

boolean b = true;

let b = true;

空值

"什么都没有"

NULL

None

null

null / undefined

关键认知:类型分两派——基本类型(值直接存在变量里)和引用类型(变量存的是地址,真实数据在堆里)。这个区别在 Java/C# 里特别重要,Python/JS 帮你在底层屏蔽了,但面试会考。


类型转换:数据总会"变身"

几乎每门语言都有隐式转换(语言帮你自动转)和显式转换(你手动转)。

# Python
x = "10" + str(5)     # 显式:结果 "105"
y = 10 + 5            # 正常加法:15
// JavaScript(隐式转换的"名场面"——坑很多)
"10" + 5;      // "105" —— 自动把 5 变成字符串
"10" - 5;      // 5     —— 自动把 "10" 变成数字
10 == "10";    // true  —— 自动转换后比较(用 === 避免)
// C(有隐式转换,但也支持显式强转)
int x = 10;
double y = (double)x;          // 显式强转:C 风格
// 隐式转换:不同类型运算时自动提升
double z = 10 / 3;             // 3.000000(整数除法再转 double)
double w = 10.0 / 3;           // 3.333333(double 除法)
// Go(完全不搞隐式转换,逼你手动写,安全性最高)
var x int = 10
var y float64 = float64(x)  // 必须显式转换

经验谈:C 的隐式类型提升规则非常复杂(整型提升、算术转换)。JS 的隐式转换是面试重灾区。Go 的严格转换是最安全的设计。你要是写后端,用强类型语言;写脚本/前端,搞清楚隐式转换的规则——别靠运气写代码

三、运算符

算术运算符

这些几乎全语言通用,不存在差异:

加 +    减 -    乘 *    除 /    取余 %
自增 ++(Python 没有)    自减 --(Python 没有)
整除 //(Python 特有,C/Java/JS 中两个整数相除默认就是整除)

C 语言的 ++--前缀后缀的区别,这是初学者最容易掉坑的地方:

// C: 前缀 vs 后缀
int a = 5;
int b = a++;    // b = 5, a = 6(先赋值,再加 1)
int c = ++a;    // c = 7, a = 7(先加 1,再赋值)

比较运算符

等于 ==     不等于 !=     大于 >     小于 <     大于等于 >=     小于等于 <=

大多数语言给你两种"等于":

  • ==比较值是否相等

  • ===(JS 特有)/ .equals()(Java):比较值+类型比较对象内容

逻辑运算符:写给机器看的"条件"

万能三件套:

# Python
and      or      not
// JS / Java / C 系列
&&       ||      !

短路求值(Short-circuit Evaluation)是跨语言通用规则:

# 如果 A 已经是 False,B 根本不会执行——这叫"短路"
result = A and B

# 常用于安全访问:
name = user and user.getName()  # 如果 user 是 None,不会崩

这是性能优化,也是防御性编程基本功,每门语言都支持。

四、控制流

条件判断:让代码学会"选择"

// C
if (score >= 90) {
    grade = 'A';
} else if (score >= 80) {
    grade = 'B';
} else {
    grade = 'C';
}
# Python
if score >= 90:
    grade = "A"
elif score >= 80:
    grade = "B"
else:
    grade = "C"
// JavaScript / Java / C 系列(用花括号代替缩进)
if (score >= 90) {
    grade = "A";
} else if (score >= 80) {
    grade = "B";
} else {
    grade = "C";
}

本质都一样:条件成立→执行这段代码,否则→检查下一个条件。唯一的区别是 Python 用缩进定范围,C 系用 {}。C 的条件必须用括号 () 包起来,这是语法要求,Java/JS 也一样。

循环:让机器替你重复

for 循环——最通用的循环,几乎每个语言都有:

// C
for (int i = 0; i < 5; i++) {
    printf("%d\n", i);        // 0, 1, 2, 3, 4
}
# Python
for i in range(5):
    print(i)   # 0, 1, 2, 3, 4
// JavaScript
for (let i = 0; i < 5; i++) {
    console.log(i);
}
// Java
for (int i = 0; i < 5; i++) {
    System.out.println(i);
}

while 循环——条件成立就一直跑:

// C
while (count > 0) {
    count--;
}
# Python
while count > 0:
    count -= 1

foreach / for-in / for-of——遍历集合的语法糖,几乎所有现代语言都有:

# Python
for item in items:
    print(item)

# JS
for (let item of items) { ... }

# Java
for (String item : items) { ... }

C 语言没有 foreach 语法,遍历数组只能用传统的 for (int i = 0; i < n; i++) 下标方式。

switch / match:多路分支

// C
switch (day) {
    case 1: printf("Monday\n"); break;
    case 2: printf("Tuesday\n"); break;
    default: printf("Other\n");
}
// JavaScript
switch (day) {
    case 1: console.log("Monday"); break;
    case 2: console.log("Tuesday"); break;
    default: console.log("Other");
}
# Python 3.10+(没有传统 switch,用的是 match-case)
match day:
    case 1: print("Monday")
    case 2: print("Tuesday")
    case _: print("Other")

别死记 switch 的 break。理解它的原理:匹配成功后从入口直直往下掉(fall-through),没有 break 就会继续执行下一个 case。C 语言的 switch 是 fall-through 的"始作俑者",面试老掉牙的考点。C 的 switch 只能匹配整数/字符,不能匹配字符串——这和 JS/Java 不同。

五、函数

函数的本质

函数 = 输入 → 处理 → 输出。一个函数只做一件事。

// C(返回值类型 + 参数类型,必须都写清楚)
int add(int a, int b) {
    return a + b;
}
# Python
def add(a, b):
    return a + b
// JavaScript
function add(a, b) {
    return a + b;
}

// 箭头函数(JS 特色,写回调必备)
const add = (a, b) => a + b;
// Java(一切皆类,方法必须属于某个类)
public static int add(int a, int b) {
    return a + b;
}

参数传递:值传递 vs 引用传递

这是跨语言的"必备概念":

  • 值传递:把值复制一份传进去。基本类型都是值传递。函数里改了,外面不变。

  • 引用传递:传的是地址。对象、数组等引用类型。函数里改了,外面也变。

C 语言是纯值传递,所有参数都复制。想在函数里修改外面的变量?必须手动传指针:

// C: 通过指针模拟"引用"效果
void modify(int *p) {
    *p = 100;               // 通过地址间接修改原变量
}

int x = 10;
modify(&x);                 // 传 x 的地址进去
// x 现在是 100
# Python 演示
def modify_list(lst):
    lst.append(4)          # 改的是同一份数据

my_list = [1, 2, 3]
modify_list(my_list)
print(my_list)             # [1, 2, 3, 4] —— 外面也变了!

搞不清这个区别的同学,debug 时间起码翻一倍。C 的指针是理解所有语言引用机制的基石——你理解了 *p&x,Java 的"对象引用"、Python 的"可变对象"都是同一个道理穿上不同的语法外衣。

作用域:变量"活"在哪里

每门语言都有作用域概念:

  • 全局作用域:文件顶部、所有函数之外

  • 函数作用域:函数内部

  • 块作用域{} 内部(JS 的 let/const、Java、C++)

  • 闭包:函数里的函数,内层能访问外层的变量(JS/Python 都有)

# Python 闭包示例
def outer(x):
    def inner(y):
        return x + y    # inner 记住了 outer 的 x
    return inner

add_5 = outer(5)
print(add_5(3))          # 8

六、数组与对象

数组 / 列表

// C: 定长数组,大小在编译时确定
int nums[5] = {1, 2, 3, 4, 5};
nums[0] = 10;                    // 索引从 0 开始
int len = sizeof(nums) / sizeof(nums[0]);  // 数组长度(C 不会自动记录长度)
// 没有 .append()、.push() 这些方法,都是最原始的操作
# Python
nums = [1, 2, 3]
nums.append(4)           # [1, 2, 3, 4]
nums[0]                  # 1(索引从 0 开始)
nums[-1]                 # 4(Python 独有:倒数第一个)
// JavaScript
let nums = [1, 2, 3];
nums.push(4);            // [1, 2, 3, 4]
nums.pop();              // 去掉最后一个
nums.map(x => x * 2);    // [2, 4, 6] —— 函数式操作
// Java
int[] nums = {1, 2, 3};              // 定长数组
ArrayList<Integer> list = new ArrayList<>();  // 动态数组
list.add(4);

共性:索引从 0 开始,有增删改查操作,支持遍历。区别在于动态扩容、方法名、是否支持混合类型。C 语言的数组是最"原始"的——没有内置方法,没有越界检查,数组名就是指向首元素的指针,数组越界是 C 程序崩溃的头号元凶

对象 / 字典 / Map

知道这几种叫法指的都是同一个东西就行:

语言

叫法

C

结构体 struct(C 没有真正的"对象"概念)

Python

字典 dict

JavaScript

对象 {}

Java

Map / HashMap

Go

map / struct

C#

Dictionary

// C: 结构体——只能存数据,不能存"方法"
struct Person {
    char name[20];
    int age;
};
struct Person p = {"小明", 20};
printf("%s\n", p.name);    // "小明"
# Python
person = {"name": "小明", "age": 20}
person["name"]            # "小明"
// JavaScript
let person = {name: "小明", age: 20};
person.name;              // "小明"(点语法)
person["name"];           // "小明"(中括号语法)

现在在每个语言中几乎都衍生出了对象class(类)

对象不可否认的是语言中最优雅的设计,人可以是People对象,狗可以是Dot对象

万物皆可对象

七、异常处理

任何程序都可能出错:文件不存在、网络断了、除数是零。异常处理让你在程序崩之前优雅地兜底

// C: 没有 try/catch,只能靠返回值 + errno 判断
FILE *fp = fopen("data.txt", "r");
if (fp == NULL) {
    perror("打开文件失败");     // 打印错误原因
    return -1;
}
// 用完了,别忘了关
fclose(fp);
# Python
try:
    result = 10 / 0
except ZeroDivisionError:
    print("除数不能为零")
finally:
    print("不管怎样,这行都会执行")
// JavaScript
try {
    let result = 10 / 0;
} catch (error) {
    console.log("出错了:", error.message);
} finally {
    console.log("清理资源");
}
// Java(需要声明可能抛出的异常类型)
try {
    int result = 10 / 0;
} catch (ArithmeticException e) {
    System.out.println("算数错误");
} finally {
    // 关数据库连接、关文件流,写这里
}

finally 是最被低估的关键字。数据库连接、文件句柄、网络套接字的关闭,全部应该放在 finally 里——不管有没有异常,这段代码一定执行。

C 语言没有异常机制,全靠返回值检查。这也是为什么 C 程序里到处都是 if (ptr == NULL) 这样的判断——不检查就崩给你看。现代语言用 try/catch 本质上就是把这种"检查返回值"的模式自动化了。

八、注释与命名

注释

// 单行注释(C99 开始支持)

/*
 * 多行注释
 * C 的传统写法
 */
# 单行注释

"""
多行注释(Python 用三引号)
"""

# 所有语言的单行注释基本都用 // 或 #
# 多行注释各有各的写法

注释的铁律:解释 WHY(为什么这么写),而不是 WHAT(这段代码干了什么)。代码本身已经说了 WHAT,加注释是在浪费时间。

# 差
x = x + 1  # 把 x 加 1

# 好
x = x + 1  # 边界条件的补偿,详见 JIRA-3842

命名规范速查

不同语言有不同的社区规范,但有一些通用的:

语言

变量/函数

类/结构体

常量

C

snake_case

PascalCase(struct 用)

UPPER_SNAKE(宏用)

Python

snake_case

PascalCase

UPPER_SNAKE

JavaScript

camelCase

PascalCase

UPPER_SNAKE

Java

camelCase

PascalCase

UPPER_SNAKE

Go

camelCase

PascalCase

PascalCase

原则就一条:到了哪个语言的地盘,就用哪个语言的规矩。不要把一个语言的命名习惯带到另一个语言里——这是新人最容易被 code review 打回来的问题之一。

九、程序是如何运行的

这是很多同学大一学 C 语言时没搞懂的概念,这里用一句话讲清楚:

  • 编译型(C/C++/Go/Rust):写完之后翻译成机器码,生成 .exe/.out 文件,拿着这个文件到处跑。

  • 解释型(Python/JS/PHP):写完直接交给解释器跑,解释器读一行执行一行。

  • 混合型(Java/C#):先编译成中间码(字节码/IL),再在虚拟机上运行。所以是"编译+解释"。

编译型:源代码 ──编译器──▶ 机器码 ──▶ 直接运行
解释型:源代码 ──解释器──▶ 逐行执行
混合型:源代码 ──编译器──▶ 字节码 ──虚拟机──▶ 执行

这不影响你写代码,但影响你理解为什么 Python 慢、C 快、Java 能跨平台。面试必问。

静态类型 vs 动态类型

静态类型

动态类型

代表语言

C, Java, Go, C++, TS

Python, JS, Ruby

类型检查时机

编译时检查

运行时才报错

优点

早期发现 bug,IDE 提示好

写起来快,灵活

缺点

写起来啰嗦

到了生产环境才炸

折中方案

TypeScript = JS + 静态类型

我的建议:入门最好从 C 语言开始,理解内存和指针是怎么一回事。然后用 Python/JS 感受高层次的编程思维。最后学一门 Go 或 Java——既有静态类型的工程安全感,又不用像 C 那样手动管理内存。等你回头写任何语言的时候,心里已经自带类型检查和内存意识了——这时候你才算真正"会编程"。

十、从语法到思维

  1. 写代码时,先想逻辑,再想语法——逻辑对了,语法只是查文档的问题。

  2. 不要纠结"哪个语言好"——没有最好的语言,只有最合适的场景。但数据结构、算法、设计模式是跨语言通用的,它们才是需要死磕的。

你接下来要学的,不是 C 语言的 *p 怎么写,不是 Python 的 def 怎么拼,不是 Java 的 public static void main 怎么记——而是看到一个需求时,脑中自动浮现出"这里需要循环"、"这里可以用哈希表"、"这函数应该拆成两个"。

那才是编程。


评论