C语言语法面试问题总结

C的语法规则

  1. 源文件中所有函数定义之外可以出现哪些语法元素?

  1. 函数定义之中可以出现哪些语法元素?

  1. 语句有哪几种?

  1. 哪些语法元素需要遵循标识符的命名规则?

  1. 表达式由哪些语法元素组成?

  1. 运算符的优先级和结合性是怎样的?

  1. 哪些运算符取操作数的左值?哪些运算符的操作数必须是整型?哪些运算符有Side Effect?

  1. 哪些表达式可以做左值?哪些表达式只能做右值?

  1. 哪些地方必须用常量表达式?哪些地方必须用整数常量表达式?

答案

1.源文件中所有函数定义之外可以出现哪些语法元素?

  1. 声明语句:例如变量、函数、结构体等的声明语句,如int a;、void func();、struct Person {...};等;

  1. 变量定义:例如变量的定义及初始化,如int a = 0;、char str[] = "hello world!";等;

  1. 宏定义:例如使用 #define 定义常量或宏函数,如#define PI 3.14159、#define ADD(a, b) ((a) + (b))等;

  1. 注释:例如单行注释或多行注释,如// This is a single line comment.、/* This is a multi-line comment. */等;

  1. 空行或空格:例如不含任何语法元素的空行或空格,用于提高代码可读性;

  1. 预编译指令:例如 #include、#ifdef、#ifndef、#endif 等预编译指令。

2.函数定义之中可以出现哪些语法元素?

  1. 返回值类型:函数定义需要指定其返回值类型,如int func(int a);;

  1. 函数名:需要定义一个函数名,用于在程序中调用函数,例如int func(int a); 中的 func;

  1. 参数列表:函数可能需要接受输入的参数,用于函数内部的计算,如int func(int a); 中的 int a;

  1. 函数体:实际的函数代码部分,包含需要执行的语句,如int func(int a) { return a * a; };

  1. 变量定义:函数内部可以定义变量,用于储存临时计算结果或中间状态值,例如int result = a * a;;

  1. 控制流语句:函数可以使用控制流语句,用于实现复杂的逻辑判断和条件分支,如 if、for、while、switch 等;

  1. 表达式和运算符:函数可以使用各种语言内置的表达式和运算符,如算术运算符、逻辑运算符、位运算符等。

3.语句有哪几种?

  1. 表达式语句:由表达式和分号组成的一句话代码。表达式计算后抛弃,一般用于修改变量的值、函数调用等。例如 a++;、printf("Hello, world!");;

  1. 赋值语句:将一个值赋给一个变量。例如 int a = 10;、a = b + c;;

  1. 控制语句:改变程序执行流程的语句。包括条件语句(if-else语句、switch语句)、循环语句(for语句、while语句、do-while语句)和跳转语句(break语句、continue语句、goto语句和return语句);

  1. 声明语句:定义变量或函数。例如 int a;、void func(int a);;

  1. 空语句:不执行任何操作的语句,只有分号。例如 ;。

还有一些其他类型的语句,例如标签语句、复合语句等,但这些语句只在特定的情况下使用,不属于基本的语言结构。

4.哪些语法元素需要遵循标识符的命名规则?

  1. 变量名和函数名:变量和函数的名称必须是由字母、数字和下划线组成,且必须以字母或下划线开头;

  1. 结构体、共用体和枚举类型的标签名:结构体、共用体和枚举类型的标签名必须是由字母、数字和下划线组成,且必须以字母或下划线开头;

  1. 标签语句中的标签名:标签语句中的标签名必须是由字母、数字和下划线组成,且必须以字母或下划线开头;

  1. 宏定义的名称:宏定义的名称必须是由字母、数字和下划线组成,且必须以字母或下划线开头;

  1. typedef 定义的类型名:typedef 定义的类型名必须是由字母、数字和下划线组成,且必须以字母或下划线开头。

需要注意的是,C 语言中标识符的命名规则是不区分大小写的,即 foo、Foo 和 FOO 被视为相同的标识符。另外,标识符的长度是有限制的,具体限制取决于编译器的实现。为了提高代码的可读性,建议在命名标识符时使用具有清晰含义的名称,并遵循统一的命名规范。

5.表达式由哪些语法元素组成?

  1. 运算符:如加号、减号、乘号、除号等用于完成各种算术、逻辑和位运算操作。

  1. 操作数:指参与运算的值,可以是常量、变量或其他表达式。

  1. 函数调用:可以将函数名称和参数列表组成的表达式作为一个整体进行处理。

  1. 表达式的分组和优先级:用圆括号来分组和改变表达式中的运算顺序。

  1. 强制类型转换:可以通过在操作数前加上需要转换的类型的名称来改变操作数的数据类型。

  1. 自增和自减运算符:用来对变量进行加一或减一操作。

  1. 条件运算符:用来执行基于某个条件进行的不同操作。

  1. 逗号运算符:可以用来在同一条语句中同时执行多个表达式。

