C Primer Plus笔记第二章

前期准备

开发环境(Windows):Microsoft Visual Studio 2019 

需要知道自2012版本起,Visual Studio不再把C作为项目类型的选项,但尽管如此,C Primer Plus这本书中的绝大多数程序仍可用Visual Studio来编译。

在新建项目时,选择C++选项,然后选择【Win32控制台应用程序】,在应用设置中选择【空项目】。几乎所有的C程序都能与C++程序兼容。所以,本书中的绝大多数C程序都可作为C++程序运行。或者,在选择C++选项后,将默认的源文件扩展名.cpp替换成.c,编译器便会使用C语言的规则代替C++。

简单的C程序示例1

#include <stdio.h>
int main(void)                    /* 一个简单的C程序 */
{
     int num;                     /* 定义一个名为num的变量 */
     num = 1;                     /* 为num赋一个值 */

     printf("I am a simple ");    /* 使用printf()函数 */
     printf("computer.\n");
     printf("My favorite number is %d because it is first.\n",num);

     return 0;
}

该程序为在屏幕上打印一些内容,运行结果如下

I am a simple computer.
My favorite number is 1 because it is first.

程序调整

示例1的输出是否在屏幕上一闪而过?或输出后自动退出程序。可以通过在程序中添加额外代码来解决此类情况。一种方法是,在程序的return语句前添加一行代码:

getchar();

这行代码会让程序等待击键,窗口会在用户按下一个键后才关闭。

示例1解释

#include<stdio.h> 

该行中由#include和<stdio.h>两部分组成,其中#include是一条C预处理器指令,通常,C编译器在编译前会对源代码做一些准备工作,即预处理。

stdio.h是C编译器软件包的标准部分,它提供键盘输入和屏幕输出的支持(stdio.h文件包含程序中出现的printf()函数的详细信息)

该行告诉编译器把stdio.h文件中的所有内容都输入该行所在的位置。

int main(void)  /*一个简单的程序*/

C程序包含一个或多个函数,它们是C程序的基本模块。

该行是一个main()函数的函数头,C程序一定从main()函数开始执行。其中圆括号可以用来识别main()是一个函数,main是函数名,int表明main()函数的返回类型为整型,圆括号中间包含一些传入函数的信息,示例1没有传入任何内容,因此为void。

此外注释是提高程序易读性的重要方法,示例1中的注释用到了/**/,C99新增了另一种风格的注释,使用//符号创建注释,仅限于单行。

// 这种注释只能写成一行。
int rigue; // 这种注释也可置于此。
{
    ...
}

一般而言,所有的C函数都使用花括号标记函数体的开始和结束。这是规定,不能省略。

int num;

这行代码叫作声明(declaration)。声明是C语言最重要的特性之一。

该行声明完成了两件事情:其一,在函数中有一个名为num的变量。其二,int表明num是一个整数。

为什么要声明变量?

  • 把所有的变量放在一处,方便读者查找和理解程序的用途。
  • 促使你在编写程序之前做一些计划。
  • 有助于发现隐藏在程序中的小错误,如变量名拼写错误
  • 如果事先未声明变量,C程序将无法通过编译。
num = 1;

这行代码是赋值表达式语句,该行代码的意思是“把值1赋给变量num”。

printf("I am a simple ");
printf("computer.\n");
printf("My favorite number is %d because it is first.\n", num);

这3行都使用了C语言的一个标准函数:printf()。圆括号表明printf是一个函数名。圆括号中的内容是从main()函数传递给printf()函数的信息。

第1行printf()演示了在C语言中如何调用函数。只需输入函数名,把所需的参数填入圆括号即可。当程序运行到这一行时,控制权被转给已命名的函数(该例中是printf())。函数执行结束后,控制权被返回至主调函数,该例中是main()。

第2行printf()函数的双引号中的\n字符并未输出。这是为什么?\n的意思是换行。\n组合代表一个换行符。打印换行符的效果与在键盘按下Enter键相同。

第3个printf()参数中的%d相当于是一个占位符,其作用是指明输出num值的位置。

return 0;

该语句以return关键字开始,后面是待返回的值,并以分号结尾。int main(void)中的int表明main()函数应返回一个整数。C标准要求main()这样做。有返回值的C函数要有return语句。

简单程序的结构

在看过一个具体的程序示例后,我们来了解一下C程序的基本结构。

程序由一个或多个函数组成,必须有main()函数。函数由函数头和函数体组成。函数头包括函数名、传入该函数的信息类型和函数的返回类型。通过函数名后的圆括号可识别出函数,圆括号里可能为空,可能有参数。函数体被花括号括起来,由一系列语句、声明组成,大部分语句都以分号结尾。

提高程序可读性的技巧

  1. 选择有意义的函数名,如高度使用height
  2. 写注释
  3. 在函数中用空行分隔概念上的多个部分,如声明变量与赋值表达式之间使用空行分隔
  4. 每条语句各占一行

