第 2 章 基本概念
# 第 2 章 基本概念
# 环境 ⏳
# 翻译 ⏳
# 文件名约定 ⏳
# 编译和链接 ⏳
# 执行 ⏳
# 词法规则 ⏳
# 字符 ⏳
转义字符
\ddd
表示 1~3 个八进制数,转义符表示的字符即为给定的八进制数值所代表的字符\xddd
表示十六进制数,转义符表示的字符即为给定的十六进制数值所代表的字符- 注意所有的十六进制数都可包含在
\xddd
中, 即ddd
可以有多个, 但如果结果值的大小超出了表示字符的范围,则其结果是未定义的
- 注意所有的十六进制数都可包含在
# 注释 ⏳
# 自由形式的源代码 ⏳
# 标识符 ⏳
# 程序的形式 ⏳
# 程序风格 ⏳
# 问题
#
问题 1
在C语言中,注释不允许嵌套。在下面的代码段中,用注释来“注释掉”一段语句会导致什么结果?
void
squares( int limit )
{
/* Comment out this entire function
int i; /* loop counter */
/*
** Print table of squares
*/
for( i = 0; i < limit; i += 1 )
printf( "%d %d0, i, i * i );
End of commented-out code */
}
2
3
4
5
6
7
8
9
10
11
12
13
答案
- 注释从
/*
开始,到*/
结束,其间的所有东西均作为注释的内容 - 第 5 行注释掉了
i
的定义,后续会报variable i undefined
- 第 12 行没有注释掉
End of commented-out code */
, 会报syntax error
#
问题 2
把一个大型程序放入一个单一的源文件中有什么优点?有什么缺点?
答案
- 优点
- 当修改特定的函数时,在单一的源文件中更容易定位
- 不用担心函数重名的问题
- 缺点
- 当文件过大时,受限于硬件性能和编辑器的性能,单一文件的编译可能更耗时
- 即使只修改了特定的函数,但仍需要完全重新编译
- 代码重用比较困难
- 单一文件过大时,较难定位到特定功能的代码块
#
问题 3
你需要用printf函数打印出下面这段文本(包括两边的双引号)。你应该使用什么样的字符串常量参数?
"Blunder??!??"
答案
- 为了输出三字符词,需要对
?
转义 - 为了输出
"
,也需要使用转义
"\"Blunder\?\?!??\""
#
问题 4
\40
的值是多少? \100
、 \x40
、 \x100
、 \0123
、 \x0123
的值又分别是多少?
答案
\40=32=空格字符
\100=64='@'
\x40=64='@'
\x100=256
,在绝大多数机器上,超出了字符的范围,其结果因编译器而异\0123
由'\012'
和'3'
两个字符构成\x0123
,在绝大多数机器上,超出了字符的范围,其结果因编译器而异
#
问题 5
下面这条语句的结果是什么?
int x/*blah blah*/y;
答案
预处理器会将注释替换为一个空格,因此此语句非法,会报 error: ‘y’ undeclared (first use in this function)
#
问题 6
下面的声明存在什么错误(如果有的话)?
int Case, If, While, Stop, stop;
答案
- 由于 C 是一种大小写敏感的语言,所以上述声明不存在错误
#
问题 7
是非题:因为C(除了预处理指令之外)是一种自由形式的语言,唯一规定程序应如何编写的规则就是语法规则,所以程序实际看上去的样子无关紧要。
答案
- 有对有错
- 对:除了预处理指令之外,语言并没有对程序应该出现的外观施加任何规则
- 错:风格恶劣的程序难以维护或无法维护
- 所以除了极为简单的程序之外,绝大多数程序的编写风格是非常重要的
#
问题 8
下面两个程序中的循环是否正确?
#include <stdio.h>
int
main( void )
{
int x, y;
x = 0;
while( x < 10 ){
y = x * x;
printf( "%d\t%d\n", x, y );
x += 1;
}
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
int
main( void )
{
int x, y;
x = 0;
while( x < 10 ){
y = x * x;
printf( "%d\t%d\n", x, y );
x += 1;
}
2
3
4
5
6
7
8
9
10
11
12
13
答案
- 两个程序都不正确,其
while
循环都缺少一个用于结束语旬的右花括号 - 但第 2 个程序更容易发现这个错误
- 说明了在函数中对语句进行缩进的价值
#
问题 9
假定你有一个 C 程序,它的 main
函数位于文件 main.c
,它还有一些函数位于文件 list.c
和 report.c
。在编译和链接这个程序时,你应该使用什么命令?
答案
- 在 linux 系统中
- 编译:
cc -c main.c list.c report.c
- 链接:
cc main.o list.o report.o
- 编译&&链接:
cc main.c list.c report.c
- 编译:
#
问题 10
接上题,如果你想使程序链接到parse函数库,你应该对命令作何修改?
答案
- 编译&&链接:
cc main.c list.c report.c lparse
#
问题 11
假定你有一个C程序,它由几个单独的文件组成,而这几个文件又分别包含了其他文件,如下所示
文件 | 包含文件 |
---|---|
main.c | stdio.h, table.h |
list.c | list.h |
symbol.c | symbol.h |
table.c | table.h |
table.h | symbol.h, list.h |
如果你对 list.c
作了修改,你应该用什么命令进行重新编译?如果是 list.h
或者 table.h
作了修改,又分别应该使用什么命令?
答案
.h
文件修改后,所有包含它的文件均需要重新编译
list.c
被修改后,重新编译的命令为cc list.c main.o symbol.o table.o
list.h
被修改后,重新编译的命令为cc list.c main.c symbol.o table.c
table.h
被修改后,重新编译的命令为cc list.o main.c symbol.o table.c
# 编程练习
#
编程练习 1
编写一个程序,它由 3
个函数组成,每个函数分别保存在一个单独的源文件中。函数 increment
接受一个整型参数,它的返回值是该参数的值加 1
。 increment
函数应该位于文件 increment.c
中。第 2
个函数称为 negate
,它也接受一个整型参数,它的返回值是该参数的负值(例如,如果参数是 25
,函数返回 25
;如果参数是 612
,函数返回 612
)。最后一个函数是 main
,保存于文件 main.c
中,它分别用参数 10,0
和 10
调用另外两个函数,并打印出结果
答案
main.c
#include <stdio.h>
int increment(int number);
int negate(int number);
int main(void)
{
printf("increment: %d, %d, %d\n",
increment(10), increment(0), increment(-10));
printf("negate: %d, %d, %d\n",
negate(10), negate(0), negate(-10));
}
2
3
4
5
6
7
8
9
10
11
increment.c
int increment(int number)
{
return ++number;
}
2
3
negate.c
int negate(int num)
{
return -num;
}
2
3
- 终端编译&运行
cc main.c increment.c negate.c && ./a.out && rm ./a.out
- 输出
increment: 11, 1, -9
negate: -10, 0, 10
2
#
编程练习 2
编写一个程序,它从标准输入读取 C 源代码,并验证所有的花括号都正确地成对出现。注意:你不必担心注释内部、字符串常量内部和字符常量形式的花括号。
答案
一般采取栈来对括号进行匹配,由于只须区配花括号,可以只记录左花括号的数量,用左花括号数量的增减,来模拟入栈出栈过程
代码
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int ch;
int braces = 0;
int flag = 1;
while ((ch = getchar()) != EOF)
{
if (ch == '{')
braces++;
if (ch == '}')
if (braces == 0)
{
printf("Extra closing brace!\n");
flag = 0;
}
else
braces--;
}
if (braces > 0)
printf("%d unmatched opening brace(s)\n", braces);
else if (flag)
printf("Brace-matching success.\n");
return EXIT_SUCCESS;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
- 输入1
}{
- 输出1
Extra closing brace!
1 unmatched opening brace(s)
2
- 输入2
#include <stdio.h>
int main(void)
{
printf("hello world!\n");
return 0;
}
2
3
4
5
6
7
- 输出2
Brace-matching success.