编程语言中语法和语义的区别是什么?

在编程语言(如 C,C + +)中,语法语义学有什么不同?

278025 次浏览

语义就是代码的含义——也就是您可能在伪代码中描述的含义。语法是实际的结构——从变量名到分号,无所不包。

语法指的是一种语言的结构,追踪它的 词源学到事物是如何组合在一起的。
例如,您可能需要通过声明类型、名称和分号来将代码放在一起,以确保语法正确。

Type token;

另一方面,语义学是关于意义的。 编译器或解释器会抱怨语法错误,你的同事会抱怨语义错误。

DR

总之,句法是一个概念,它只关心句子对语言的语法是否有效。语义是关于句子是否有有效的意义。

长话短说:

语法是关于 结构或语言的语法的。它回答了这个问题: 我如何构造一个有效的句子?所有的语言,甚至是英语和其他人类语言(又名“自然”)都有语法,也就是说,规则决定了句子是否构造得当。

下面是一些 C 语言的语法规则:

  • 用分号分隔语句
  • 将 IF 语句的条件表达式放在括号内
  • 用花括号括起来,将多个语句组合成一个语句
  • 数据类型和变量必须在第一个可执行语句之前声明(这个特性在 C99中被删除了)。C99和后者允许混合类型声明。)

语义学是关于句子的 意义的。它回答了以下问题: 这个句子有效吗?如果是这样,这句话是什么意思?例如:

x++;                  // increment
foo(xyz, --b, &qrs);  // call foo

是语法上有效的 C 语句。但它们是什么意思呢?试图将这些语句转换成可执行的指令序列是否有效?这些问题是语义学的核心。

考虑一下第一个语句中的 + + 运算符。首先,尝试这样做是否有效?

  • 如果 x 是一个 float 数据类型,则该语句没有任何意义(根据 C 语言规则) ,因此它是一个错误 尽管语句在句法上是正确的
  • 如果 x 是指向 某种数据类型的指针,那么该语句的意思是“将 sizeof (某种数据类型)添加到地址 x 的值,并将结果存储到地址 x 的位置”。
  • 如果 x 是一个标量,那么这个语句的意思是“将一加到地址 x 的值中,并将结果存储到地址 x 的位置中”。

最后,请注意,有些语义不能在编译时确定,因此必须在运行时进行评估。在 + + 运算符示例中,如果 x 已经处于其数据类型的最大值,那么当您尝试向其添加1时会发生什么?另一个例子: 如果您的程序试图取消引用一个值为 NULL 的指针会发生什么?

维基百科有答案。阅读 语法(编程语言)语义学(计算机科学)维基页面。

或者想想任何 编译器翻译的工作。第一步是 词法分析,其中通过将字符串划分为词素然后是 解析来生成标记,解析构建一些 抽象语法树(这是语法的表示)。接下来的步骤涉及转换或评估这些 AST (语义)。

另外,注意如果你定义了一个 C 的变体,其中每个关键字都被转换成它的法语等价物(所以 if变成了 sido变成了 faireelse变成了 sinon等等。.)你肯定会改变你的语言的语法,但是你不会改变太多的语义: 用法语 C 编程不会更容易!

句法: 它指的是语言的语法结构。.如果你正在写 C 语言。你必须非常小心地使用数据类型,标记[它可以是文字或符号,如“ printf ()”。它有3个 tokes,“ printf,(,)”]。同样,你必须非常小心,如何使用函数,函数语法,函数声明,定义,初始化和调用它。

而语义学则涉及到句子或语句的逻辑或概念。如果你说或写一些概念或逻辑之外的东西,那么你在语义上是错误的。

语法 是表达式、语句和程序单元的结构或形式,而 语义学是这些表达式、语句和程序单元的含义。语义学直接从 语法跟随。 语法 指的是特定编程语言指定的代码的结构/形式,而 语义学则处理分配给符号、字符和单词的含义。

  • 编译时需要正确的 语法
  • 你需要正确的 语义学使它工作。

了解编译器如何看待代码

通常,代码的语法和语义分析是在编译器的“前端”部分完成的。

  • 语法: 编译器为每个关键字和符号生成令牌: 令牌包含关键字的信息类型及其在代码中的位置。 使用这些标记,创建并分析一个 AST (抽象语法树的缩写)。 编译器在这里实际检查的是代码是否具有词法意义,即“关键字序列”是否符合语言规则?正如在前面的答案中所建议的那样,您可以将其视为语言的语法(而不是代码的意义)。 旁注: 在这个阶段报告语法错误。(将错误类型的标记返回给系统)

  • 语义: 现在,编译器将检查代码操作是否“有意义”。 例如:。如果该语言支持类型推断,则如果试图将字符串分配给 float,则会报告语义错误。或者两次声明同一个变量。 这些错误在语法上或语法上是正确的,但在操作过程中没有意义。 注意: 为了检查同一个变量是否声明了两次,编译器管理一个 符号表

因此,这两个前端阶段的输出是一个带注释的 AST (带数据类型)和符号表。

用一种不那么专业的方式去理解它

考虑到我们使用的正常语言; 这里,英语:

不正确的语法/句法,尽管他想传达正确的意义/语义。

例如:。他被冷落了。冷是个形容词。在英语中,我们可能会说这不符合语法,但它实际上是我能想到的最接近不正确语义和正确语法的例子。

他喝米饭(错误的语义-无意义,正确的句法-语法)

喜喝水(正确的语义-有意义,错误的句法-语法)

派对迟到了——但对我来说,这里的答案似乎是正确的,但并不完整。

实际上,我将区分三个层面:

  1. 语法
  2. 低层次的语义
  3. 高级语义学

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 中,这是一个语法正确的赋值语句,但是在语义上它是一个错误,因为它试图将 int赋给 String

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 交易,进入一笔错误方向的交易,错误计算交易规模,等等,那么您就犯了一个高级语义错误。

DR

如果您搞砸了语法或低级语义,您的编译器将会抱怨。

如果您搞砸了高级语义,那么您的程序就不合适,您的客户就会抱怨。