编程语言(如 C、C++)中 的语法和语义有什么区别?
11 回答
TL; 博士
总而言之,句法是只关心句子是否对语言的语法有效的概念。语义是关于句子是否具有有效含义。
长答案:
语法是关于语言的结构或语法的。它回答了这个问题:我如何构造一个有效的句子?所有语言,甚至英语和其他人类(又名“自然”)语言都有语法,即定义句子是否正确构造的规则。
以下是一些 C 语言语法规则:
- 用分号分隔语句
- 将 IF 语句的条件表达式括在括号内
- 通过用花括号括起来将多个语句组合成一个语句
- 数据类型和变量必须在第一个可执行语句之前声明(此功能已在 C99 中删除。C99 及更高版本允许混合类型声明。)
语义是关于句子的含义。它回答了以下问题:这句话有效吗?如果是这样,这句话是什么意思?例如:
x++; // increment
foo(xyz, --b, &qrs); // call foo
是语法上有效的 C 语句。但它们是什么意思?尝试将这些语句转换为可执行的指令序列是否有效?这些问题是语义学的核心。
考虑第一条语句中的 ++ 运算符。首先,尝试这样做是否有效?
- 如果 x 是浮点数据类型,则该语句没有意义(根据 C 语言规则),因此即使该语句在语法上是正确的,它也是一个错误。
- 如果 x 是指向某个数据类型的指针,则该语句的含义是“将 sizeof( some data type ) 添加到地址 x 处的值,并将结果存储到地址 x 处的位置”。
- 如果 x 是标量,则语句的含义是“将地址 x 处的值加一并将结果存储到地址 x 处的位置”。
最后,请注意,某些语义无法在编译时确定,因此必须在运行时进行评估。在 ++ 运算符示例中,如果 x 已经是其数据类型的最大值,那么当您尝试将 1 添加到它时会发生什么?另一个例子:如果你的程序试图取消引用一个值为 NULL 的指针会发生什么?
句法是指一种语言的结构,将其词源追溯到事物的组合方式。
例如,您可能需要通过声明类型、名称和分号来将代码放在一起,以确保语法正确。
Type token;
另一方面,语义是关于意义的。编译器或解释器可能会抱怨语法错误。你的同事会抱怨语义。
语义是你的代码的意思——你可能用伪代码描述的东西。语法是实际的结构——从变量名到分号的所有内容。
- 您需要正确的语法来编译。
- 您需要正确的语义才能使其工作。
语法是表达式、语句和程序单元的结构或形式,但语义是这些表达式、语句和程序单元的含义。语义直接来自语法。 语法是指特定编程语言指定的代码结构/形式,但语义处理分配给符号、字符和单词的含义。
派对迟到了——但对我来说,这里的答案似乎正确但不完整。
务实地,我会区分三个层次:
- 句法
- 低级语义
- 高级语义
1. 语法
语法是语言的形式语法,它指定编译器将识别的格式良好的语句。
所以在 C 中,变量初始化的语法是:
data_type variable_name = value_expression;
例子:
int volume = 66 * 22 * 55;
在提供类型推断的 Go 中,一种初始化形式是:
variable_name := value_expression
例子:
volume := 66 * 22 * 55
显然,Go 编译器无法识别 C 语法,反之亦然。
2. 低级语义
句法与形式有关,语义与意义有关。
在自然语言中,一个句子在句法上可能是正确的,但在语义上是无意义的。例如:
The man bought the infinity from the store.
这句话在语法上是正确的,但在现实世界中没有意义。
在底层,编程语义关注的是语法正确的语句是否也符合开发人员使用语言类型系统表达的语义规则。
例如,这是 Java 中语法正确的赋值语句,但在语义上它是错误的,因为它试图将 an 分配int
给 aString
String firstName = 23;
因此,类型系统旨在保护开发人员免于在低级别出现意外的含义失误。
像 JavaScript 或 Python 这样的松散类型语言提供的语义保护非常少,而像 Haskell 或 F# 这样的具有表达类型系统的语言为熟练的开发人员提供了更高级别的保护。
例如,在 F# 中,您的 ShoppingCart 类型可以指定购物车必须处于以下三种状态之一:
type ShoppingCart =
| EmptyCart // no data
| ActiveCart of ActiveCartData
| PaidCart of PaidCartData
现在编译器可以检查您的代码是否没有尝试将购物车置于非法状态。
在 Python 中,您必须编写自己的代码来检查有效状态。
3. 高级语义
最后,在更高的层次上,语义关注的是代码要达到的目的——编写程序的原因。
这可以表示为可以用任何完整语言实现的伪代码。例如:
// Check for an open trade for EURUSD
// For any open trade, close if the profit target is reached
// If there is no open trade for EURUSD, check for an entry signal
// For an entry signal, use risk settings to calculate trade size
// Submit the order.
在这个(非常简化的)场景中,如果您的系统同时输入两笔 EURUSD 交易、输入方向错误的交易、错误计算交易规模等,您将犯下高级语义错误。
TL; 博士
如果你搞砸了你的语法或低级语义,你的编译器会抱怨。
如果你搞砸了你的高级语义,你的程序就不适合你的目的,你的客户会抱怨。
他喝米饭(错误的语义-无意义,正确的句法-语法)
嗨喝水(正确的语义-有意义,错误的语法-语法)
了解编译器如何看待代码
通常,代码的语法和语义分析是在编译器的“前端”部分完成的。
语法:编译器为每个关键字和符号生成标记:标记包含关键字的信息类型及其在代码中的位置。使用这些标记,可以创建和分析 AST(抽象语法树的缩写)。编译器在这里实际检查的是代码是否具有词法意义,即“关键字序列”是否符合语言规则?正如先前答案中所建议的,您可以将其视为语言的语法(而不是代码的意义/含义)。旁注:在此阶段报告语法错误。(将具有错误类型的令牌返回给系统)
语义:现在,编译器将检查您的代码操作是否“有意义”。例如,如果语言支持类型推断,如果您尝试将字符串分配给浮点数,则会报告语义错误。或两次声明相同的变量。这些是“语法上”/语法上正确的错误,但在操作期间没有任何意义。旁注:为了检查同一个变量是否被声明了两次,编译器管理一个符号表
因此,这两个前端阶段的输出是带注释的 AST(带有数据类型)和符号表。
以较少技术性的方式理解它
考虑到我们使用的正常语言;在这里,英语:
例如,他去学校。- 不正确的语法/句法,尽管他想传达正确的意义/语义。
例如,他感冒了。- 寒冷是一个形容词。在英语中,我们可能会说这不符合语法,但它实际上是我能想到的最接近错误语义和正确语法的例子。
语法:它指的是语言的语法结构。如果您正在编写 c 语言。您必须非常小心地使用数据类型、标记 [它可以是文字或符号,如“printf()”。它有 3 个令牌,“printf, (, )”]。同样,你必须非常小心,你如何使用函数、函数语法、函数声明、定义、初始化和调用它。
而语义,它涉及句子或语句的逻辑或概念。如果你说或写了一些超出概念或逻辑的东西,那么你在语义上是错误的。
编程语言的语法是其表达式、语句和程序单元的形式。它的语义是那些表达式、语句和程序单元的含义。例如,Java while 语句的语法是
while (boolean_expr) statement
这种语句形式的语义是当布尔表达式的当前值为真时,执行嵌入的语句。然后控制隐式返回布尔表达式以重复该过程。如果布尔表达式为假,则控制转移到 while 构造之后的语句。