Skip to content

C Programming

PTA homework of ZJU Fundamentals of Programming and Algorithm, 2020 Fall & Winter.

Week 1

Q1-6. C 语言的标识符由字母、数字和其他任意字符组成。(T/F)

Answer

F。应当由字母、数字和下划线组成,其中首字符不能为数字。实际运行中,包含 $ 的标识符 (identifier) 也可以编译通过。在这样的标识符中,$ 字符也可以作为首字符

Week 3

Q2-6. 在嵌套使用 if 语句时,C 语言规定 else 总是 ______

Answer

和之前与其最近的且不带 else if 配对

Week 4

Q1-3. sizeof( ) C 语言的一个函数,可以计算参量所占内存的字节数。如 sizeof(int) 可计算整型所占的内存字节数。(T/F)

Answer

F。sizeof() C 语言的一个运算符(这就是一个坑题)

Q1-4. 通过函数调用只能获得一个返回值。(T/F)

Answer

F。1 个或 0 个(这也是一个坑题)

Q1-5. 函数可以嵌套调用但不能嵌套定义。(T/F)

Answer

T。

Q2-1. C 语言程序中,若对函数类型未加显式说明,则函数的隐含类型为(

Answer

int

期中 Ⅰ 段:运算符优先级

优先级 运算符 结合性 / 目数
1 [] 数组 ()
1 -> . 结构
2 - 取负 ++ -- 自增减 /
2 (类型)
2 * & 指针 / 地址 /
2 ~ / 按位反 /
2 sizeof
3 * / % /
4 + - /
5 << >> /
6 >(=) <(=) /
7 == != /
8/9/10 & / ^ / | /
11/12 && || 逻辑 /
13 ?: /
14 各种赋值
15 ,

需要关注的点:

  • 大小比较符 (6) 高于等于比较符 (7),都处于中游
  • 1 yyds:数组 []、结构 .->、括号 () 可认为与相关操作数一体
  • 关注第 2 级(除第 1 级外,第 2 级几乎最高,且都有右结合的特殊性)
  • 赋值没地位,逗号最没地位
  • 数字计算中最常用的*/% (3) +- (4) 符合认知,仅居于 12 之下
  • */%+- 为表格的中心,可见所有的比较符都在其下面

    判断条件中 */%+- 对比较符号间不需要括号

  • */%+- 为表格的中心,可见几乎所有逻辑和位运算都在其下面

    !~是“反面”例子,居于第 2

  • 逻辑:非 > >
  • 位运算:反 > > 异或 >
  • 特殊的三目运算符:只比最没地位的两个好一点,有右结合的特殊性
  • 右结合:第 2 级,?: 条件和赋值
  • 具体例子:第 2 级右结合的体现
    • *p++
    • (char*)(&n) 等价于 (char*)&n

Week 10

Q1-1. 指针只有加减操作,没有乘除操作。指针可以加常数、减常数;相同类型的指针可以相加、相减。(T/F)

Answer

F。相同类型的指针不能相加,因为相加之后没有什么意义

Q1-2. 假设有定义如下: int array[10]; , 则该语句定义了一个数组 array。其中 array 的类型是整型指针(即: int *)(T/F)

Answer

F。array 的类型是数组名。array 存储的是 array[0] 的地址,array 代表的是整个数组。因此 &array array 虽然数值相同但意义不同,可以从 (&array)+1 array+1 中看出——前者移动了 40 Byte,后者移动了 4 Byte

Q2-1. If variables are defined and assigned correctly, the expression __ is wrong.

A. a&b

B. a^b

C. &&x

D. a, b

Answer

C。&x 访问到了 x 的地址,但是这个地址是直接访问到的而不是从某个存储它的变量中读到的,所以不能再取一次地址

Q2-2. According to the declaration: int a[10], *p=a;,

the expression __ is wrong.

A. a[9]

B. p[5]

C. *p++

D. a++

Answer

D。数组名可以看成 const 指针,不能做 ++ 运算

Q2-3. char str[10]; str="string"; (T/F)

Answer

F。用字符串字面量("xxx")初始化:字符数组只能在被定义时这么干,定义了之后不行。

字符串在被定义时和定义了之后都可以这么干。

Q2-6. According to the declaration: int (*p)[10];,

p is a(n) __.

Answer

pointer。p 是一个数组指针 / 行指针,指向若干个长度为 10 的数组 / 若干一行有 10 个元素的行,类型与传参时的二维数组相同 ( 二维数组作为函数的参数时就可以这么写 )

但注意二维数组名的类型并不是指针,而是数组。二维数组是 " 数组的数组 ",其数组名自然也是数组,但是传参 / 赋值的时候由于隐式转化的问题,类型会与此相同。

Q2-7. Among the following statements, __ is equivalent to the declaration: int *p[4];.

A. int p[4];

B. int **p;

C. int *(p[4]);

D. int (*p)[4];

Answer

C。再次体现了优先级问题,[] 是第 1 级的,* 是第 2 级的。 int *p[4] 中,p 是一个数组,存储着 4 个指针元素。

D: 因此 int *p[4] 中的 p int (*p)[4] 中的 p 存在类型上的差异————前者是数组,后者是指针。

虽然 int *p[4] p 是一个数组,但考虑到数组名隐式转化为指针的问题,在传参 / 赋值的时候会认为 p 的类型是 int**。不过本身类型并不一样,所以 B 也错。

int (*p)[4] 的类型就是 (int*)[4],和 int** 不同。这在下一题会有明显的体现。

Q2-8. For the function declaration void f(char ** p),

the definition __ of var makes the function call f(var) incorrect。

A. char var[10][10];

B. char *var[10];

C. void *var = NULL;

D. char *v=NULL, **var=&v;

Answer

A。A var 的类型是二维数组,传参时的类型为 (*char)[10],虽然是个 ( 数组 / ) 指针,但并不是二重指针。这是因为传参时的隐式转化只能转化一层,因此一维数组传参时完全等价于 const 指针,但是二维数组就无法等价于二重指针。

另外 *var 确实代表 var[0] 这个数组,**var 也确实就是 var[0][0],但是注意这里的 * 是运算符而不是类型定义。作为类型定义时,数组和指针是需要区分的。

B var 的类型是指针数组,但是由于数组隐式转化的问题,其传参时表现出来的类型就是 char**,因此是正确的。

C var 的类型是无类型指针,它可以指向任何东西。如果它指向的是一个 char*,那么它就符合 char** 的类型要求了,因此正确。

D var 的类型是正统的 char**

关于隐式转化,这是我在网络上看到的一种说法,大概解释如下

隐式转化的解释

数组名代表整个数组,但是存储着其第一个元素的地址

因此数组名既是数组,又可以看成一个指针 ( 类型依然是数组 )

在传参 / 赋值时,其代表数组的意义并没有什么意义,应当看成一个指针,因此会有“数组隐式转化为指针”的现象

这种转化只能转化一层,原因是转化一层之后它已经是个指针可以进行传参 / 赋值了,编译器不需要再考虑它指的是什么东西

因此二维数组传参 / 赋值时只会隐式转化为 (char*)[10](以上题为例,而不会转化为 char**

Q2-10. 对于以下变量定义,正确的赋值是(

char *pc[5], s[10];

A. pc = s;

B. *pc = s[0];

C. *pc = s;

D. *pc = &s;

Answer

C。有了以上的铺垫,感觉这里就很自然了。

A,pc 的类型是数组,此处作为赋值的左值相当于 const 指针,不能被赋值。

BCD,*pc 就是 pc[0],一个字符串 (char* )

B,s[0] 是字符 (char ),不能赋值给字符串 (char* )

D,&s 是整个字符数组 s 的地址,如果存储起来类型就是 (char*)[10],当然也不能赋值给字符串 (char* )

C,s 是字符数组,赋值时隐式转化为 char*,可以被赋给 *pc

Week 11

Q1-2. 不同类型的指针变量是可以直接相互赋值的。(T/F)

Answer

F。会报错,一般需要强制类型转换。

但是存在例外,例如 void* 是可以直接赋值给其他指针的。

Q2-8. For definitions: char s[2][3]={"ab", "cd"}, *p=(char *)s;

the expression __ is correct and its value is equivalent to the element s[1][1].

A. *(s+3);

B. *s+2;

C. p[1][1];

D. *++p+2;

Answer

D。A 没有意义。*s "ab"*(s+1) "cd"( 移动步长为sizeof(s[0]) )

B 的输出为 NULL*s+1 "b"*s+3 "cd" ( 移动步长为sizeof(s[0][0]) )

CD 中,s 存储的是 s[0] 的地址,其地址与 s[0][0] 相同。对 p 赋值时强制转化为 (char* ),则 p s[0] 隐式转化的指针 (const 指针 ) 赋值,成为存储 s[0][0] 地址的指针。因此 C 失去意义。

D ++p 使得 p 指向 s[0][1]* 取到了 s[0][1],然后注意 +2 不是指针的移动而是单纯对取到的 s[0][1] 进行数值的 +2,因此 *++p+2='b'+2='d',而不是 *++p+2=s[1][1]='d',值的相等只是一个巧合。所以,有 *++p+3='e'*++p+4='f'……

Q2-9. 以下哪个定义中的 p 不是指针,请选择恰当的选项:

A. char **p;

B. char (*p)[10];

C. char *p[6];

D. 给出的三项中,p 都是指针

Answer

A、B 都是指针,C 是指针数组

Q2-10. 若有定义 char *str[]={"Python", "SQL", "JAVA", "PHP", "C++"};

则表达式 *str[1] > *str[3] 比较的是:

A. 字符 P 和字符 J

B. 字符串 SQL 和字符串 PHP

C. 字符串 Python 和字符串 JAVA

D. 字符 S 和字符 P

Answer

D。考察的依然是 []* 的优先级问题。

*str[1] 首先计算 char* 类型的 str[1],也就是第一个字符串 "SQL"

然后 *str[1] 得到的就是 "SQL" 的第一个字符 "S"

Week 12

Q1-1. 结构体变量可以作数组元素 (T/F)

Answer

T。结构体数组!

Q1-2. 结构体成员的类型必须是基本数据类型 (T/F)

Answer

F。链表?自定义类型也可以,只要是完整的可以让结构体估计大小的类型。

Q1-3. 结构体类型本身不占用内存空间,结构体变量占用内存空间 (T/F)

Answer

T。