示例2

// fathm_ft.c -- 把2英寻转换成英尺
#include <stdio.h>
int main(void)
{
     int feet, fathoms;

     fathoms = 2;
     feet = 6 * fathoms;
     printf("There are %d feet in %d fathoms!\n", feet, fathoms);
     printf("Yes, I said %d feet!\n", 6 * fathoms);

     return 0;
}

与示例1相比,示例2声明了两个变量,两个变量中间用逗号隔开。

C语言和许多其他语言一样,用*表示乘法。

示例3

/* two_func.c -- 一个文件中包含两个函数 */
#include <stdio.h>
void butler(void); /* ANSI/ISO C函数原型 */
int main(void)
{
     printf("I will summon the butler function.\n");
     butler();
     printf("Yes. Bring me some tea and writeable DVDs.\n");

     return 0;
}
void butler(void) /* 函数定义开始 */
{
     printf("You rang, sir?\n");
}

该程序的输出如下:

I will summon the butler function.
You rang, sir?
Yes. Bring me some tea and writeable DVDs.

butler()函数在程序中出现了3次。

第1次是函数原型,告知编译器在程序中要使用该函数;函数原型是一种声明形式,告知编译器正在使用某函数,因此函数原型也被称为函数声明(function declaration)。函数原型还指明了函数的属性。C标准建议,要为程序中用到的所有函数提供函数原型。标准include文件(包含文件)为标准库函数提供了函数原型。例如,在C标准中,stdio.h文件包含了printf()的函数原型。

第2次以函数调用的形式出现在main()中,这里要注意,何时执行butler()函数取决于它在main()中被调用的位置。无论main()在程序文件中处于什么位置,所有的C程序都从main()开始执行。

最后一次出现在函数定义中,函数定义即是函数本身的源代码。

调试程序

程序的错误通常叫作bug,找出并修正错误的过程叫作调试(debug)。

试着找找以下示例中的错误

/* nogood.c -- 有错误的程序 */
#include <stdio.h>
int main(void)
(
     int n, int n2, int n3;

     /* 该程序有多处错误
     n = 5;
     n2 = n * n;
     n3 = n2 * n2;
     printf("n = %d, n squared = %d, n cubed = %d\n", n, n2, n3)

     return 0;
)

修正后的程序为

/* stillbad.c -- 修复了语法错误的程序 */
#include <stdio.h>
int main(void)
{//<---错误1
     int n, n2, n3;//<---错误2

     /* 该程序有一个语义错误 */ //<---错误3
     n = 5;
     n2 = n * n;
     n3 = n2 * n;//<---错误4
     printf("n = %d, n squared = %d, n cubed = %d\n", n, n2, n3);

     return 0;
}

其中错误1、2、3都属于语法错误,不遵循C语言的规则就会犯语法错误。C语言的语法错误指的是,把有效的C符号放在错误的地方。

错误4属于语义错误,语义错误是指意思上的错误。在C语言中,如果遵循了C规则,但是结果不正确,那就是犯了语义错误。

定义语义错误方法:

  • 跟踪程序状态(通过逐步跟踪程序的执行步骤,并记录每个变量,便可监视程序的状态。程序状态是在程序的执行过程中,某给定点上所有变量值的集合。它是计算机当前状态的一个快照。)方法:1 自己模拟计算机逐步执行程序,2 出现大量的循环时,可以跟踪一小部分循环
  • 在程序中的关键点插入额外的printf()语句,以监视指定变量值的变化;
  • 检测程序状态的第3种方法是使用调试器(debugger)。

关键字和保留标识符

关键字是C语言的词汇。它们对C而言比较特殊,不能用它们作为标识符(如,变量名)。许多关键字用于指定不同的类型,如int。还有一些关键字(如,if)用于控制程序中语句的执行顺序。ISO C关键字如下图:

还有一些保留标识符(如printf),C语言已经指定了它们的用途或保留它们的使用权,如果你使用这些标识符来表示其他意思会导致一些问题。

复习题

  1. C语言的基本模块是什么?
  2. 什么是语法错误?写出一个C语言例子。
  3. 什么是语义错误?写出一个C语言例子。
  4. Indiana Sloth编写了下面的程序,并征求你的意见。请帮助他评定。
    include studio.h
    int main{void} /* 该程序打印一年有多少周 /*
    (
         int s
    
         s := 56;
         printf(There are s weeks in a year.);
         return 0;
  5. 假设下面的4个例子都是完整程序中的一部分,它们都输出什么结果?
    a. printf("Baa Baa Black Sheep.");
       printf("Have you any wool?\n");
    b. printf("Begone!\nO creature of lard!\n");
    c. printf("What?\nNo/nfish?\n");
    d. int num;
       num = 2;
       printf("%d + %d = %d", num, num, num + num);
  6. 在main、int、function、char、=中,哪些是C语言的关键字?
  7. 如何以下面的格式输出变量words和lines的值(这里,3020和350代表两个变量的值)?
    There were 3020 words and 350 lines.
  8. 考虑下面的程序:
    #include <stdio.h>
    int main(void)
    {
          int a, b;
    
          a = 5;
          b = 2; /* 第7行 */
          b = a; /* 第8行 */
          a = b; /* 第9行 */
          printf("%d %d\n", b, a);
          return 0;
    }
    请问,在执行完第7、第8、第9行后,程序的状态分别是什么?
  9. 考虑下面的程序:
    #include <stdio.h>
    int main(void)
    {
          int x, y;
    
          x = 10;
          y = 5;        /* 第7行 */
          y = x + y;    /*第8行*/
          x = x*y;      /*第9行*/
          printf("%d %d\n", x, y);
          return 0;
    }
    请问,在执行完第7、第8、第9行后,程序的状态分别是什么?

