标识符、关键字和预定义标识符

好的,我们来逐一详细解答这些问题。这些是C语言乃至很多编程语言中非常核心的基础概念。


1. 标识符、关键字和预定义标识符的区别

  • 标识符
    • 定义:由程序员自行定义的名称,用于命名变量、函数、数组、结构体等各种程序实体。
    • 规则:通常以字母或下划线 _ 开头,后跟字母、数字或下划线。(注意:C语言标准规定标识符不能以数字开头)。
    • 示例myVariable, sum, _count, MAX_SIZE
  • 关键字
    • 定义:也称为“保留字”。是语言本身预先定义好的、具有特殊含义和功能的单词。
    • 特点绝对不能用作标识符。编译器会将这些词作为指令来处理。
    • 示例int, if, else, for, while, return, sizeof
  • 预定义标识符
    • 定义:在标准库中预先定义好的标识符,通常用于库函数名或宏名。
    • 特点理论上你可以用自己的标识符去覆盖它们(重定义),但强烈不建议这样做,否则会导致对应的库函数无法使用,引发混乱和难以调试的错误。
    • 示例printf, scanf, main, EOF, NULL
特性标识符关键字预定义标识符
定义者程序员语言标准标准库
是否可自定义 (保留字)技术上可以,但绝对不要
用途命名变量、函数等构成程序的控制结构、类型等提供标准库功能
示例myVarint, ifprintf, main

核心区别:关键字是语言的“语法骨架”,不可更改;标识符是程序员的“命名标签”,自由定义;预定义标识符是标准库的“工具名称”,最好不要去重新定义它们。


2. 语法检查 vs. 语义检查

这两个检查是编译过程中的重要阶段。

  • 语法检查
    • 检查内容:程序代码的结构是否符合语言的文法规则。它只关心形式,不关心含义。
    • 类比:检查一句英语的单词顺序、标点符号是否正确。例如,“Apple red is.” 在语法上是错误的(正确的语法应该是“Apple is red.”)。
    • 编译器行为:发现语法错误会报错(如 error: expected ';' before '}' token)。
    • 示例:缺少分号 ;、括号不匹配、关键字拼写错误。
  • 语义检查
    • 检查内容:在语法正确的基础上,检查代码的含义是否合法、是否有意义。
    • 类比:检查一句语法正确的英语是否有逻辑错误。例如,“The rock is eating an apple.” 语法完全正确,但语义荒谬(石头不会吃苹果)。
    • 编译器行为:发现语义错误会报错或警告(如 warning: assignment to 'int' from 'float' truncates value)。
    • 示例:类型不匹配、使用未声明的变量、给函数传入不匹配的参数、除数为零(常量情况下)。
方面语法检查语义检查
关心什么形式(Form)含义(Meaning)
检查层次表面结构深层逻辑
类比单词拼写和句子结构句子的逻辑和常识
错误示例int a = 5 (缺分号)int b = "hello"; (类型不匹配)

核心区别:语法是关于“写法对不对”,语义是关于“意思通不通”。


3. 表达式、语句和代码块

  • 表达式
    • 定义:由运算符、操作数和函数调用组成的有值的代码单元。表达式的主要目的是计算出一个值
    • 示例
      • 5 + 3 * 2 (值为11)
      • x = 10 (值为10,同时有副作用)
      • printf("Hello") (值为返回值,即打印的字符数)
      • x (值为变量x的值)
      • a > b (值为1或0)
  • 语句
    • 定义:构成程序执行流程的完整指令。C语言中,语句通常以分号 ; 结尾。
    • 关键表达式加上分号就构成了表达式语句。但语句不一定是表达式(如控制流语句if, while等)。
    • 示例
      • ; (空语句)
      • x = 10; (表达式语句)
      • printf("Hello\n"); (表达式语句)
      • if (x > 0) { ... } (控制语句)
      • return 0; (跳转语句)
  • 代码块
    • 定义:由一对花括号 {} 括起来的多条语句的集合。也称为复合语句。
    • 作用:1. 将多条语句在语法上合并为一条语句(这样iffor后面就能跟多条语句了);2. 创建一个新的作用域(在块内声明的变量通常在块外不可访问)。
    • 示例
      c { // 代码块开始 int temp = a; a = b; b = temp; // 这三条语句合成为一个代码块 } // 代码块结束

关系:多个表达式可以组成一个语句,多个语句可以用 {} 组成一个代码块


4. 左值、右值、对象、副作用、未定义行为

这些是C语言标准文档中的术语,描述了程序执行模型。

  • 对象
    • 定义:C语言中的“对象”指的是内存中一个可标识的、具有特定类型和大小的存储区域,用于存储值。它不仅仅指“面向对象编程”中的对象。
    • 示例int a; 定义后,a 就是一个(int类型的)对象。
  • 左值
    • 原意:指可以出现在赋值运算符左边的表达式。它标识了一个对象,而不仅仅是一个值。
    • 现代理解:一个指定了一个对象的表达式,代表了一个可被引用的内存位置。简单说,有地址、有名字、持久存在的实体
    • 示例:变量名(a)、数组元素(arr[0])、通过指针解引用(*p)。
  • 右值
    • 原意:指只能出现在赋值运算符右边的表达式。它提供了一个值,但不标识一个存储位置。
    • 现代理解:一个仅提供的临时表达式。它通常是短暂的,没有可被程序显式使用的地址。
    • 示例:字面量(10, ‘A’)、算术表达式的结果(a + b)、函数返回值(非左值引用时)。
  • 副作用
    • 定义:对执行环境的状态的改变。表达式求值的主要目的是得到结果值,但如果它顺便修改了某个对象的值,就产生了副作用。
    • 示例
      • 赋值运算符 =:主要副作用是修改左操作数的值。
      • 自增/自减 ++/--:主要副作用是修改操作数的值。
      • 函数调用:如果函数内部修改了全局变量或静态变量,或者通过指针修改了参数,也产生了副作用。
  • 未定义行为
    • 定义:C语言标准没有明确规定行为方式的一些代码写法。编译器遇到UB时,可以做出任何处理,包括产生看似正常的结果、崩溃、产生不可预测的结果等。
    • 原因:为了给编译器实现最大的自由度以进行优化,并适应不同的平台。
    • 常见示例
      • 解引用空指针或野指针
      • 数组越界访问
      • 有符号整数溢出
      • 修改字符串字面量
      • 在同一表达式中多次修改同一个变量而没有序列点(如 i = i++;

5. 结合性、左结合、右结合

这个概念决定了运算符的求值顺序当它们具有相同的优先级时。

  • 结合性
    • 定义:当表达式中连续出现多个相同优先级的运算符时,规定了运算的分组方向
  • 左结合
    • 定义:运算符从左向右进行分组和求值。
    • 示例a + b + c 等价于 (a + b) + c。大多数运算符是左结合的,如 +, -, *, /, %
  • 右结合
    • 定义:运算符从右向左进行分组和求值。
    • 示例
      • 赋值运算符:a = b = c 等价于 a = (b = c)。先执行 b = c,再执行 a = (b=c的结果)
      • 单目运算符:*p++ 等价于 *(p++)++* 优先级相同,但结合性是从右向左)。
      • 条件运算符:a ? b : c ? d : e 等价于 a ? b : (c ? d : e)

总结:优先级决定“先算谁后算谁”,结合性决定“当优先级相同时,是从左往右算还是从右往左算”。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注