6.运算符的优先级和结合性是怎样的?

  1. 优先级高于其他运算符的是括号运算符,用来改变表达式中的运算顺序。

  1. ++和--运算符优先级高于算术运算符,但是结合性是从右到左。例如++a的意思是先进行a+1的操作,然后再对a进行其他操作。

  1. 算术运算符(包括乘、除、取模、加、减)的优先级从高到低依次是:乘、除、取模,加、减,同一优先级的结合性是从左到右。

  1. 移位运算符(包括左移和右移)的优先级低于算术运算符,同一优先级的结合性是从左到右。

  1. 关系运算符(包括小于、大于、小于等于、大于等于)的优先级低于移位运算符,同一优先级的结合性是从左到右。

  1. 相等性运算符(包括等于和不等于)的优先级比关系运算符低,同一优先级的结合性是从左到右。

  1. 按位与运算符的优先级低于相等性运算符,同一优先级的结合性是从左到右。

  1. 按位异或运算符和按位或运算符的优先级低于按位与运算符,同一优先级的结合性是从左到右。

  1. 逻辑运算符(包括逻辑非、逻辑与和逻辑或)的优先级比按位运算符低,同一优先级的结合性是从左到右。

  1. 赋值运算符的优先级低于逻辑运算符,同一优先级的结合性是从右到左。

总体来说,从优先级高到低,表达式的计算顺序是从左到右,从内到外。但是同一优先级内部则是从左到右或从右到左,根据具体的运算符而定。

7.哪些运算符取操作数的左值?哪些运算符的操作数必须是整型?哪些运算符有Side Effect?

  1. 取左值运算符:取地址运算符&、下标运算符[]等都取操作数的左值(Lvalue)。

  1. 整数运算符:如+、-、*、/、%、<<、>>等,其操作数必须是整型,不能是浮点型,也不能是指针类型。

  1. 有Side Effect的运算符:自增运算符++、自减运算符--、赋值运算符=等都有Side Effect,它们在执行时会修改其操作数的值。

  1. 逻辑运算符:如&&、||、!等的操作数可以是任何类型,但最终返回值为整型。

  1. 位运算符:如&、|、^、~、<<、>>等的操作数必须是整型,可以是有符号整型或无符号整型。

  1. 条件运算符:条件运算符?:的操作数可以是任意类型,但必须是同一类型。

8.哪些表达式可以做左值?哪些表达式只能做右值?

  1. 变量名:变量名可以作为左值,因为它可以代表变量所在内存地址。例如:x = 5;中的x就是一个左值,赋值语句会将5赋给变量x的内存空间。

  1. 指针变量解引用:使用解引用运算符*可以获得指针变量所指向的内存地址的值,这个值可以作为左值或右值。例如:*p = 10;中的*p是一个左值,赋值语句会将10赋给指针变量p所指向的内存空间。

  1. 数组元素:数组元素可以作为左值,因为它们代表数组对应内存地址中的值。例如:a[2] = 3;中的a[2]是一个左值,赋值语句会将3赋给数组a的第三个元素。

  1. 结构体成员:结构体成员可以作为左值,因为它们代表结构体对应内存地址中的成员值。例如:p->name = "John";中的p->name是一个左值,赋值语句会将字符串赋给结构体p的成员变量name。

  1. 字面量、字符串等:字面量和字符串等不是左值,只能作为右值出现。例如:x = 5;中的5是一个右值,赋值语句会将5赋给变量x的内存空间。

只有能够代表内存地址的表达式才能作为左值,其他表达式只能作为右值。在使用表达式时,需要根据表达式的特性来选择合适的左值或右值上下文,以确保正确的执行结果。

9.哪些地方必须用常量表达式?哪些地方必须用整数常量表达式?

必须用常量表达式的地方包括:

  1. 数组大小的声明:数组大小必须是常量表达式,例如:int arr[10]。

  1. 枚举常量的定义:枚举常量必须是常量表达式,例如:enum { RED = 1, GREEN = 2, BLUE = 4 }。

  1. switch 语句中 case 标签的定义:case 标签必须是整型常量表达式,例如:switch (a) { case 1: // do something }。

而必须用整数常量表达式的地方包括:

  1. 位字段成员的宽度:位字段成员的宽度必须是整数常量表达式,例如:struct Flags { unsigned int a: 8; }。

  1. #if,#elif 指令的条件表达式:#if,#elif 指令的条件表达式必须是整数常量表达式,例如:#if MAX_SIZE > 1024。

  1. sizeof 操作符的操作数:sizeof 操作符的操作数必须是类型名或表达式,但不能是运行时的变量,例如:int arr[10]; sizeof(arr) // 正确,int n = 10; sizeof(int[n]) // 错误。

总之,常量表达式是在编译时就可以计算出值的表达式,而整数常量表达式是指仅由常量和整型运算符组成的常量表达式。在需要使用这两种类型的地方,必须使用符合规范的常量表达式或整数常量表达式,否则会引起编译错误。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值