编程练习

  1. 编写一个程序,调用一次printf()函数,把你的名和姓打印在一行。再调用一次printf()函数,把你的名和姓分别打印在两行。然后,再调用两次printf()函数,把你的名和姓打印在一行。输出应如下所示(当然要把示例的内容换成你的名字):
    Gustav Mahler      ←第1次打印的内容
    Gustav             ←第2次打印的内容
    Mahler             ←仍是第2次打印的内容
    Gustav Mahler      ←第3次和第4次打印的内容
  2. 编写一个程序,打印你的姓名和地址。
  3. 编写一个程序把你的年龄转换成天数,并显示这两个值。这里不用考虑闰年的问题。
  4. 编写一个程序,生成以下输出:
    For he's a jolly good fellow!
    For he's a jolly good fellow!
    For he's a jolly good fellow!
    Which nobody can deny!
    除了main()函数以外,该程序还要调用两个自定义函数:一个名为jolly(),用于打印前3条消息,调用一次打印一条;另一个函数名为deny(),打印最后一条消息。
  5. 编写一个程序,生成以下输出:
    Brazil, Russia, India, China
    India, China
    Brazil, Russia
    除了main()以外,该程序还要调用两个自定义函数:一个名为br(),调用一次打印一次“Brazil, Russia”;另一个名为ic(),调用一次打印一次“India, China”。其他内容在main()函数中完成。
  6. 编写一个程序,创建一个整型变量toes,并将toes设置为10。程序中还要计算toes的两倍和toes的平方。该程序应打印3个值,并分别描述以示区分。
  7. 许多研究表明,微笑益处多多。编写一个程序,生成以下格式的输出:
    Smile!Smile!Smile!
    Smile!Smile!
    Smile!
    该程序要定义一个函数,该函数被调用一次打印一次“Smile!”,根据程序的需要使用该函数。
  8. 在C语言中,函数可以调用另一个函数。编写一个程序,调用一个名为one_three()的函数。该函数在一行打印单词“one”,再调用第2个函数two(),然后在另一行打印单词“three”。two()函数在一行显示单词“two”。main()函数在调用one_three()函数前要打印短语“starting now:”,并在调用完毕后显示短语“done!”。因此,该程序的输出应如下所示:
    starting now:
    one
    two
    three
    done!

复习题答案

  1. 它们都叫作函数。
  2. 语法错误违反了组成语句或程序的规则。这是一个有语法错误的C语言例子:printf"Where are the parentheses?";。
  3. 语义错误是指含义错误。这是一个有语义错误的C语言例子: thrice_n = 3 + n;。
  4. 第1行:以一个#开始;studio.h应改成stdio.h;然后用一对尖括号把stdio.h括起来。第2行:把{}改成();注释末尾把/*改成*/。第3行:把(改成{ 第4行:int s末尾加上一个分号。 第5行没问题。第6行:把:=改成=,赋值用=,而不是用:=(这说明Indiana Sloth了解Pascal)。另外,用于赋值的值56也不对,一年有52周,不是56周。第7行应该是:printf("There are %d weeks in a year.\n", s); 第9行:原程序中没有第9行,应该在该行加上一个右花括号}。修改后的程序如下:
    #include <stdio.h>
    int main(void) /* this prints the number of weeks in a year */
    {
         int s;
    
         s = 52;
         printf("There are %d weeks in a year.\n", s);
         return 0;
    }
  5. 关键字是int和char(main是一个函数名;function是函数的意思;=是一个运算符)。
  6. printf("There were %d words and %d lines.\n",words, lines);
  7.  执行完第7行后,a是5,b是2。 执行完第8行后,a和b都是5。执行完第9行后,a和b仍然是5(注意,a不会是2,因为在执行a = b;时,b的值已经被改为5)。
  8. 执行完第7行后,x是10,b是5。执行完第8行后,x是10,y是15。执行完第9行后,x是150,y是15。
  • 14
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值