当前位置:   article > 正文

大一学生一周十万字爆肝版C语言总结笔记_大一c语言笔记

大一c语言笔记

目录

前言

大学计算机基础教育:

为什么选择C语言入门?

大牛给计算机专业学生的 7 个建议

进行程序设计要解决以下的两个问题:

每一位学编程的都应该好好读读的Zen of Python(Python之禅)

后记:

第1章程序设计和C语

C语言程序的结构

C语言中允许用两种注释方式:

C语言中的保留字:

scanf小结:

对自定义函数调用的理解:

C语言的编译过程是怎样的

程序设计的任务

第一章课后题答案:

目录回顾:

第2章算法——程序的灵魂

算法的分类:

算法的特征:

第二章目录回顾:

第3章最简单的C程序设计——顺序程序设计

C语言中反斜杠"\"的意义和用法

在C语言中数据有两种形式:常量和变量

常量

变量

标识符:

数据类型

运算符

算术优先级:

数据的输入输出

C语言%7.2d、%-7d、%7.2f、%0.2f的含义和区别

字符串函数:

小结:基本数据类型 

位、字节和字 

第三章习题答案:

第三章目录回顾:

第4章选择结构程序设计

*算术符号优先次序*:

Switch语句的用法:

 选择结构典型例题:

有3个整数a, b, c,由键盘输入,输出其中最大的数。

 给出一百分制成绩,要求输出成绩等级’A’、‘B’、‘C’、‘D’、‘E’。 90分以上为’A’,8089分为’B’,7079分为’C’ ,60~69分为’D’ ,60分以下为’E’。

给一个不多于5位的正整数,要求:①求出它是几位数;②分别输出每一位数字;③按逆序输出各位数字,例如原数为321,应输出123。

课后第十题:

第四章目录回顾

第5章循环结构程序设计

结构化程序设计的3种基本结构:

5.2用while语句实现循环

do-while和while的区别

5.4用for语句实现循环

5.5循环的嵌套

5.7改变循环执行的状态

continue和break的区别

第五章典型题(循环):

斐波那契数列

第五章习题答案:

第五章目录回顾:

第6章利用数组处理批量数据

怎样定义和引用一维数组

用数组来求fibonacci数列问题:

冒泡排序例题:

6.2怎样定义和引用二维数组

*交换行和列*

6.3字符数组

6.3.1怎样定义字符数组

6.3.4字符串和字符串结束标志

6.3.5字符数组的输入输出

6.3.6使用字符串处理函数

puts函数——输出字符串的函数

gets函数——输入字符串的函数

Strcat函数———字符串连接函数

Strcpy和strncpy——字符串复制函数

Strcmp——字符串比较函数

Strupr函数——转换为大写的函数

6.3.7字符数组应用举例

第六章课后习题:

3. 求一个3 X 3的整形矩阵对角线元素之和

5. 将一个数组中的值按逆序重新存放。例如:原来顺序为8,6,5,4,1。要求改为1,4,5,6,8。

第六章目录回顾:

7.1为什么要用函数

为什么要定义函数?

定义函数应包括以下内容:

定义函数的方法:

 

7.3调用函数

7.3.1函数调用的形式171

7.3.2函数调用时的数据传递

7.3.3函数调用的过程

7.4对被调用函数的声明和函数原型

7.5函数的嵌套调用

 

7.6函数的递归调用

7.7数组作为函数参

静态局部变量的值(static局部变量):

第七章课后题

第七章目录回顾:

第8章善于利用指针

数组和指针 

指针的四方面的内容:

指针的算术运算

运算符&和*

指针表达式

数组和指针的关系

指针和结构类型的关系

指针和函数的关系

指针类型转换

指针的安全问题

第八章典型例题:

第八章目录回顾

第9章用户自己建立数据类型

示例问题:创建图书目录 

建立结构声明 

 访问结构成员 

指向结构的指针 

关键概念 

本章小结 

第九章目录:

第九章课后题答案:

第10章对文件的输入输出

与文件进行通信 

文件是什么 

文本模式和二进制模式 

关键概念 

本章小结 

第十章课后习题:

第十章目录回顾

一些典型题:

C语言——经典200道实例【基础例题100道——进阶例题100道】_刘鑫磊up的博客-CSDN博客_c语言基础编程100道

输出单个字符

输出浮点数

输出双精度(double)数

字符转 ASCII 码

计算字节大小

不使用临时变量交换两个整数的值

奇偶数判断(利用二进制)

循环输出 26 个字母

直接条件判断闰年

判断素数

求一个整数的所有因数

计算一个数是否可为两个素数之和

二进制与十进制相互转换

删除字符串中的特殊字符

进阶学习:

前言

是对C语言程序设计的一次期末总结,也是对未来从事相关工作的一次展望和打下基础。编程能力从来不会是一蹴而就的,而是日积月累的反反复复的练习和思考总结出来的,不是多少天就可以速成的。

编程是一项引发积极思维的活动,它不是一种简单的技能,不是只要数据有关规则,熟能生巧就能完成任务的编程,需要指挥编写每一个程序,都要积极开动脑筋,发挥创造精神,编程是一件很灵活的工作,没有标准答案,不同的人可以写出不同的程序。

计算机的本质是“程序的机器”,程序和指令思想是计算机系统中最基本的概念。

中国大学MOOC(慕课)_优质在线课程学习平台_中国大学MOOC(慕课) (icourse163.org)

强烈推荐翁恺老师的所有课程!!!

要看图片的去专栏看10个章节的分总结

C语言_时雨h的博客-CSDN博客

大学计算机基础教育:

  1. 信息技术的发展
  2. 面向应用的需求
  3. 科学思维的培养

为什么选择C语言入门?

C++是为了处理较大规模的程序开发而研制的大型语言,C语言更适合解决某些小型程序的编程,作为传统的面向过程的程序设计语言,在编写底层的设备驱动程序和内嵌应用程序是,往往是更好的选择。

大牛给计算机专业学生的 7 个建议

大牛给计算机专业学生的 7 个建议( C语言程序设计上美国资深软件专家写的《对计算机系学生的建议》)_shaozheng0503的博客-CSDN博客

进行程序设计要解决以下的两个问题:

  1. 要学习和掌握解决问题的思路和方法,即算法。
  2. 学习怎样实现算法,即用计算机语言编写程序。

每一位学编程的都应该好好读读的Zen of PythonPython之禅)

Beautiful is better than ugly. (优美比丑陋好)

Explicit is better than implicit.(清晰比晦涩好)

Simple is better than complex.(简单比复杂好)

Complex is better than complicated.(复杂比错综复杂好)

Flat is better than nested.(扁平比嵌套好)

Sparse is better than dense.(稀疏比密集好)

Readability counts.(可读性很重要)

Special cases aren't special enough to break the rules.(特殊情况也不应该违反这些规则)

Although practicality beats purity.(但现实往往并不那么完美)

Errors should never pass silently.(异常不应该被静默处理)

Unless explicitly silenced.(除非你希望如此)

In the face of ambiguity, refuse the temptation to guess.(遇到模棱两可的地方,不要胡乱猜测)

There should be one-- and preferably only one --obvious way to do it.(肯定有一种通常也是唯一一种最佳的解决方案)

Although that way may not be obvious at first unless you're Dutch.(虽然这种方案并不是显而易见的,因为你不是那个荷兰人[这里指的是Python之父Guido])

Now is better than never.(现在开始做比不做好)

Although never is often better than *right* now.(不做比盲目去做好[极限编程中的YAGNI原则])

If the implementation is hard to explain, it's a bad idea.(如果一个实现方案难于理解,它就不是一个好的方案)

If the implementation is easy to explain, it may be a good idea.(如果一个实现方案易于理解,它很有可能是一个好的方案)

Namespaces are one honking great idea -- let's do more of those!(命名空间非常有用,我们应当多加利用)

后记:

2022年12月31日星期六完成了对C语言的总结,是对未来的一次铺垫,也是对自己过去的一次总结,新的一年里要努力提升自己的能力,不要沉沦于过去,努力看向未来,卷起来吧,少年!

第1章程序设计和C语言

程序:一组计算机能识别和执行的指令。 

从打下第一行hello world 开始,第一次接触main主函数开始,从第一次调用头文件开始,从第一次return一个返回值开始...... 我们就认识了一个最简单的 C语言程序“This is a C program”

在屏幕上显示一个短句Hello World!


#include <stdio.h>

int main(void)

{

/*---------*/

printf( "Hello World!");

return 0;

}

C语言必须通过编译连接才能执行

算法+数据结构=程序

软件:程序+相关文档

 

C语言程序的结构

(1)C语言程序主要由函数构成,函数是C语言程序的基本单位。一个C语言源程序必须有一个main函数,可以包含一个main函数和若干个其他函数。主函数可以调用其他函数,其他函数之间可以互相调用,但其他函数不能调用主函数。被调用的函数可以是系统提供的库函数(如printf和scanf函数),也可以是用户根据自己需要自己编制设计的函数。C语言的函数相当于其他语言中的子程序。用函数来实现特定功能。程序全部工作都是由各个函数分别完成的。编写C语言程序就是编写函数。

(2)一个函数由两部分组成

①函数首部。即函数的第一行,包括:函数名、函数类型、函数参数(形式参数)名和参数类型。一个函数名后面必须跟一对圆括号,括号内写函数的参数名及其类型。函数可以没有参数,如:

int mian()

②函数体。即函数首部下面的花括号内的部分。如果一个函数内有多个花括号,以最外层的一对花括号为函数体的范围。函数体一般包括声明部分(声明有关变量和函数类型)和执行部分(执行函数语句)。

(3)一个C语言程序总是从main函数开始执行的,而不论main函数在整个程序中的位置如何。

(4)C语言程序书写格式自由,一行内可以写几个语句,一个语句可以分写在多行上。

(5)每个语句和数据声明的最后必须有一个分号。

C语言本身没有输入输出语句。输入和输出的操作是由库函数scanf和printf等函数来完成的。

可以用“//”对程序进行注释,注释不被编译,不生成目标程序,不影响程序运行结果。

C语言中允许用两种注释方式:

(1)//开始的当行注释

(2)/*   */的块式注释

C语言中的保留字:

scanf小结:

scanf()函数是格式化输入函数,它从标准输入设备(键盘) 读取输入的信息。

其调用格式为:      scanf("<格式化字符串>",<地址表>);

格式化字符串包括以下三类不同的字符;

1、 格式化说明符:

格式化说明符与printf()函数中的格式说明符基本相同。但和printf()函数中格式字符串的用法有一些小区别。我们来看下面这个表。

格式字符 说明

%d   从键盘输入十进制整数

%o    从键盘输入八进制整数

%x 从键盘输入十六进制整数

%c   从键盘输入一个字符

%s 从键盘输入一个字符串

%f 从键盘输入一个实数

%e 与%f的作用相同

附加格式说明字符表    

字符  说明

L   输入"长"数据

H 输入"短"数据

M 指定输入数据所占宽度

* 空读一个数据

                                                        

2、 空白字符: 空白字符会使scanf()函数在读操作中略去输入中的一个或多个空白字符。

3、 非空白字符: 一个非空白字符会使scanf()函数在读入时剔除掉与这个非空白字符相同的字符。

对自定义函数调用的理解:

与书第八页例1.3类似    一定要记住不论调用什么函数前一定要记得声明

#include "stdio.h"

int main(void)

{

    int m, n;

    int repeat, ri;

    double s;

    double fact(int n);  //也是第一次见到自定义函数的调用(注意此时自变量到因变量的变化,形参和实参之间的转化)

    scanf("%d", &repeat);

    for(ri = 1; ri <= repeat; ri++){

        scanf("%d%d", &m, &n);

        s=fact(n)/(fact(m)*fact(n-m));        

        printf("result = %.0f\n", s);

    }

}

double fact(int n)

{

   double result;

   int i;

   result=1;

   for(i=1;i<=n;i++)

       result=result*i;  

   return result; //注意返回的是什么值!   n>>result的变换

C语言的编译过程是怎样的

1.预处理(Preprocessing), 2.编译(Compilation), 3.汇编(Assemble), 4.链接(Linking)。

程序设计的任务

程序设计是指从确定任务到得到结果,写出文档的全过程。一般分为以下几个工作阶段:

(1)问题分析       谋定而后动!!!

(2)设计算法 用哪些方法,步骤,一般使用流程图

(3)编写程序    根据得到的算法,用一种高级语言程序编写出源程序

(4)对源程序进行编辑 编译和连接   得到可执行程序

(5)运行程序 分析结果

(6)编写程序文档 向用户提供使用说明书  +

编程是一项引发积极思维的活动,它不是一种简单的技能,不是只要数据有关规则,熟能生巧就能完成任务的编程,需要指挥编写每一个程序,都要积极开动脑筋,发挥创造精神,编程是一件很灵活的工作,没有标准答案,不同的人可以写出不同的程序。

第一章课后题答案:

C语言程序设计第五版 谭浩强 第五版课后答案_月已满西楼的博客-CSDN博客_c程序设计谭浩强第五版课后答案

什么是程序?什么是程序设计?

程序:就是一组能识别和执行的指令,每一条指令使计算机执行特定的操作

程序设计:是指从确定任务到得到结果、写出文档的全过程

2.为什么需要计算机语言?高级语言有哪些特点?

为什么需要计算机语言:计算机语言解决了人和计算机交流是的语言问题,使得计算机和人都能识别

高级语言有哪些特点:

2.1 高级语言的数据结构要比汇编和机器语言丰富;

2.2 高级语言与具体机器结构的关联没有汇编以及机器语言密切;

2.3 高级语言更接近自然语言更容易掌握;

2.4 高级语言编写的程序要经过编译或解释计算机才能执行;

3.正确理解以下名词及其含义:

(1)源程序,目标程序,可执行程序。

​源程序:指未编译的按照一定的程序设计语言规范书写的文本文件,是一系列人类可读的计算机语言指令

目标程序:为源程序经编译可直接被计算机运行的机器码集合,在计算机文件上以.obj作扩展名

可执行程序:将所有编译后得到的目标模块连接装配起来,在与函数库相连接成为一个整体,生成一个可供计算机执行的目标程序,成为可执行程序

(2)程序编辑,程序编译,程序连接。

程序编辑:上机输入或者编辑源程序。

程序编译:

先用C提供的“预处理器”,对程序中的预处理指令进行编译预处理

对源程序进行语法检查, 判断是否有语法错误,直到没有语法错误未知

编译程序自动把源程序转换为二进制形式的目标程序

程序连接:将所有编译后得到的目标模块连接装配起来,在与函数库相连接成为一个整体的过程称之为程序连接

(3)程序,程序模块,程序文件。

程序:一组计算机能识别和执行的指令,运行于电子计算机上,满足人们某种需求的信息化工具

程序模块:可由汇编程序、编译程序、装入程序或翻译程序作为一个整体来处理的一级独立的、可识别的程序指令

程序文件:程序的文件称为程序文件,程序文件存储的是程序,包括源程序和可执行程序

(4)函数,主函数,被调用函数,库函数。

函数:将一段经常需要使用的代码封装起来,在需要使用时可以直接调用,来完成一定功能

主函数:又称main函数,是程序执行的起点

被调用函数:由一个函数调用另一个函数,则称第二个函数为被调用函数

库函数:一般是指编译器提供的可在c源程序中调用的函数。可分为两类,一类是c语言标准规定的库函数,一类是编译器特定的库函数

(5)程序调试,程序测试。

程序调试:是将编制的程序投入实际运行前,用手工或编译程序等方法进行测试,修正语法错误和逻辑错误的过程

程序测试:是指对一个完成了全部或部分功能、模块的计算机程序在正式使用前的检测,以确保该程序能按预定的方式正确地运行

 

目录回顾:

第1章程序设计和C语言1

1.1什么是计算机程序1

1.2什么是计算机语言1

1.3C语言的发展及其特点3

1.4最简单的C语言程序5

1.4.1最简单的C语言程序举例5

1.4.2C语言程序的结构9

1.5运行C程序的步骤与方法11

1.6程序设计的任务13

第2章算法——程序的灵魂

程序=算法+数据结构

什么是算法?_拉杆给油不要慌的博客-CSDN博客_算法

算法(algorithm)是解决一系列问题的清晰指令,也就是,能对一定规范的输入,在有限的时间内获得所要求的输出。 6是解决做什么和怎么做的问题

        简单来说,算法就是解决一个问题的具体方法和步骤。算法是程序的灵魂。

程序:(1)对数据的描述。   在程序中要制定用到哪些数据,以及这些数据的组织形式。这也是大名鼎鼎的数据结构(我还在期待着马上能啃这块)

  1. 对操作的描述。 要求计算机进行操作的步骤。(算法

如何得到结构化的程序:

  1. 自顶向下
  2. 逐步细化
  3. 模块化设计
  4. 结构化编码

算法的分类:

这块还在学习思考,期待未来我可以补上这一块,谈一谈我自己的看法和鄙见(希望不会咕咕咕太久)

十大基本算法介绍__陌默的博客-CSDN博客_算法

1、冒泡排序

2、选择排序

3、插入排序

4、希尔排序

5、归并排序

6、快速排序

7、堆排序

8、计数排序

9、桶排序

10、基数排序

算法的特征:

一个算法应该具有以下五个重要的特征:

有穷性

(Finiteness)

算法的有穷性是指算法必须能在执行有限个步骤之后终止;

确切性

(Definiteness)

算法的每一步骤必须有确切的定义;

输入项

(Input)

一个算法有0个或多个输入,以刻画运算对象的初始情况,所谓0个输入是指算法本身定出了初始条件;

输出项

(Output)

一个算法有一个或多个输出,以反映对输入数据加工后的结果。没有输出的算法是毫无意义的;

可行性

(Effectiveness)

算法中执行的任何计算步骤都是可以被分解为基本的可执行的操作步骤,即每个计算步骤都可以在有限时间内完成(也称之为有效性)。

第二章课后答案:

C程序设计第五版谭浩强课后答案 第二章答案_月已满西楼的博客-CSDN博客_c语言程序设计谭浩强第五版第二章课后答案

1. 什么是算法?试从日常生活中找3个例子,描述它们的算法

算法:简而言之就是求解问题的步骤,对特定问题求解步骤的一种描述。

比如生活中的例子:

考大学

首先填报志愿表、交报名费、拿到准考证、按时参加考试、收到录取通知书、按照日期到指定学校报到。

去北京听演唱会

首先在网上购票、然后按时坐车到北京,坐车到演唱会会场。

把大象放进冰箱

先打开冰箱门,然后将大象放进冰箱,关冰箱。

2. 什么叫结构化的算法?为什么要提倡结构化的算法?

结构化算法:由一些顺序、选择、循环等基本结构按照顺序组成,流程的转移只存在于一个基本的范围之内。

结构化算法便于编写,可读性高,修改和维护起来简单,可以减少程序出错的机会,提高了程序的可靠性,保证了程序的质量,因此提倡结构化的算法。

3. 试述3种基本结构的特点,请另外设计两种基本结构(要符合基类结构的特点)

结构化程序设计方法主要由以下三种基本结构组成:

顺序结构:顺序结构是一种线性、有序的结构,它依次执行各语句模块

选择结构:选择结构是根据条件成立与否选择程序执行的通路。

循环结构:循环结构是重复执行一个或几个模块,直到满足某一条件位置

重新设计基本结构要满足以下几点:

只有一个入口

只有一个出口

结构内的每一部分都有机会执行到

结构内不存在死循环

第二章目录回顾:

2.1程序=算法+数据结构15

2.2什么是算法16

2.3简单的算法举例17

2.4算法的特性20

2.5怎样表示一个算法21

2.5.1用自然语言表示算法21

2.5.2用流程图表示算法21

2.5.3三种基本结构和改进的流程图25

2.5.4用N-S流程图表示算法28

2.5.5用伪代码表示算法31

2.5.6用计算机语言表示算法32

2.6结构化程序设计方法33

第3章最简单的C程序设计——顺序程序设计

计算物体自由下落的距离 

一个物体从 100m 的高空自由落下,编写程序,求它在前 3s 内下落的垂直距离(结果保留2位小数)。设重力加速度为10米/秒^2。

#include <stdio.h>

int main(void)

{

    double height;

    height=0.5 * 10 * 3* 3;   /*---------*/

    printf("height = %.2f\n", height);

}

阶梯电价

输入一个正整数repeat (0<repeat<10),做repeat次下列运算:

为了提倡居民节约用电,某省电力公司执行"阶梯电价",安装一户一表的居民用户电价分为两个"阶梯":月用电量50千瓦时以内的,电价为0.53元/千瓦时;超过50千瓦时的用电量,电价上调0.05元/千瓦时。

输入用户的月用电量e(千瓦时),计算并输出该用户应支付的电费(元),结果保留2位小数。

输入输出示例:括号内是说明

输入

2        (repeat=2)

10       (e=10)

100      (e=100)

输出

cost = 5.30

cost = 55.50

#include <stdio.h>

int main(void)

{

    int repeat, ri;

    double cost, e;

    scanf("%d", &repeat);

    for(ri = 1; ri <= repeat; ri++){

        scanf("%lf", &e);

        if(e<=50)   cost=e*0.53;

else cost=0.53*50+(e-50)*0.58;

/*---------*/

        printf("cost = %.2f\n", cost);

}

}

求一元二次方程的根 

输入一个正整数repeat (0<repeat<10),做repeat次下列运算:

输入参数a,b,c,求一元二次方程a*x*x+b*x+c=0的根,结果保留2位小数。

输出使用以下语句:

printf("参数都为零,方程无意义!\n");

printf("a和b为0,c不为0,方程不成立\n");

printf("x = %0.2f\n", -c/b);

printf("x1 = %0.2f\n", (-b+sqrt(d))/(2*a));

printf("x2 = %0.2f\n", (-b-sqrt(d))/(2*a));

printf("x1 = %0.2f+%0.2fi\n", -b/(2*a), sqrt(-d)/(2*a));

printf("x2 = %0.2f-%0.2fi\n", -b/(2*a), sqrt(-d)/(2*a));

输入输出示例:括号内为说明

输入:

5               (repeat=5)

0 0 0           (a=0,b=0,c=0)

0 0 1           (a=0,b=0,c=1)

0 2 4           (a=0,b=2,c=4)

2.1 8.9 3.5     (a=2.1,b=8.9,c=3.5)

1 2 3           (a=1,b=2,c=3)

输出:

参数都为零,方程无意义!

a和b为0,c不为0,方程不成立

x = -2.00

x1 = -0.44

x2 = -3.80

x1 = -1.00+1.41i

x2 = -1.00-1.41i

#include <stdio.h>

#include <math.h>

int main(void)

{

    int repeat, ri;

    double a, b, c, d;

    scanf("%d", &repeat);

    for(ri = 1; ri <= repeat; ri++){

        scanf("%lf%lf%lf", &a, &b, &c);

d=b*b-4*a*c;

if(a==0){

if(b==0){

if(c==0) printf("参数都为零,方程无意义!\n");

else printf("a和b为0,c不为0,方程不成立\n");

}

else printf("x = %0.2f\n", -c/b);

}

else{

if(d>=0){

printf("x1 = %0.2f\n", (-b+sqrt(d))/(2*a));

printf("x2 = %0.2f\n", (-b-sqrt(d))/(2*a));

}

else{

printf("x1 = %0.2f+%0.2fi\n", -b/(2*a), sqrt(-d)/(2*a));

printf("x2 = %0.2f-%0.2fi\n", -b/(2*a), sqrt(-d)/(2*a));

}

}

    }     

}

参考2

#include <stdio.h>

#include <math.h>

int main(void)

{

    int repeat, ri;

    double a, b, c, d;

    scanf("%d", &repeat);

    for(ri = 1; ri <= repeat; ri++){

        scanf("%lf%lf%lf", &a, &b, &c);

        d=b*b-4*a*c;

        if(a==0&&b==0&&c==0)     printf("参数都为零,方程无意义!\n");

        if(a==0&&b==0&&c!=0)    printf("a和b为0,c不为0,方程不成立\n");

        if(a==0&&b!=0)

                  printf("x = %0.2f\n", -c/b);

        if(a!=0&&d>=0){

               printf("x1 = %0.2f\n", (-b+sqrt(d))/(2*a));

               printf("x2 = %0.2f\n", (-b-sqrt(d))/(2*a));

         }

         if(a!=0&&d<0){

                 printf("x1 = %0.2f+%0.2fi\n", -b/(2*a), sqrt(-d)/(2*a));

                 printf("x2 = %0.2f-%0.2fi\n", -b/(2*a), sqrt(-d)/(2*a));

          }    

    }     

}

C语言中反斜杠"\"的意义和用法

C语言中反斜杠"\"的意义和用法_yf夜风的博客-CSDN博客_\

在阅读C语言代码经常可以看到代码中出现反斜杠"\",不是很明白它的意思,遂对反斜杠"\"的意义和用法进行查询和总结记录:

1. 转义字符

非常常用的一种用法,在反斜杠后面加一个字符,表示一些特定的意思,如:

\n     换行符(LF)

\r     回车(CR) ,相当于键盘上的"Enter"

\t     跳到下一个TAB位置

\0     空字符(NULL)

\'     单引号(撇号)

\"     双引号

\\     代表一个反斜线字符''\' 等,详细可百度“转义字符”。

例:

a.

printf("Hello World!");

printf("Hello World!");

输出:

Hello World!Hello World!

b.

printf("Hello World!\n");

printf("Hello World!\n");

输出:

Hello World!

Hello World!

2. 继续符

可用于一行的结尾,表示本行与下一行连接起来

C语言中以 ; 作为语句的结束,不以行为单位结束,当一行的内容太长不方便卸载一行时可使用反斜杠"\"作为继续符,分为多行书写

例如:STM32官方库文件"stm32f30x_usart.h"有如下一段:

#define IS_USART_123_PERIPH(PERIPH) (((PERIPH) == USART1) || \

                                     ((PERIPH) == USART2) || \

                                     ((PERIPH) == USART3))

写成一行意义完全相同:

#define IS_USART_123_PERIPH(PERIPH) (((PERIPH) == USART1) || ((PERIPH) == USART2) ||  ((PERIPH) == USART3))

C语言中数据有两种形式:常量和变量

C语言数据类型(超详细)_无念至此的博客-CSDN博客_c语言数据

常量

可以分为

  1. 整型常量: 1000,-345等
  2. 实型常量:①十进制小数 123.456,-34.45。0.0②12.34e3(代表12.34*10的三次方)——由于在计算机中输入输出无法表示上角或下角,所以以E或e 代表以10为底的指数。
  3. 字符常量:①普通字符”a” 代表97(以二进制形式存放)

②转义字符\n等(上面有介绍在此不赘述了)

  1. 字符串常量:“boy”“123”(双撇号里可以包含一个字符串,单撇号里只能包含一个字符
  2. 符号常量:用#define指令指定用一个符号名称代表一个常量

#define PI 3.1256 //注意行末没有分号

(学会使用这种方法在程序中提升效率,但不要忘记自己定义的符号常量代表的是什么)

变量

记住一定是先定义后使用

1.什么是变量?

         * 变量本质上来说是内存中的一块空间,这块空间"有数据类型"、"有名字"、"有字面值"。

         * 变量包含三部分:数据类型、名称、字面值【数据】

         * 变量是内存中储存数据的最基本的单元。

     2.数据类型的作用?

         * 不同的数据有不同的类型,不同的数据类型底层会分配不同大小的空间。

         * 数据类型是指导程序在运行阶段应该分配多大的内存空间。

     

     3.变量要求:变量中存储的具体的"数据"必须和变量的"数据类型"一致,当不一致的时候编译报错。

     4.声明/定义变量的语法格式:

         数据类型 变量名;

     5.变量声明之后怎么赋值?

         语法格式:

             变量名 = 字面值;

             要求: 字面值的数据类型必须和变量的数据类型一致

         = 等号是一个运算符,叫做赋值运算符,赋值运算符先运算等号右边的表达式,表达式执行结束之后赋值给

         左边的变量

     6.声明和赋值可以放到一起完成。

         int i = 10;

     7.变量赋值以后,可以重新赋值,变量的值可以变化;

     8.有了变量的概念之后,内存空间的得到了重复的使用:

     9.通常访问一个变量包括两种访问形式:

       *第一种:读取变量中保存的具体数据 get/获取

       *第二种:修改变量中保存的具体数据 set/设置

       i = 10; //set

       System.out.println(i); //get

     10.变量在一行上可以声明多个

         int a,b,c

     11.java中的变量必须先声明,在赋值,才能访问

     int i; 程序执行到这里,内存空间并没有开辟出来,变量i并没有初始化。所以没有赋值之前是无法访问

      12.在方法体当中的java代码,是遵守自上而下的顺序一次执行的。逐行执行

           第一行

           第二行

           第三行

           特点:在第二行的我代码必须完整的结束之后,第三行程序才能执行。

      13.在同一个“作用域”当中,变量名不能重名,但是变量可以重新赋值。

变量的作用域

         

         1.什么是作用域?

             变量的作用域,其实描述的就是变量的有效范围。

             在什么范围之内是可以被访问的,只要出了这个范围该变量就无法访问了

         2.变量的作用域只要记住一句话:

             出了大括号就不认识了。

关于变量的分类:

          根据变量声明的位置来分类:

              *局部变量

               -在方法体当中声明的变量叫做局部变量

              *成员变量

               -在方法体外【类体之内】声明的变量叫做成员变量

      

      在不同的作用域当中,变量名是可以相同的  

思考:

标识符:

(4条消息) 标识符——C语言基础知识_一抹南伤的博客-CSDN博客    

用来对变量,符号常量名,函数,数组类型等命名的有效字符序列统称为标识符(identifier)

例子:sum,average,huangshaozheng,_total(下划线:英文输入模式下shift+-)   

Student_name ......(后面定义结构体变量时会很好表示变量的名称)    

还要注意CLASS和class并不能表示同一变量,他们是两个不同的变量

数据类型

(4条消息) C语言基本数据类型_今天你debug了嘛?的博客-CSDN博客_c语言数据类型  

                    

一、整数类型

1.整型变量

整数类型数据即整型数据,整型数据没有小数部分的数值。整型数据可分为:基本型、短整型、长整型和无符号型四种。

基本型:以int表示。

短整型:以short int表示。

长整型:以long int表示。

无符号型:存储单元中全部二进位用来存放数据本身,不包括符号。无符号型中又分为无符号整型、无符号短整型和无符号长整型,分别以unsigned int,unsigned short和unsigned long表示。

要注意的是,不同的计算机体系结构中这些类型所占比特数有可能是不同的,下面列出的是32位机中整型家族各数据类型所占的比特数。

虽然int与unsigned int所占的位数一样,但int的最高位用作了符号位,而unsigned int的最高位仍为数据位,所以它们的取值范围不同。

若要查看适合当前计算机的各数据类型的取值范围,可查看文件“limits.h”(通常在编译器相关的目录下),如下是“limits.h”的部分示例。

#define CHAR_BIT      8         /* number of bits in a char */

#define SCHAR_MIN   (-128)      /* minimum signed char value */

#define SCHAR_MAX     127       /* maximum signed char value */

#define UCHAR_MAX     0xff      /* maximum unsigned char value */

#ifndef _CHAR_UNSIGNED

#define CHAR_MIN    SCHAR_MIN   /* mimimum char value */

#define CHAR_MAX    SCHAR_MAX   /* maximum char value */

#else

#define CHAR_MIN      0

#define CHAR_MAX    UCHAR_MAX

#endif  /* _CHAR_UNSIGNED */

#define MB_LEN_MAX    2             /* max. # bytes in multibyte char */

#define SHRT_MIN    (-32768)        /* minimum (signed) short value */

#define SHRT_MAX      32767         /* maximum (signed) short value */

#define USHRT_MAX     0xffff        /* maximum unsigned short value */

#define INT_MIN     (-2147483647 - 1) /* minimum (signed) int value */

#define INT_MAX       2147483647    /* maximum (signed) int value */

#define UINT_MAX      0xffffffff    /* maximum unsigned int value */

#define LONG_MIN    (-2147483647L - 1) /* minimum (signed) long value */

#define LONG_MAX      2147483647L   /* maximum (signed) long value */

#define ULONG_MAX     0xffffffffUL  /* maximum unsigned long value */

在嵌入式开发中,经常需要考虑的一点就是可移植性的问题。通常,字符是否为有符号数会带来两难的境地,因此,最佳妥协方案就是把存储于int型变量的值限制在signed int和unsigned int的交集中,这可以获得最大程度上的可移植性,同时又不牺牲效率。

2.整型常量

C语言整型数据一般有十进制整数、八进制整数和十六进制整数三种表达形式。说明如下。

十进制整数的表示与数学上的表示相同,例如:

256,-321,0

八进制整数的表示以数字0开头,例如:

0234表示八进制整数(234)8,所对应的十进制数为 。2×82+3×81+4×80=156。

十六进制整数的表示以0x开头,例如:

0×234表示十六进制整数(234)16。(应当注意的是十六进制数前导字符0x,x前面是数字(0)。

在一个整型数据后面加一个字母L或l(小写),则认为是long int型量。如342L、0L、78L等,这往往用于函数调用中。如果函数的形参为long int型,则要求实参也为long int型,此时需要用342L作实参。

二、实数(浮点)类型

1.实数(浮点)变量

实型变量又可分为单精度(float)、双精度(double)和长双精度(long double)3种。列出的是常见的32位机中实型家族各数据类型所占的比特数。

要注意的是,这里的有效数字是指包括整数部分+小数部分的全部数字总数。

小数部分所占的位(bit)越多,数的精度就越高;指数部分所占的位数越多,则能表示的数值范围就越大。下面程序就显示了实型变量的有效数字位数。

#include<stdio.h>

int main()

{

float a;

double b;

a = 33333.33333;

b = 33333.333333;

printf(" a = %f , b = %lf \n" , a , b );

 return 0;

程序执行结果如下:

a=33333.332031 , b=33333.333333

1

可以看出,由于a为单精度类型,有效数字长度为7位,因此a的小数点后4位并不是原先的数据而由于b为双精度类型,有效数字为16位,因此b的显示结果就是实际b的数值。

2.实型(浮点)常量

在C语言程序设计中,实型数据有以下两种表达形式。

十进制数形式。由正负号、数字和小数点组成。如5.734、一0.273、0.8、一224等都是十进制数形式。

指数形式。如546E+3或546E3都代表546×10 3。字母E(或e)之前必须有数字,E(或e)后面的指数必须为整数。

E8、4.6E+2.7、6e、e、9E7.5都是不合法的指数形式;

5.74E-7、-3E+6是合法的指数形式实型常量。

三.字符类型

1.字符变量

字符变量可以看作是整型变量的一种,它的标识符为“char”,一般占用一个名节(8bit),它也分为有符号和无符号两种,读者完全可以把它当成一个整型变量。当它用于存储字符常量时,实际上是将该字符的ASCⅡ码值(无符号整数)存储到内存单元中。

实际上,一个整型变量也可以存储一个字符常量,而且也是将该字符的ASCH码值(无符号整数)存储到内存单元中。但由于取名上的不同,字符变量则更多地用于存储字符常量。以下一段小程序显示了字符变量与整型变量实质上是相同的。

#include<stdio.h>

int main()

{

char a,b;

int c,d;

/*赋给字符变量和整型变量相同的整数常量*/

a=c=65;

/*赋给字符变量和整型变量相同的字符常量*/

b=d='a';

/*以字符的形式打印字符变量和整型变量*/

printf("char a=%c,int c = %c", a,c);

/*以整数的形式打印字符变量和整型变量*/

printf("char b=%d,int d=%d\n",b,d);

return 0;

程序执行结果如下:

char a=A,int c=A;

char b=97,int d=97;

由此可见,字符变量和整型变量在内存中存储的内容实质是一样的。

2.字符常量

字符常量是指用单引号括起来的一个字符,如:‘a’,‘5’,‘?’ 等都是字符常量。以下是使用字符常量时容易出错的地方,请读者仔细阅读。

字符常量只能用单引号括起来,不能用双引号或其他括号。

字符常量只能是单个字符,不能是字符串。

字符可以是字符集中任意字符。但数字被定义为字符型之后就不能参与数值运算。如’5’和5是不同的。‘5’是字符常量,不能直接参与运算,而只能以其ASCⅡ码值(053)来参与运算。

除此之外,C语言中还存在一种特殊的字符常量——转义字符。转义字符以反斜线“\”开头,后跟一个或几个字符。转义字符具有特定的含义,不同于字符原有的意义,故称“转义”字符。

例如,在前面各例题printí函数的格式串中用到的“\n”就是一个转义字符,其意义是“回车换行”。转义字符主要用来表示那些用一般字符不便于表示的控制代码。

常见的转义字符以及它们的含义。

‘0’ 的ASCII码 48,‘A’ 的ASCII码 65,‘a’ 的ASCCII码 97

 C语言中‘a‘和“a“有什么区别?_嵌入式Linux系统开发的博客-CSDN博客_c语言'a'和“a”

四.枚举类型

在实际问题中,有些变量的取值被限定在一个有限的范围内。例如,一个星期内只有7天,一年只有12个月,一个班每周有6门课程等。如果把这些量说明为整型、字符型或其他类型显然是不妥当的。

为此,C语言提供了一种称为枚举的类型。在枚举类型的定义中列举出所有可能的取值,被定义为该枚举类型的变量取值不能超过定义的范围。

枚举类型是一种基本数据类型,而不是一种构造类型,因为它不能再分解为任何基本类型。

枚举类型定义的一般形式如下。

enum 枚举名

{

枚举值表

};

在枚举值表中应罗列出所有可用值,这些值也称为枚举元素。

例如,定义一个枚举类型和枚举变量如下:

enum colorname

{red,yellow,blue,white,black};

enum colorname color;

变量color是枚举类型enum colorname,它的值只能是red、yellow、blue、white例如

下面的赋值合法:

color=red;

color=white;

而下面的赋值则不合法:

color=green;

color=orange;

关于枚举类型有几点说明:

enum是关键字,标识枚举类型,定义枚举类型必须以enum开头。

在定义枚举类型时花括号中的名字称为枚举元素或枚举常量。它们是程序设计者自己指定的,命名规则与标识符相同。这些名字并无固定的含义,只是一个符号,程序设计者仅仅是为了提高程序的可读性才使用这些名字。

枚举元素不是变量,不能改变其值。如下面这些赋值是不对的:

red=8;yellow=9;

但枚举元素作为常量,它们是有值的。从花括号的第一个元素开始,值分别是0、1、2、3、4,这是系统自动赋给的,可以输出。例如:

printf("%d",blue);

输出的值是2。但是定义枚举类型时不能写成:

enum colorname{0,1,2,3,4};

必须用符号red,yellow,……,或其他标识符。

可以在定义类型时对枚举常量初始化:

enum colornmae{red=3,yellow,blue,white=8,black};

此时,red为3,yellow为4,blue为5,white为8,black为9。因为yellow在red之后,red为3,yellow顺序加一,同理black为9。

④枚举常量可以进行比较。例如:

if(color==red) printf("red");

if(color!=black) printf("it is not black!”);

if(color>white) printf(“it is black!”);

它们是按所代表的整数进行比较的。

⑤一个枚举变量的值只能是这几个枚举常量之一,可以将枚举常量赋给一个枚举变量。但不能将一个整数赋给它。例如:

color=black; //正确

color=5;///错误

⑥枚举常量不是字符串,不能用下面的方法输出字符串”red”。

printf("%s",red);

如果想先检查color的值,若是red,就输出字符串"red”,可以这样:

color=red;

if(color==red)

printf("red");

五.指针类型

由于指针需要篇幅过大,我们后期单独介绍。(第八章也会单独介绍指针有关的知识)

(作者也有自己总结别人文章得出的拙见可以用来参考一下)

运算符

 C语言的运算符大全_Chshyz的博客-CSDN博客_c语言运算符

 C语言中的运算符大全(内附优先级表)_「已注销」的博客-CSDN博客_c语言运算符号

算术优先级:

(4条消息) C语言的算术优先级_Victor_psl的博客-CSDN博客_c语言算术运算符优先级

语言的i运算符包括单目运算符、双目运算符、三目运算符,优先级如下:

第1优先级:各种括号,如()、[]等、成员运算符 . ;

第2优先级:所有单目运算符,如++、--、!、~等;

第3优先级:乘法运算符*、除法运算符/、求余运算符%;

第4优先级:加法运算符+、减法运算符-;

第5优先级:移位运算符<<、>>;

第6优先级:大于运算符>、大于等于运算符>=、小于运算符<、小于等于运算符<=;

第7优先级:等于运算符==、不等于运算符!=;

第8优先级:按位与运算符&;

第9优先级:按位异或运算符^;

第10优先级:按位或运算符|;

第11优先级:逻辑与运算符&&;

第12优先级:逻辑或运算符||;

第13优先级:三目条件运算符  ?: ;

第14优先级:各种赋值运算符,如=、+=、-=、*=、/= 等;

第15优先级:逗号运算,  。

书上原图:

 

书上第53页++ --多留意

注意赋值时是从右向左的

赋值运算符的左侧就是一个可修改值的“左值”(left value 简写为lvalue)

b=a                    B=5

整型数据间的赋值按存储单元中的存储形式直接传送,实型数据之间以及整型与实型之间的赋值,是先转换(类型)后赋值。

数据的输入输出

输入2个整数 num1 num2,计算并输出它们的和、差、积、商与余数。

输出两个整数的余数可以用 printf("%d %% %d = %d\n", num1, num2, num1%num2);

输入输出示例:括号内是说明

输入

5 3       (num1=5,num2=3)

输出

5 + 3 = 8

5 - 3 = 2

5 * 3 = 15

5 / 3 = 1

5 % 3 = 2

#include <stdio.h>

int main(void)

{

     int num1,num2;

/*---------*/

scanf("%d%d",&num1,&num2);

printf("%d + %d = %d\n",num1,num2,num1+num2);

printf("%d - %d = %d\n",num1,num2,num1-num2);

printf("%d * %d = %d\n",num1,num2,num1*num2);

printf("%d / %d = %d\n",num1,num2,num1/num2);

printf("%d % % %d = %d\n",num1,num2,num1%num2);

return 0;

}

 

求一元二次方程的根 

输入一个正整数repeat (0<repeat<10),做repeat次下列运算:

输入参数a,b,c,求一元二次方程a*x*x+b*x+c=0的根,结果保留2位小数。

输出使用以下语句:

printf("参数都为零,方程无意义!\n");

printf("a和b为0,c不为0,方程不成立\n");

printf("x = %0.2f\n", -c/b);

printf("x1 = %0.2f\n", (-b+sqrt(d))/(2*a));

printf("x2 = %0.2f\n", (-b-sqrt(d))/(2*a));

printf("x1 = %0.2f+%0.2fi\n", -b/(2*a), sqrt(-d)/(2*a));

printf("x2 = %0.2f-%0.2fi\n", -b/(2*a), sqrt(-d)/(2*a));

输入输出示例:括号内为说明

输入:

5               (repeat=5)

0 0 0           (a=0,b=0,c=0)

0 0 1           (a=0,b=0,c=1)

0 2 4           (a=0,b=2,c=4)

2.1 8.9 3.5     (a=2.1,b=8.9,c=3.5)

1 2 3           (a=1,b=2,c=3)

输出:

参数都为零,方程无意义!

a和b为0,c不为0,方程不成立

x = -2.00

x1 = -0.44

x2 = -3.80

x1 = -1.00+1.41i

x2 = -1.00-1.41i

#include <stdio.h>

#include <math.h>

int main(void)

{

    int repeat, ri;

    double a, b, c, d;

    scanf("%d", &repeat);

    for(ri = 1; ri <= repeat; ri++){

        scanf("%lf%lf%lf", &a, &b, &c);

d=b*b-4*a*c;

if(a==0){

if(b==0){

if(c==0) printf("参数都为零,方程无意义!\n");

else printf("a和b为0,c不为0,方程不成立\n");

}

else printf("x = %0.2f\n", -c/b);

}

else{

if(d>=0){

printf("x1 = %0.2f\n", (-b+sqrt(d))/(2*a));

printf("x2 = %0.2f\n", (-b-sqrt(d))/(2*a));

}

else{

printf("x1 = %0.2f+%0.2fi\n", -b/(2*a), sqrt(-d)/(2*a));

printf("x2 = %0.2f-%0.2fi\n", -b/(2*a), sqrt(-d)/(2*a));

}

}

    }     

}

参考2

#include <stdio.h>

#include <math.h>

int main(void)

{

    int repeat, ri;

    double a, b, c, d;

    scanf("%d", &repeat);

    for(ri = 1; ri <= repeat; ri++){

        scanf("%lf%lf%lf", &a, &b, &c);

        d=b*b-4*a*c;

        if(a==0&&b==0&&c==0)     printf("参数都为零,方程无意义!\n");

        if(a==0&&b==0&&c!=0)    printf("a和b为0,c不为0,方程不成立\n");

        if(a==0&&b!=0)

                  printf("x = %0.2f\n", -c/b);

        if(a!=0&&d>=0){

               printf("x1 = %0.2f\n", (-b+sqrt(d))/(2*a));

               printf("x2 = %0.2f\n", (-b-sqrt(d))/(2*a));

         }

         if(a!=0&&d<0){

                 printf("x1 = %0.2f+%0.2fi\n", -b/(2*a), sqrt(-d)/(2*a));

                 printf("x2 = %0.2f-%0.2fi\n", -b/(2*a), sqrt(-d)/(2*a));

          }    

    }     

}

 

%lf:双精度实数

C语言%7.2d%-7d%7.2f%0.2f的含义和区别

(4条消息) C语言%7.2d、%-7d、%7.2f、%0.2f的含义和区别_云小逸的博客-CSDN博客_c语言中0.2f是什么意思

1.%d是输出整形格式,即int型数据

%-7d也是整形,但是输出的时候是左对齐,最少输出7位,不足7位的右端补空格。

%07d中的d代表:十进制有符号整数

7代表:输出的数字的最大宽度,小于这个宽度的数字前面就补空格,大于将按其实际长度输出

0代表:这里同上面的7一起作用,小于这个宽度的数字前面用0来补

2.    %7.2f有,表示输出最少7位浮点数,其中小数占两位

%7.2f中的2表示小数位数为2位,7表示最少输出7位,不足的左端补空格,比如123.4567输出就是空格123.46

%0.2 f也可以写成 %.2f 指保留小数点后两位

(1条消息) C语言 基本输入输出函数_pecuyu的博客-CSDN博客_c语言输入输出

(1条消息) C语言中scanf函数用法详解_沧州刺史的博客-CSDN博客_c语言scanf

字符串函数:

(1条消息) C语言中的getchar()与putchar()详解_平凡的世界_的博客-CSDN博客_ch=getchar()

Putchar输出 getchar输入

小结:基本数据类型 

关键字: 

基本数据类型由11个关键字组成:int、long、short、unsigned、char、 

float、double、signed、_Bool、_Complex和_Imaginary。 

有符号整型: 

151有符号整型可用于表示正整数和负整数。 

int ——系统给定的基本整数类型。C语言规定int类型不小于16位。 

short或short int ——最大的short类型整数小于或等于最大的int类型整 

数。C语言规定short类型至少占16位。 

long或long int ——该类型可表示的整数大于或等于最大的int类型整数。 

C语言规定long类型至少占32位。 

long long或long long int ——该类型可表示的整数大于或等于最大的long

类型整数。Long long类型至少占64位。 

一般而言,long类型占用的内存比short类型大,int类型的宽度要么和 

long类型相同,要么和short类型相同。例如,旧DOS系统的PC提供16位的 

short和int,以及32位的long;Windows 95系统提供16位的short以及32位的int

和long。 

无符号整型: 

无符号整型只能用于表示零和正整数,因此无符号整型可表示的正整数 

比有符号整型的大。在整型类型前加上关键字unsigned表明该类型是无符号 

整型:unsignedint、unsigned long、unsigned

short。单独的unsigned相当于 

unsignedint。 

字符类型: 

可打印出来的符号(如A、&和+)都是字符。根据定义,char类型表示 

一个字符要占用1字节内存。出于历史原因,1字节通常是8位,但是如果要 

表示基本字符集,也可以是16位或更大。 

char ——字符类型的关键字。有些编译器使用有符号的char,而有些则 

使用无符号的char。在需要时,可在char前面加上关键字signed或unsigned来 

指明具体使用哪一种类型。 

152布尔类型: 

布尔值表示true和false。C语言用1表示true,0表示false。 

_Bool ——布尔类型的关键字。布尔类型是无符号 int类型,所占用的空 

间只要能储存0或1即可。 

实浮点类型: 

实浮点类型可表示正浮点数和负浮点数。 

float ——系统的基本浮点类型,可精确表示至少6位有效数字。 

double ——储存浮点数的范围(可能)更大,能表示比 float 类型更多 

的有效数字(至少 10位,通常会更多)和更大的指数。 

long

long

——储存浮点数的范围(可能)比double更大,能表示比 

double更多的有效数字和更大的指数。 

复数和虚数浮点数: 

虚数类型是可选的类型。复数的实部和虚部类型都基于实浮点类型来构 

成: 

float _Complex

double _Complex

long double _Complex

float _Imaginary

double _Imaginary

long long _Imaginary

153小结:如何声明简单变量 

1.选择需要的类型。 

2.使用有效的字符给变量起一个变量名。 

3.按以下格式进行声明: 

类型说明符 变量名;

类型说明符由一个或多个关键字组成。下面是一些示例: 

int erest;

unsigned short cash;

4.可以同时声明相同类型的多个变量,用逗号分隔各变量名,如下所 

示: 

char ch, init, ans;

5.在声明的同时还可以初始化变量: 

float mass = 6.0E24;

 

位、字节和字 

位、字节和字是描述计算机数据单元或存储单元的术语。这里主要指存 

115储单元。 

最小的存储单元是位(

bit),可以储存0或1(或者说,位用于设 

置“开”或“关”)。虽然1位储存的信息有限,但是计算机中位的数量十分庞 

大。位是计算机内存的基本构建块。 

字节(

byte)是常用的计算机存储单位。对于几乎所有的机器,1字节 

均为8位。这是字节的标准定义,至少在衡量存储单位时是这样(但是,C

语言对此有不同的定义,请参阅本章3.4.3节)。既然1位可以表示0或1,那 

么8位字节就有256(

2的8次方)种可能的0、1的组合。通过二进制编码(仅 

用0和1便可表示数字),便可表示0~255的整数或一组字符(第15章将详细 

讨论二进制编码,如果感兴趣可以现在浏览一下该章的内容)。 

字(word)是设计计算机时给定的自然存储单位。对于8位的微型计算 

机(如,最初的苹果机), 1个字长只有8位。从那以后,个人计算机字长 

增至16位、32位,直到目前的64位。计算机的字长越大,其数据转移越快, 

允许的内存访问也更多。

第三章习题答案:

(1条消息) C程序设计谭浩强第五版课后答案 第三章习题答案_月已满西楼的博客-CSDN博客_c语言程序设计第五版第三单元题目

 

购房从银行贷了一笔款d,准备每月还款额为p,月利率为r,计算多少月能还清。设d为300 000元,p为6000元,r为1%。对求得的月份取小数点后一位,对第2位按四舍五人处理。

提示:计算还清月数m的公式如下:

可以将公式改写为:

C的库函数中有求对数的函数log10,是求以10为底的对数,log( p )表示log p。

题目解析:

该题的关键主要是利用数学函数库中的log函数进行求解,然后月份要求按照小数点后一位进行四舍五入进行处理,只需要在输出时进行格式控制即可。

代码示例:

#include<stdio.h>

#include <math.h>

int main()

{

float d = 300000, p = 6000, r = 0.01, m;

m = log10(p / (p - d*r)) / log10(1 + r);

printf("m = %3.1f\n", m);

return 0;

}

第三章目录回顾:

3.1顺序程序设计举例37

3.2数据的表现形式及其运算39

3.2.1常量和变量39

3.2.2数据类型42

3.2.3整型数据44

3.2.4字符型数据47

3.2.5浮点型数据49

3.2.6怎样确定常量的类型50

3.3运算符和表达式51

3.3.1C运算符52

3.3.2基本的算术运算符52

3.3.3自增(++)、自减(--)运算符53

3.3.4算术表达式和运算符的优先级与结合性53

3.3.5不同类型数据间的混合运算54

3.3.6强制类型转换运算符55

3.4C语句56

3.4.1C语句的作用和分类56

3.4.2最基本的语句——赋值语句58

3.5数据的输入输出64

3.5.1输入输出举例64

3.5.2有关数据输入输出的概念65

3.5.3用printf函数输出数据67

3.5.4用scanf函数输入数据74

3.5.5字符输入输出函数76

 

 

第4章选择结构程序设计

(1条消息) if语句—超详解_蒲公英的吴的博客-CSDN博客_if语句

//多分支

if (表达式1)

       语句1;

else if (表达式2)3.

       语句2;

else

       语句3;

 在if语句中又包含一个或多个if语句称为if语句的嵌套(nest)

else总是与它上面最近的未配对的if配对

如何避免不是与自己想要的if配对上了呢?

此时{}限定了内嵌if语句的范围因此else与第一个if配对

If()

If()语句1

else   语句2

 由于有外层的else相隔,内嵌的else不会被误认为和外层的if配对,而只能与内嵌的if配对                                                                                                                                                                                                                                                                                                                    

if(x<0)

y=-1;

else

if(x==0)y=0;

else y=1;

printf();

return 0;

                                                                           

*算术符号优先次序*

由高到低为:!(非)>算术运算符>关系运算符>&&||>赋值运算符

 

 

Switch语句的用法:

显示五级记分制成绩所对应的百分制成绩区间(使用switch

输入一个正整数 repeat (0<repeat<10),做 repeat 次下列运算:

输入五级制成绩(A-E),输出相应的百分制成绩(0-100)区间,要求使用switch语句。

五级制成绩对应的百分制成绩区间为:A(90-100)、B(80-89)、C(70-79)、D(60-69)和E(0-59),如果输入不正确的成绩,显示"Invalid input"。

输出使用以下语句:

printf("90-100\n");

printf("80-89\n");

printf("70-79\n");

printf("60-69\n");

printf("0-59\n");

printf("Invalid input\n");

输入输出示例:括号内是说明

输入

6ABCDEj    (repeat=6,输入的五级成绩分别为A、B、C、D、E和无效的字符j)

输出

90-100

80-89

70-79

60-69

0-59

Invalid input   (输入数据不合法)

#include <stdio.h>

int main(void)

{

    char ch;

    int repeat, ri;

    scanf("%d", &repeat);

    for(ri = 1; ri <= repeat; ri++){

        ch = getchar();    

switch(ch){

case'A':

printf("90-100\n");

break;

case'B':

printf("80-89\n");

break;

case'C':

printf("70-79\n");

break;

case'D':

printf("60-69\n");

break;

case'E':

printf("0-59\n");

break;

default:

printf("Invalid input\n");

break;

}

    }

return 0;

}

显示水果的价格(使用switch

查询水果的单价。有4种水果,苹果(apples)、梨(pears)、桔子(oranges)和葡萄(grapes),单价分别是3.00元/公斤,2.50元/公斤,4.10元/公斤和10.20元/公斤。

在屏幕上显示以下菜单(编号和选项),用户可以连续查询水果的单价,当查询次数超过5次时,自动退出查询;不到5次时,用户可以选择退出。

当用户输入编号1~4,显示相应水果的单价(保留1位小数);输入0,退出查询;输入其他编号,显示价格为0。

输入输出示例:括号内是说明

输入

3   (oranges的编号)

0   (退出查询)

输出

[1] apples

[2] pears

[3] oranges

[4] grapes

[0] Exit

price = 4.1

[1] apples

[2] pears

[3] oranges

[4] grapes

[0] Exit

#include <stdio.h>

int main(void)

{

    int choice, i;

    double price;

    for(i = 1; i <= 5; i++){

        printf("[1] apples\n");

        printf("[2] pears\n");

        printf("[3] oranges\n");

        printf("[4] grapes\n");

        printf("[0] Exit\n");

        scanf("%d", &choice);

        if(choice == 0)

            break;

        else{

switch (choice){

case 1: price= 3.00; break;

case 2: price=2.50; break;

case 3: price=4.10; break;

case 4: price=10.20; break;

default:              price=0; break;

}

          printf("price = %0.1f\n", price);

      }

    }

}

 

 选择结构典型例题:

(1条消息) C语言程序设计第五版 谭浩强 第四章 课后习题 答案_月已满西楼的博客-CSDN博客_c语言第四章课后题答案

1. 什么是算术运算?什么是关系运算?什么是逻辑运算?

【答案解析】

算术运算:

算术运算即“四则运算”,是加法、减法、乘法、除法、乘方、开方等几种运算的统称。

其中加减为一级运算,乘除为二级运算,乘方、开方为三级运算。在一道算式中,如果有多级运算存在,则应先进行高级运算,再进行低一级的运算。

C语言中的算熟运算符包括:+、-、*、/、++、--、% 等种类。

如果只存在同级运算;则从左至右的顺序进行;如果算式中有括号,则应先算括号里边,再按上述规则进行计算。

示例:$ (1 + 1)^{2} * 4+5 * 3$

解析:

先进行括号内运算1+1,然后进行乘方运算得到结果4.

接下来与4相乘,得到结果16

因为乘法优先级大于加法,因此先进行5*3,得到结果15

最终相加得到结果31

结果:31

关系运算:

关系的基本运算有两类:一类是传统的集合运算(并、差、交等),另一类是专门的关系运算(选择、投影、连接、除法、外连接等),而在C语言中,关系运算通常被认为是比较运算,将两个数值进行比较,判断比较结果是否符合给定的条件。

常见的关系运算符包括:<、<=、>、>=、==、!= 等种类。

其中,前4种关系运算符(<、<=、>、>= )的优先级别相同,后2种(==、!=)也相同。而前4种高于后2种。

例如, > 优先于 == 。而 > 与 < 优先级相同。

并且,关系运算符的优先级低于算术运算符,关系运算符的优先级高于赋值运算符(=)。

逻辑运算:

在逻辑代数中,有与、或、非三种基本逻辑运算。表示逻辑运算的方法有多种,如语句描述、逻辑代数式、真值表、卡诺图等。而在C语言中,逻辑运算通常用于使用逻辑运算符将关系表达式或其它逻辑量连接起来组成逻辑表达式用来测试真假值。

常见的逻辑运算符包括:&&、||、! 等种类

&&: 与是双目运算符,要求有两个运算对象,表示两个运算对象都成立,则结果为真,否则结果为假。

例如:(a<b) && (x>y),表示(a<b)和(x>y)同时成立则为真。

||:是双目运算符,要求有两个运算对象,表示两个运算对象只要任意一个成立,则结果为真,否则结果为假。

例如:(a<b) && (x>y),表示(a<b)和(x>y)两个对象中任意一个成立则结果为真。

**!**是单目运算符,只要求有一个运算对象,表示取运算对象反义,运算对象为真则结果为假,运算对象结果为假则结果为真。

例如:!(a>b),表示(a>b)成立时结果为假,不成立时结果为真。

若在一个逻辑表达式中包含多个逻辑运算符,则优先次序为: ! > && > ||。当然若一个逻辑表达式中包含括号括起来的子逻辑,则优先括号内的子逻辑判断。

示例:

(1>2)||(2>3)&&(4>3) 结果为0

!(1>2)||(2>3)&&(4>3)结果为1

注:&&优先级大于||,((2>3)&&(4>3))无法同时成立,则结果为假,然后与(1>2)结果进行逻辑或运算,两者都为假因此第一次结果为假。 而第二次!优先级最高,先对(1>2)的结果取逻辑非,得到结果为真,因此结果为真。

2. C语言中如何表示“真”和“假”?系统如何判断一个量的“真”和“假”?

答案:

在C语言中逻辑常量只有两个,即0和1,用来表示两个对立的逻辑状态,其中0表示假,1表示真。

逻辑变量与普通代数一样,也可以用字母、符号、数字及其组合成为的逻辑表达式表示。

对于系统来说,判断一个逻辑量的值时,系统会以0作为假,以非0作为真。例如3&&5的值为真,系统给出3&&5的值为1。

3. 写出下面各逻辑表达式的值。设a=3,b=4,c=5。

(1)a + b > c && b == c

(2)a || b + c && b - c

(3)!(a > b) && !c || 1

(4)!(x = a) && (y = b) && 0

(5)!(a + b) + c - 1 && b + c / 2

解题思路:

关系运算符的优先级高于赋值运算符,但是低于算术运算符;

&&表示两边条件同为真则成立,||表示两边条件任意一个为真则成立,!取条件反义。

逻辑运算符优先级: ! > && > ||

有括号优先括号。

3个整数a, b, c,由键盘输入,输出其中最大的数。

解题思路: 每个数字两两与剩余两个数字进行比较,若比剩下的两个数大则最大,例如:a>b && a>c则a是最大的

答案:

#include <stdio.h>

int main()

{

    int a, b, c;

    scanf("%d %d %d", &a, &b, &c);

    if (a == b && a == c) {

        printf("Three numbers are equal\n");

    }else if (a == b && a > c) {

        printf("a and b are the largest number\n", a);

    }else if (a == c && a > b) {

        printf("a and c are the largest number\n", a); //相等的情况不能忘!!!

    }else if (b == c && b > a) {

        printf("c and b are the largest number\n", a);

    }else if (a > b && a > c) {

        printf("a=%d is the largest number\n", a);

    }else if (b > a && b > c) {

        printf("b=%d is the largest number\n", b);

    }else {

        printf("c=%d is the largest number\n", c);

    }   

    return 0;

}

 给出一百分制成绩,要求输出成绩等级’A’‘B’‘C’‘D’‘E’90分以上为’A’,8089分为’B’,7079分为’C’ ,60~69分为’D’ ,60分以下为’E’

解题思路: 根据不同的阶段成绩区间作为成绩的判断条件,属于哪个区间则输出对应等级即可

答案:

#include <stdio.h>

int main()

{

int score;

printf("enter score:");

scanf_s("%d", &score);

if (score >= 90) {

printf("A\n");

}else if (score >= 80 && score < 90) {

printf("B\n");

}else if (score >= 70 && score < 80) {

printf("C\n");

}else if (score >= 60 && score < 70) {

printf("D\n");

}else {

printf("E\n");

}

system("pause");

return 0;

}

给一个不多于5位的正整数,要求:①求出它是几位数;②分别输出每一位数字;③按逆序输出各位数字,例如原数为321,应输出123

①求出它是几位数;

解题思路: 大于10000就是5位,否则大于1000就是四位,否则大于100是三位…

答案:

#include <stdio.h>

int main()

{

int num;

printf("enter num:");

scanf_s("%d", &num);

if (num > 99999 || num < 0) {

printf("请输入0~99999之间的正数\n");

return -1;

}

if (num >= 10000) {

printf("5\n");

}else if (num >= 1000) {

printf("4\n");

}else if (num >= 100) {

printf("3\n");

}else if (num >= 10) {

printf("2\n");

}else {

printf("1\n");

}

system("pause");

return 0;

}

②分别输出每一位数字;

解题思路: 99999除以10000则输出9;9999除以1000则输出9,…

答案:

#include <stdio.h>

int main()

{

int num;

printf("enter num:");

scanf_s("%d", &num);

if (num > 99999 || num < 0) {

printf("请输入0~99999之间的数字\n");

return -1;

}

if (num / 10000 > 0) {//取出万位数字

printf("%d ", num / 10000);

}

if (num%10000 >= 1000) {//取余10000则可以取出低四位的数据,除以1000则得到千位的数字

printf("%d ", (num % 10000) / 1000);

}

if (num%1000 >= 100) {//取余1000则可以取出低三位的数据,除以100则得到百位的数字

printf("%d ", (num % 1000) / 100);

}

if (num%100 >= 10) {//取余100则可以取出低两位的数据,除以10则得到十位的数字

printf("%d ", (num % 100) / 10);

}

if (num%10 >= 0) {//取余10则取出个位数字

printf("%d ", num % 10);

}

printf("\n");

system("pause");

return 0;

}

③按逆序输出各位数字,例如原数为321,应输出123。

解题思路: 思路与第二题相同,只不过将整个过程逆序即可

答案:

#include <stdio.h>

int main()

{

int num;

printf("enter num:");

scanf_s("%d", &num);

if (num > 99999 || num < 0) {

printf("请输入0~99999之间的数字\n");

return -1;

}

if (num % 10 >= 0) {

printf("%d ", num % 10);

}

if (num % 100 >= 10) {

printf("%d ", (num % 100) / 10);

}

if (num % 1000 >= 100) {

printf("%d ", (num % 1000) / 100);

}

if (num % 10000 >= 1000) {

printf("%d ", (num % 10000) / 1000);

}

if (num / 10000 > 0) {

printf("%d ", num / 10000);

}

printf("\n");

system("pause");

return 0;

}

课后第十题:

企业发放的奖金根据利润提成。利润I低于或等于100000元的,奖金可提成10%;利润高于100000元,低于200000元(100000<I≤200000)时,低于100000元的部分按10%提成,高于100000元的部分,可提成7. 5%;200000<I≤400000时,低于200000元的部分仍按上述办法提成(下同)。高于200000元的部分按5%提成;400000<<I≤600000元时,高于400000元的部分按3%提成;600000<1≤1000000时,高于600000元的部分按1.5%提成;I>1000000时,超过1000000元的部分按1%提成。从键盘输入当月利润I,求应发奖金总数。要求:(1) 使用if语句编写程序。(2) 使用switch语句编写程序。

(1) 使用if语句编写程序。

解题思路: 先将每一档的最大奖金算出来,在某一个区间时,则那小于这一档的奖金加上多出部分的奖金即可,例如:

先列出100000档的奖金是10000,则180000就是10000 + (180000-100000) * 0.075;列出200000档的奖金是第一档加上多出100000部分的7.5%得到17500,则300000就是17500 + (300000-200000)*0.05;

答案:

#include <stdio.h>

int main()

{

double I, salary = 0;

printf("enter performance:");

scanf_s("%lf", &I);

if (I < 0) {

printf("请输入一个正数\n");

system("pause");

return -1;

}

double salary1 = 100000 * 0.1;//10万的奖金

double salary2 = (200000 - 100000) * 0.075 + salary1;//20万的奖金

double salary3 = (400000 - 200000) * 0.05 + salary2;//40万的奖金

double salary4 = (600000 - 400000) * 0.03 + salary3;//60万的奖金

double salary5 = (1000000 - 600000) * 0.015 + salary4;//100万的奖金

if (I <= 100000) {

salary = I * 0.1;//小于100000按10%提成

}else if (I > 100000 && I <= 200000) {

salary = salary1 + (I - 100000) * 0.075;//多出10万的按比例计算,加上10w的奖金

}else if (I > 200000 && I <= 400000) {

salary = salary2 + (I - 200000) * 0.05;//多出20万的按比例计算,加上20w的奖金

}else if (I > 400000 && I <= 600000) {

salary = salary3 + (I - 400000) * 0.03;//多出40万的按比例计算,加上40w的奖金

}else if (I > 600000 && I <= 1000000) {

salary = salary4 + (I - 600000) * 0.015;//多出60万的按比例计算,加上60w的奖金

}else if (I > 1000000){

salary = salary5 + (I - 1000000) * 0.01;//多出100万的按比例计算,加上100w的奖金

}

printf("salary:%f\n", salary);

system("pause");

return 0;

}

(2) 使用switch语句编写程序。

解题思路: 与第一题思路没有太大差别,区别在于switch语句的case子句中需要是一个常量整数,并且switch中若子句中没有break将循序向下执行,直到遇到break才会跳出switch语句,如果这时候将利润除以10w,则得到09的数字,其中0表示小于10w,1表示介于1020w,2、3表示介于2040w,4、5表示介于4060w,6、7、8、9表示介于60~100w,否则就是大于100w

答案:

#include <stdio.h>

int main()

{

double I, salary = 0;

printf("enter performance:");

scanf_s("%lf", &I);

if (I < 0) {

printf("请输入一个正数\n");

system("pause");

return -1;

}

double salary1 = 100000 * 0.1;//大于100000时0~100000的奖金

double salary2 = (200000 - 100000) * 0.075 + salary1;//大于200000时0~20万的奖金

double salary3 = (400000 - 200000) * 0.05 + salary2;//大于400000时0~40万的奖金

double salary4 = (600000 - 400000) * 0.03 + salary3;//大于600000时0~60万的奖金

double salary5 = (1000000 - 600000) * 0.015 + salary4;//大于1000000时0~100万的奖金

int grade = I / 100000;

switch(grade) {

case 0:

salary = I * 0.1; break;

case 1:

salary = salary1 + (I - 100000) * 0.075; break;

case 2://会顺序执行到下一个break处

case 3:

salary = salary2 + (I - 200000) * 0.05; break;

case 4:

case 5:

salary = salary3 + (I - 400000) * 0.03; break;

case 6:

case 7:

case 8:

case 9:

salary = salary4 + (I - 600000) * 0.015; break;

default:

salary = salary5 + (I - 1000000) * 0.01; break;

}

printf("salary:%f\n", salary);

system("pause");

return 0;

}

第四章目录回顾

4.1选择结构和条件判断83

4.2用if语句实现选择结构85

4.2.1用if语句处理选择结构举例85

4.2.2if语句的一般形式87

4.3关系运算符和关系表达式89

4.3.1关系运算符及其优先次序90

4.3.2关系表达式90

4.4逻辑运算符和逻辑表达式90

4.4.1逻辑运算符及其优先次序91

4.4.2逻辑表达式92

4.5条件运算符和条件表达式94

4.6选择结构的嵌套96

4.7switch语句实现多分支选择结构99

4.8选择结构程序综合举例102

 

 

 

第5章循环结构程序设计

结构化程序设计的3种基本结构:

循环结构 顺序结构 选择结构

5.2while语句实现循环

 while循环和for循环的区别_FutureForXHF的博客-CSDN博客_while循环和for循环的区别

do-whilewhile的区别

Do可以简单的使用英语翻译进行理解,就是做

Do{               做

             Printf(“2 “);     输出2 (这个行为)

}while(i<=5)        当i<=5时

而while则是

While(i<=5)      当i<=5时

{

     Printf(“2 “);      输出2

}

从这两个例子就可以看到,do- while循环比while多了一个“做”的命令,而这就可以理解为:不管怎么样,你先给我运行一次再说。

11/41/71/101/131/16+……

输入一个正整数repeat (0<repeat<10),做repeat次下列运算:

读入1个正实数 eps,计算并输出下式的值,精确到最后一项的绝对值小于 eps(保留6位小数)。请使用 while 语句实现循环。

计算:1-1/4+1/7-1/10+1/13-1/16+……

输入输出示例:括号内是说明

输入

2        (repeat=2)

2E-4     (eps=2E-4)

0.02      (eps=0.02)

输出

sum = 0.835549

sum = 0.826310

#include <stdio.h>

#include <math.h>

int main(void)

{

    int denominator, flag;

    int repeat, ri;

    double eps, item, sum;

    scanf("%d", &repeat);

    for(ri = 1; ri <= repeat; ri++){

        scanf("%le", &eps);

sum=0;

denominator=1;

flag=1;

item=1.0;

while(fabs(item)>=eps){

item=flag*1.0/denominator;

sum=sum+item;  

flag=-flag;

denominator=denominator+3;

}

       printf("sum = %.6f\n", sum);

   }

return 0;

}

40013    求奇数和

输入一个正整数repeat (0<repeat<10),做repeat次下列运算:

读入一批正整数(以零或负数为结束标志),求其中的奇数和。请使用while语句实现循环。

输入输出示例:括号内是说明

输入

2    (repeat=2)

1 3 90 7 0

8 7 4 3 70 5 6 101 -1

输出

The sum of the odd numbers is 11.

The sum of the odd numbers is 116.

#include <stdio.h>

int main(void)

{

    int x, sum;

    int repeat, ri;

    scanf("%d", &repeat);

for(ri = 1; ri <= repeat; ri++) {

        scanf("%d", &x);

sum=0;

while(x>0) {

if(x%2!=0)

sum+=x;

scanf("%d", &x);

}

       printf("The sum of the odd numbers is %d.\n", sum);

   }

}

40014    求整数的位数 

输入一个正整数 repeat (0<repeat<10),做 repeat 次下列运算:

输入一个整数 in,求它的位数。例如123的位数是3。请使用do-while语句实现循环。

输入输出示例:括号内是说明

输入

4        (repeat=4)

12345    (in=12345)

-100     (in=-100)

-1       (in=-1)

1290     (in=1290)

输出

count = 5 (12345的位数是5)

count = 3 (-100的位数是3)

count = 1 (-1的位数是1)

count = 4 (99的位数是2)

#include <stdio.h>

int main(void)

{

    int count, in;

    int repeat, ri;

    scanf("%d", &repeat);

    for(ri = 1; ri <= repeat; ri++){

      scanf("%d", &in);

count=0;

if(in<0) in=-in;

do{

count++;

in=in/10;

} while (in!=0);

      printf("count = %d\n", count);

   }

return 0;

}

 求整数的位数以及各位数字之和

程序填空,不要改变与输入输出有关的语句。

输入一个正整数 repeat (0<repeat<10),做 repeat 次下列运算:

输入一个整数 in,求它的位数以及各位数字之和。例如 123 的各位数字之和是 6,位数是 3。

输入输出示例:括号内是说明

输入

4        (repeat=4)

0        (in=0)

23456    (in=23456)

-100     (in=-100)

-1       (in=-1)

输出

count = 1, sum = 0 (0的位数是1, 各位数字之和是0)

count = 5, sum = 20 (23456的位数是5, 各位数字之和是20)

count = 3, sum = 1 (-100的位数是3, 各位数字之和是1)

count = 1, sum = 1 (-1的位数是1, 各位数字之和是1)

#include <stdio.h>

int main(void)

{

    int count, in, sum;

    int repeat, ri;

    scanf("%d", &repeat);

    for(ri = 1; ri <= repeat; ri++){

      scanf("%d", &in);

count=0; sum=0;

if(in<0) in=-in;

do{

count++;

sum+=in%10;

in=in/10;

} while (in!=0);

      printf("count = %d, sum = %d\n", count, sum);

   }

return 0;

}

  

5.4for语句实现循环

for(表达式1;表达式2;表达式3)

for(循环变量赋初值;循环条件;循环变量增值)

 

tipsfor语句会比while语句功能强,除了可以给出循环条件外,还可以赋初值,使循环变量自动增值。

 

2/13/25/38/5...

输入一个正整数 repeat (0<repeat<10),做 repeat 次下列运算:

输入一个正整数 n,输出 2/1+3/2+5/3+8/5+……前n项之和,保留2位小数。(该序列从第2项起,每一项的分子是前一项分子与分母的和,分母是前一项的分子)

输入输出示例:括号内是说明

输入

3        (repeat=3)

1        (n=1)

5        (n=5)

20       (n=20)

输出

sum = 2.00    (第1项是2.00)

sum = 8.39    (前5项的和是8.39)

sum = 32.66   (前20项的和是32.66)

#include <stdio.h>

int main(void)

{

    int i, n;

    int repeat, ri;

    double denominator, numerator, sum, temp;

    scanf("%d", &repeat);

    for(ri = 1; ri <= repeat; ri++){

        scanf("%d", &n);

sum=0;

denominator=1;

numerator=2;

for(i=1;i<=n;i++){

sum += numerator/denominator;

temp = denominator; /* 为求下一项分子,保留当前项分母 */

denominator=numerator;

numerator=numerator+temp;

}

      printf("sum = %.2f\n",sum);

   }

}

高空坠球 (循环语句)

程序填空,不要改变与输入输出有关的语句。

输入一个正整数 repeat (0<repeat<10),做 repeat 次下列运算:

皮球从 height(米)高度自由落下,触地后反弹到原高度的一半,再落下,再反弹,……,如此反复。问皮球在第 n 次落地时,在空中一共经过多少距离?第 n 次反弹的高度是多少?(输出保留1位小数)

输入输出示例:括号内是说明

输入

3 (repeat=3)

10  2 (height=10m, n=2)

4   1 (height=2m, n=1)

100 8 (height=100m, n=8)

输出

distance=20.0, height=2.5 (第2次落地时,空中经过距离为20米, 第2次反弹高度为2.5米)

distance=4.0, height=2.0 (第1次落地时,空中经过距离为4米,第1次反弹高度为2米)

distance=298.4, height=0.4 (第8次落地时,空中经过距离为298.4米,第8次反弹高度为0.4米)

#include <stdio.h>

int main(void)

{

    int i, n;

    int repeat, ri;

    double distance, height, bh;

    scanf("%d", &repeat);

    for(ri = 1; ri <= repeat; ri++){

        scanf("%lf%d", &height, &n);

/*---------*/

distance = height;

bh = height/2.0;

if ( n>1 )

for (i = 2; i<=n; i++) {

distance  += bh*2;

bh = bh/2;

}

   printf("distance = %.1f, height = %.1f\n", distance, bh);

   }

}

5.5循环的嵌套

使用嵌套循环求e = 11/1!1/2!....1/n!

程序填空,不要改变与输入输出有关的语句。

输入一个正整数 repeat (0<repeat<10),做 repeat 次下列运算:

输入1个正整数n,计算下式的前n+1项之和(保留4位小数)。要求使用嵌套循环。

   e = 1+1/1!+1/2!+....+1/n!  

输入输出示例:括号内为说明

输入:

2       (repeat=2)

2       (n=2)

10      (n=10)

输出:

e = 2.5000

e = 2.7183

#include "stdio.h"

int main(void)

{

    int i, j, n;

    int repeat, ri;

    double e, product;

    scanf("%d", &repeat);

    for(ri = 1; ri <= repeat; ri++){

        scanf("%d", &n);

e = 1.0;

for (i = 1; i<= n; i++) {

product = 1;

for (j = 1; j<=i ; j++) {

product *= j;

}

e += 1.0/product;

}

/*---------*/

     printf("e = %0.4f\n", e);

  }

}

40023    换硬币 (多重循环语句)

输入一个正整数 repeat (0<repeat<10),做 repeat 次下列运算:

将一笔零钱(大于8分,小于1元, 精确到分)换成5分、2分和1分的硬币。

输入金额,问有几种换法?针对每一种换法,每种硬币至少有一枚,请输出各种面额硬币的数量和硬币的总数量。

要求:硬币面值按5分、2分、1分顺序,各类硬币数量依次从大到小的顺序,输出各种换法。

输出使用语句:printf("fen5:%d,fen2:%d,fen1:%d,total:%d\n",fen5, fen2, fen1, fen5+fen2+fen1);

输入输出示例:括号内为说明

输入:

2     (repeat=2)

10    (money=10分)

13    (money=13分)

输出:

fen5:1,fen2:2,fen1:1,total:4

fen5:1,fen2:1,fen1:3,total:5

count = 2     (10分有2种换法)

fen5:2,fen2:1,fen1:1,total:4

fen5:1,fen2:3,fen1:2,total:6

fen5:1,fen2:2,fen1:4,total:7

fen5:1,fen2:1,fen1:6,total:8

count = 4     (13分有4种换法)

#include "stdio.h"

int main(void)

{

    int count, fen1, fen2, fen5, money;

    int repeat, ri;

    scanf("%d", &repeat);

    for(ri = 1; ri <= repeat; ri++){

        scanf("%d", &money);

count=0;

for(fen5=money/5;fen5>=1;fen5--)

for(fen2=money/2;fen2>=1;fen2--)

for(fen1=money/1;fen1>=1;fen1--)

if(fen5*5+fen2*2+fen1*1==money){

printf("fen5:%d,fen2:%d,fen1:%d,total:%d\n",fen5, fen2, fen1, fen5+fen2+fen1);

count++;}

     printf("count = %d\n", count);

    }

return 0;

}

5.6几种循环的比较                                     

5.7改变循环执行的状态

  1. 用break语句提前终止循环

验证歌德巴赫猜想(选作) 

程序填空,不要改变与输入输出有关的语句。

验证哥德巴赫猜想:任何一个大于6的偶数均可表示为两个素数之和。例如6=3+3,8=3+5,…,18=7+11。

输入两个正整数 m 和 n(6<=m, n<=100),将 m 到 n 之间的偶数表示成两个素数之和,打印时一行打印5组。

输出使用语句:printf("%d=%d+%d ", number, i, number - i);

输入输出示例:括号内为说明

输入:

89 100 (m=90, n=100)

输出:

90=7+83 92=3+89 94=5+89 96=7+89 98=19+79

100=3+97

#include "stdio.h"

#include "math.h"

int main(void)

{

    int count, i, j, k, m, n, number;

    scanf("%d%d", &m, &n);

    if(m % 2 != 0) m = m + 1;

    if(m >= 6){

/*---------*/

    }

}

#include <stdio.h>

int prime(int m)

{

int i, ifPrime=0;

if(m==1) return 0;

for(i=2;i<=m/2;i++) {

if(m%i==0)

break;

}

if(i>m/2)

ifPrime=1;

return ifPrime;

}

int main(void)

{

    int count, i, m, n, number;

    scanf("%d%d", &m, &n);

    if(m % 2 != 0) m = m + 1;

if(m >= 6) {

count=0;

for(number=m;number<=n;number=number+2) {

for(i=1;i<=number/2;i++) {

if(  prime(i) && prime(number-i) ) {

printf("%d=%d+%d ", number, i, number-i);

count++;

if(count%5==0)

printf("\n");

break;

}

}

}

    }

}

注:此题标准答案是输出每个数的第一对满足条件的素数之和,但是:

90=7+83 90=11+79 90=17+73 90=19+71 90=23+67 ...90=83+7

也都是满足条件的, 而机器只对输出第一种为正确解

  1. 用continue语句提前结束本次循环

一定要记住当执行continue时只是结束本次循环而不是终止整个循环的执行。而break语句则是结束整个循环过程,不再判断执行循环的条件是否成立。

continue语句应该这样理解:当执行continue语句时,流程跳转到表示循环体结束的右花括号的前面(注意不是右花括号的后面)  也可以理解为continue后的不执行,然后重新进入下一次新的循环当中

# include <stdio.h>

int main(void)

{

int val; //variable的缩写, “变量”的意思

printf("请输入您想去的楼层:");

while (1)

{

scanf("%d", &val);

switch (val)

{

case 1:

printf("1层开!\n");

break; //跳出switch

case 2:

printf("2层开!\n");

break; //跳出switch

case 3:

printf("3层开!\n");

break; //跳出switch

default:

printf("该层不存在, 请重新输入:");

continue; //结束本次while循环

}

break; //跳出while

}

return 0;

}

区别:

continuebreak的区别

continue 语句和 break 语句的区别是,continue 语句只结束本次循环,而不是终止整个循环。break 语句则是结束整个循环过程,不再判断执行循环的条件是否成立。而且,continue 只能在循环语句中使用,即只能在 for、while 和 do…while 中使用,除此之外 continue 不能在任何语句中使用。

所以,再次强调:continue 不能在 switch 中使用,除非 switch 在循环体中。此时 continue 表示的也是结束循环体的本次循环,跟 switch 也没有关系。

第五章典型题(循环):

斐波那契数列

【C语言】斐波那契数列_EurekaO-O的博客-CSDN博客_斐波那契数列c语言

斐波那契数列_详解(C语言)_小-黯的博客-CSDN博客_斐波那契数列c语言

斐波那契数列的四种实现方式(C语言)_cherrydreamsover的博客-CSDN博客_斐波那契数列c语言

第五章习题答案:

C语言程序设计第五版谭浩强课后答案 第五章习题答案_月已满西楼的博客-CSDN博客_统计出当fabs(t) >= pow(10, -6)和fabs(t) >= pow(10, -8)时

3.输人两个正整数m和n,求其最大公约数和最小公倍数

答案解析:

该题题目直接使用“辗转相除法”来求解最大公约数,以除数和余数反复做除法运算,当余数为 0 时,就取得当前算式除数为最大公约数。

最大公约数和最小公倍数之间的性质:两个自然数的乘积等于这两个自然数的最大公约数和最小公倍数的乘积。所以,当我们求出最大公约数,就可以很轻松的求出最小公倍数。

代码示例:

#include <stdio.h>

int main()

{

int  p, r, n, m, temp;

printf("请输入两个正整数n,m:");

scanf("%d%d,", &n, &m);

//调整n保存较大的值

if (n < m)

{

temp = n;

n = m;

m = temp;

}

p = n * m;

while (m != 0)

{

r = n % m;

n = m;

m = r;

}

printf("它们的最大公约数为:%d\n", n);

printf("它们的最小公倍数为:%d\n", p / n);

return 0;

}

6.求∑ n = 1 20 n ! \sum\limits_{n=1}^{20}n!

n=1

20

 n! (即求1!+2!+3!+4!+…+20!)。

答案解析:

该题需要从1循环到20,依次求出每一个数字阶乘的结果。所以在代码当中需要有两个循环,大循环从1到20,保证1到20个数字都被循环到,小循环里计算N阶乘,累加求和。注意:对于20的阶乘已经超出了int类型能过表示的数字范围,所以在代码当中使用double类型

代码示例:

#include<stdio.h>

int main()

{

double total_sum = 0;

for(int i = 1; i <= 20; i++)

{

double single_sum = 1;

for (int j = i; j > 0; j--)

{

single_sum *= j;

}

total_sum += single_sum;

}

printf("1~20每个数字阶乘总和为:%lf\n",total_sum);

return 0;

}

8.输出所有的“水仙花数”,所谓“水仙花数”是指一个3位数,其各位数字立方和等于该数本身。例如,153是水仙花数,因为153=1*+5*+3。

答案解析:

从题目当中得到”水仙花数“为一个3位数,则范围确定为[100, 999]。另外需要获取该数字的百位数字,十位数字,个位数字相加起来等于该数本身,则我们需要使用到%除的方式,来获取每一个位权的数字。

代码示例:

#include <stdio.h>

int main()

{

//a表示百位数字,b表示十位数字,c表示各位数字

int a, b, c;

for (int i = 100; i <= 999; i++)

{

a = i / 100;

b = (i / 10) % 10;

c = i % 10;

if (a * a * a + b * b * b + c * c * c == i)

{

printf("%d\n", i);

}

}

return 0;

}

第五章目录回顾:

5.1为什么需要循环控制110

5.2用while语句实现循环111

5.3用do…while语句实现循环113

5.4for语句实现循环116

5.5循环的嵌套120

5.6几种循环的比较121

5.7改变循环执行的状态121

5.7.1用break语句提前终止循环122

5.7.2用continue语句提前结束本次循环123

5.7.3break语句和continue语句的区别124

5.8循环程序举例127

 

第6章利用数组处理批量数据

C语言总结(一维数组、二维数组、字符数组和字符串)_快乐的兔子1的博客-CSDN博客_c语言一维数组 字符串 比较

数组的定义及其理解:用来表示同一性质的数据(比如说一个班的30名同学的成绩)

  1. 数组是一组有序数据的集合。数组中各数据的排列是有一定规律的,小标代表数据在数组中的序号。
  2. 用一个数组名(如s)和下标(如15)来唯一地确定数组中的元素,如s15来唯一地确定数组中的元素,如s15 就代表第十五个学生的成绩。
  3. 数组中的每一个元素都属于同一个数据类型。不能把不同类型的数据(如学生的成绩和学生的性别)放在同一个数组中。
  4. 在计算机中只能输入有限的单个字符而无法表示上下标,C语言中就规定用方括号中的数字来表示下标,如用s[15]表示s15  ,即第15个学生的成绩。

将数组与循环相结合起来可以有效的处理大批量的数据,大大提高工作效率

 

 

怎样定义和引用一维数组

int a[10];

表示定义了一个整型数组,数组名为a,此数组中包含了10个整型元素。

注意a[10]中是从a[0]开始到a[9]结束 没有a[10]!!!

常量表达式中可以包含常量和符号常量,如inta[3+5]是合法的,不能包含变量,如int a[n];是不合法的,也就是说,C语言不允许对数组的大小作动态定义,即数据的大小不依赖于程序运行过程中变量的值。

例题:简化的插入排序

输入一个正整数 repeat (0<repeat<10),做 repeat 次下列运算:

输入一个正整数 n(0<n<=9)和一组(n个)有序的整数,再输入一个整数 x,把 x 插入到这组数据中,使该组数据仍然有序。

输入输出示例:括号内为说明

输入:

4               (repeat=4)

5               (数据的个数n=5)

1 2 4 5 7       (5个有序整数)

3               (待插入整数x=3)

4               (数据的个数n=4)

1 2 5 7        (4个有序整数)

-10             (待插入整数x=-10)

3               (数据的个数n=3)

1 2 4        (3个有序整数)

100             (待插入整数x=100)

5               (数据的个数n=5)

1 2 4 5 7       (5个有序整数)

4               (待插入整数x=4)

输出:

1 2 3 4 5 7

-10 1 2 5 7

1 2 4 100

1 2 4 4 5 7

#include <stdio.h>

int main(void)   

{

    int i, j, n, x;

    int repeat, ri;

    int a[10];

    scanf("%d", &repeat);

    for(ri = 1; ri <= repeat; ri++){

        scanf("%d", &n);

        for(i = 0; i < n; i++)

         scanf("%d", &a[i]);

         scanf("%d", &x);

if (x >= a[n-1]) a[n] = x;  /* 特殊情况:若x比所有的元素都要大 */

         else

for(i = 0; i < n; i++) {

  if(x > a[i])   continue;  /* 将x 插入到合适的位置*/

  j = n - 1;     /* 从当前要插入的位置往后移位 */

 while(j >= i){

  a[j+1] = a[j];

  j--;   

 }       

      a[i] = x;      /* 将x查到当前的位置 */

              break;

}

     for(i = 0; i < n + 1; i++)

            printf("%d ", a[i]);

        putchar('\n');   

    }

}

             

一维数组的初始化

int a[10]={0,1,2,3,}

int a[10]={0}未赋值的部分自动设定为0

用数组来求fibonacci数列问题:

#include<stdio.h>

int main()

int i;

int f[20]={1,1};

for(i=2;i<20;i++)

{

f[i]=f[i-2]+f[i-1];

for(i=2;i<20;i++)

{

if(i%5==0) printf(“\n”);

printf(“%12d”,f[i]);

}

printf(“\n”);

return 0;

}

冒泡排序例题:

(算法入门,算法的重要性!)

http://t.csdn.cn/ERGm9

数字的排序:

#include <stdio.h>

#define SIZE 10

int main()

{

    int a[SIZE]={12 ,43,9,13,67,98,101,89,3,35};//十个数的无序数列

    int i,j,t;

    printf("此程序使用冒泡排序法排列无序数列!\n");

    //冒泡排序

    for(i=0;i<10-1;i++)//n个数的数列总共扫描n-1次

    {

        for(j=0;j<10-i-1;j++)//每一趟扫描到a[n-i-2]与a[n-i-1]比较为止结束

        {

            if(a[j]>a[j+1])//后一位数比前一位数小的话,就交换两个数的位置(升序)

            {

               t=a[j+1];

               a[j+1]=a[j]; //t就是一个中间量用来交换2个数的值的

               a[j]=t;

            }

        }

    }

    printf("排列好的数列是:\n");

    //输出排列好得数列

    for(i=0;i<10;i++)

    {

        printf("%d ",a[i]);

    }

    return 0;

}

字符排序:

用函数来解决这个问题:

#include <stdio.h>

#define SIZE 10

int main()

{

    char a[SIZE]={'i','l','o','v','e','y','o','u','y','x'};//十个数的无序数列

    int i,j;

    char t;

    printf("此程序使用冒泡排序法排列无序数列!\n");

    //冒泡排序

    for(i=0;i<10-1;i++)//n个数的数列总共扫描n-1次

    {

        for(j=0;j<10-i-1;j++)//每一趟扫描到a[n-i-2]与a[n-i-1]比较为止结束

        {

            if(a[j]>a[j+1])//后一位数比前一位数小的话,就交换两个数的位置(升序)

            {

               t=a[j+1];

               a[j+1]=a[j];

               a[j]=t;

            }

        }

    }

    printf("排列好的字符组是:\n");

    //输出排列好得吃数列

    for(i=0;i<10;i++)

    {

        printf("%c ",a[i]);

    }

    return 0;

}

#include <stdio.h>

void function(char a[],int);//尤其注意,此处的函数声明必须是char a[],因为这里穿的是地址,不能仅仅使用char

int main()

{

    int i;

    char a[10]={'i','l','o','v','e','y','o','u','y','x'};//十个数的无序字符数列

    printf("此程序使用冒泡排序法排列无序数列!\n");

    function(a,10);//调用冒泡排序

    printf("排列好的字符组是:\n");

    //输出排列好得吃数列

    for(i=0;i<10;i++)

    {

        printf("%c ",a[i]);

    }

    return 0;

}

void function(char a[],int m)

{

    //冒泡排序

    int i,j;

    char t;

    for(i=0;i<m-1;i++)//n个数的数列总共扫描n-1次

    {

        for(j=0;j<m-i-1;j++)//每一趟扫描到a[n-i-2]与a[n-i-1]比较为止结束

        {

            if(a[j]>a[j+1])//后一位数比前一位数小的话,就交换两个数的位置(升序)

            {

               t=a[j+1];

               a[j+1]=a[j];

               a[j]=t;

            }

        }

    }

    return;

}

6.2怎样定义和引用二维数组

二维数组常称为矩阵。把二维数组写成行和列的排列形式,可以有助于形象化地理解二维数组的逻辑结构。

float pay[3][6];

3*6(3行6列)

多维数组在内存中的排列顺序为:第一维的下标变化最慢,最右边的下标变化最快。例如:

a[0][0][0]>>a[0][0][1]>>a[0][0][2]>>a[0][1][0]>>a[0][1][1]>>a[0][1][2]>>a[1][0][0]

*交换行和列*

#include<stdio.h>

int main()

{

    int n,m,i,j,k;

    int num1,num2;//要交换的两行或两列

    char ch;//决定进行行变换还是列变换

    int rek;//进行几次操作

    int tmp;

    scanf("%d %d",&n,&m);

    int arr[n][m];

    //输入矩阵

    for(i=0;i<n;i++)

    {

        for(j=0;j<m;j++)

        {

            scanf("%d",&arr[i][j]);

        }

    }

    

    scanf("%d",&k);

    

    for(rek=0;rek<k;rek++)

    {

       scanf(" %c %d %d",&ch,&num1,&num2);

        if(ch=='r')//进行行变换

        {

            for(j=0;j<m;j++)

            {

                tmp=arr[num1-1][j];//进行行变换的时候约定矩阵的行不变循环递归列然后交换即可

                arr[num1-1][j]=arr[num2-1][j];

                arr[num2-1][j]=tmp;

            }

        }

        else if(ch=='c')//进行列变换

        {

            for(i=0;i<n;i++)

            {

                tmp=arr[i][num1-1];//同理进行列变换的时候约定列为需要交换的两列不变后,循环递归循环行即可。

                arr[i][num1-1]=arr[i][num2-1];

                arr[i][num2-1]=tmp;

            }

        }

    }

    

    for(i=0;i<n;i++)

    {

        for(j=0;j<m;j++)

        {

            printf("%d ",arr[i][j]);

        }

        printf("\n");

    }

      

    return 0;

}

 

 

 

6.3字符数组

字符型数据是以字符的ASCII代码存储在存储单元中的,一般占1字节

C语言中没有字符串类型,也没有字符串变量,字符串是存放在字符串数组中的。

6.3.1怎样定义字符数组

char c[10]

由于字符型数据是以整数形式(ASCII代码)存放的,因此也可以用整型数组来存放字符数据,例如:

int c[10]; //合法,但浪费存储空间

c[0]=‘a’

6.3.4字符串和字符串结束标志

C语言规定了一个“字符串结束标志”,以字符串‘\0’作为结束标志。如果字符数组中存放有若干字符,前面9个字符都不是空字符‘\0’,而第十个字符是‘\0’,则认为数组中有一个字符串,其有效字符为9个。也就是说,在遇到字符‘\0’时,表示字符串结束,把它前面的字符组成一个字符串。

注意:C系统在用字符数组存储字符串常量时会自动加一个‘\0’作为结束符。例如“C program”共有9个字符。字符串是存放在一维数组中的,在数组中占10个字节,最后有一个字节‘\0’是系统自动加上的。

说明:‘\0’代表ASCII码为0的字符,从ASCII码表中可以查到,ASCII码为0的字符不是一个可以显示的字符,而是一个“空操作符”,即它什么也不做。用它来作为字符串结束标志不会产生附加的操作或增加有效字符,只起一个供辨别的标志。

对字符串的说明:

①不能用运算符对字符串做运算

②通过数组的方式可以遍历字符串

6.3.5字符数组的输入输出

字符串的结尾有‘\0’          

Char string[8];

Scanf (“%s”,string);       //前面不允许加&(因为在C语言中数组名代笔该数组第一个元素的地址或者也可以说是数组的起始地址)

Printf (“%s”,string);

安全的输入:

Char string[8];

Scanf (“%7s”,string);

这个7代表:最多允许读入的字符数量,应比数组数组的大小小1

6.3.6使用字符串处理函数

注意:在使用后字符串处理函数时,应当在程序文件的开头用

#include <string.h>

puts函数——输出字符串的函数

Puts(str);

gets函数——输入字符串的函数

Gets(str);

Strcat函数———字符串连接函数

Strcat(字符数组1,字符数组2)

#include <stdio.h>  

#include <string.h>  

int main(void)  {

    char str1[6] = "hello";  

    char str2[6] = "world";  

    strcat(str1,str2);

    printf("str1 = %s\n",str1);  

    printf("str2 = %s\n",str2);

    int len = strlen(str1);

    printf("len的长度:%d\n",len);

    

    return 0;  

}

说明:

  1. 字符数组1必须足够大,以便容纳连接后的新字符串
  2. 连接两个字符串的后面都有‘\0’,连接时将字符串1后面的‘\0’取消,只在最后保留‘\0’。

Strcpystrncpy——字符串复制函数

Strcpy(字符数组1,字符串2)

Char strl[10],str2[]“china”;

Strcpy(str1;str2);

说明:

  1. 字符数组1必须足够大,以便容纳连接后的新字符串
  2. “字符数组1”必须写出数组名形式如(str1),“字符串2”可以是字符数组名,也可以是一个字符串常量。

Strcmp——字符串比较函数

Strcmp(字符串1,字符串2)

字符串比较的规则是:将两个字符串自左向右逐个字符相比(按ASCII码值大小比较)直到出现不同的字符或者是遇到‘\0’为止。

  1. 如全部字符相同,则认为两个字符串相等;
  2. 若出现并不相同的字符,则以第1对不相同的字符的比较结果为准。

比较的结果由函数值带回:

  1. 如果字符串1与字符串2相同,则函数值为0。
  2. 如果字符串1>字符串2,则函数值为一个正整数
  3. 如果字符串1<字符串2,则函数值为一个负整数

这样使用if(strcmp(str1,str2)>0)

Printf(“yes”)

Strlen函数——测字符串长度的函数

是string length(字符串的长度)的缩写。

Char str[10]=“CHINA”

Printf(“%d”,strlen(str));

输出的结果不是10,也不是6,而是5。

Strlwr函数——转换为小写的函数         

Strlwr(字符串)          

是string lowercase的缩写

Strupr函数——转换为大写的函数

Strupr(字符串)    

6.3.7字符数组应用举例

70013    将数组中的数逆序存放 

输入一个正整数 repeat (0<repeat<10),做 repeat 次下列运算:

输入一个正整数 n (1<n<=10),再输入 n 个整数,存入数组a中,先将数组a中的这n个数逆序存放,再按顺序输出数组中的n个元素。

输入输出示例:括号内为说明

输入

2            (repeat=2)

4            (n=4)

10 8 1 2

5            (n=5)

1 2 5 4 0

输出

2 1 8 10

0 4 5 2 1

#include <stdio.h>

int main(void)

{

    int i, n, temp;

    int repeat, ri;

    int a[10];

    scanf("%d", &repeat);

    for(ri = 1; ri <= repeat; ri++){

        scanf("%d", &n);

        for(i = 0; i < n; i++)

            scanf("%d", &a[i]);

i=0;

for(i=0;i<n/2;i++){

temp=a[i];

a[i]=a[n-1-i];

a[n-1-i]=temp;

}

        for(i = 0; i < n; i++)

            printf("%d ", a[i]);

        printf("\n");

    }

}

70014    求最大值及其下标 

输入一个正整数 repeat (0<repeat<10),做 repeat 次下列运算:

输入一个正整数 n (1<n<=10),再输入n个整数,输出最大值及其对应的最小下标,下标从0开始。

输入输出示例:括号内为说明

输入

3     (repeat=3)

3     (n=3)

1 6 4

4     (n=4)

10 8 1 9

5     (n=5)

1 2 0 4 5

输出

max = 6, index = 1    (最大值6的下标是1)

max = 10, index = 0   (最大值10的下标是0)

max = 5, index = 4    (最大值5的下标是4)

#include <stdio.h>

int main(void)

{

    int i, index, n;

    int ri, repeat;

    int a[10];

    scanf("%d", &repeat);

    for(ri = 1; ri <= repeat; ri++){

        scanf("%d", &n);

        for(i = 0; i < n; i++)

            scanf("%d", &a[i]);

index=0;

a[index]=a[0];

for(i = 1; i < n; i++){

if(a[index]<a[i]){

a[index]=a[i];

index=i;

}

}

        printf("max = %d, index = %d\n", a[index], index);

    }

}

70015    交换最小值和最大值

输入一个正整数 repeat (0<repeat<10),做 repeat 次下列运算:

输入一个正整数 n (1<n<=10),再输入 n 个整数,将最小值与第一个数交换,最大值与最后一个数交换,然后输出交换后的 n 个数。

输入输出示例:括号内为说明

输入

3         (repeat=3)

5         (n=5)

8 2 5 1 4

4         (n=4)

1 5 6 7

5         (n=5)

5 4 3 2 1

输出

After swap: 1 2 5 4 8

After swap: 1 5 6 7

After swap: 1 4 3 2 5

#include <stdio.h>

int main(void)

{

    int i, index, n, t;

    int repeat, ri;

    int a[10];

    scanf("%d", &repeat);

    for(ri = 1; ri <= repeat; ri++){

        scanf("%d", &n);

        for(i = 0; i < n; i++)

            scanf("%d", &a[i]);

index=0;

for(i = 1; i < n; i++){

if(a[i]<a[index])

index=i;

}

t=a[index];   /* 最小值与第1个数交换 */

a[index]=a[0];

a[0]=t;

index=0;

for(i = 1; i < n; i++){

if(a[i]>a[index])

index=i;

}

t=a[index];     /* 最大值与最后一个数交换 */

a[index]=a[n-1];

a[n-1]=t;

        printf("After swap: ");

        for(i = 0; i < n; i++)

            printf("%d ", a[i]);

        printf("\n");

    }

}

70016    选择法排序

输入一个正整数 repeat (0<repeat<10),做 repeat 次下列运算:

输入一个正整数 n (1<n<=10),再输入 n 个整数,将它们从大到小排序后输出。

输入输出示例:括号内为说明

输入

3           (repeat=3)

4           (n=4)

5 1 7 6

3           (n=3)

1 2 3

5           (n=5)

5 4 3 2 1

输出

After sorted: 7 6 5 1

After sorted: 3 2 1

After sorted: 5 4 3 2 1

#include <stdio.h>

int main(void)

{

    int i, index, k, n, temp;

    int repeat, ri;

    int a[10];

    scanf("%d", &repeat);

    for(ri = 1; ri <= repeat; ri++){

        scanf("%d", &n);

        for(i = 0; i < n; i++)

            scanf("%d", &a[i]);

for(k=0;k<n-1;k++){

index=k;

for(i=k+1;i<n;i++)

if(a[i]>a[index])

index=i;

temp=a[index];

a[index]=a[k];

a[k]=temp;

}

        printf("After sorted: ");

        for(i = 0; i < n; i++)

            printf("%d ", a[i]);

        printf("\n");

    }

}

70017    在数组中查找指定的元素

输入一个正整数 repeat (0<repeat<10),做 repeat 次下列运算:

输入一个正整数 n (1<n<=10),再输入 n 个整数,将它们存入数组 a 中,再输入一个整数 x,然后在数组 a 中查找与 x 相同的元素,如果找到,输出 x 在数组 a 中对应元素的最小下标,如果没有找到,输出相应信息。

输入输出示例:括号内为说明

输入:

2 (repeat=2)

6 (n=6)

1 3 5 7 9 5

5 (x=5)

4 (n=4)

1 3 5 7

2 (x=2)

输出:

5: a[2]

2: not found

#include <stdio.h>

int main(void)

{

    int flag, i, n, x;

    int repeat, ri;

    int a[10];

    scanf("%d", &repeat);

    for(ri = 1; ri <= repeat; ri++){

flag=0;          /* 若标志不重新初始化的化,就会一影响下一轮循环的计算 */

        scanf("%d", &n);

        for(i = 0; i < n; i++) scanf("%d", &a[i]);

        scanf("%d", &x);

for (i = 0; i < n; i++) {

if(x==a[i])

{

flag = 1;

break;

}

}

        if(flag != 0) printf( "%d: a[%d]\n", x, i);

        else printf( "%d: not found\n", x);

    }

}

70021    求矩阵各行元素之和

输入2个正整数 m 和 n (1<=m<=6, 1<=n<=6),然后输入矩阵 a(m 行 n 列)中的元素,分别求出各行元素之和,并输出。

输出使用语句:printf("sum of row %d is %d\n", i, sum);

输入输出示例:括号内为说明

输入:

3 2 (m=3,n=2)

6  3

1  -8

3  12

输出:

sum of row 0 is 9

sum of row 1 is -7

sum of row 2 is 15

#include <stdio.h>

int main(void)

{

    int i, j, m, n, sum;

    int a[6][6];

    scanf("%d%d",&m,&n);

    for(i = 0; i < m; i++)    

        for(j = 0; j < n; j++)

            scanf("%d", &a[i][j]);

for(i = 0; i < m; i++){   

sum=0;

for(j = 0; j < n; j++)

sum+=a[i][j];

printf("sum of row %d is %d\n", i, sum);

}

}

70022    矩阵运算

程序填空,不要改变与输入输出有关的语句。

输入一个正整数 repeat (0<repeat<10),做 repeat 次下列运算:

读入 1 个正整数 n(1≤n≤6), 再读入 n 阶方阵 a , 计算该矩阵除副对角线、最后一列和最后一行以外的所有元素之和。副对角线为从矩阵的右上角至左下角的连线。

输入输出示例:括号内为说明

输入:

1        (repeat=1)

4        (n=4)

2 3 4 1

5 6 1 1

7 1 8 1

1 1 1 1

sum = 35   (2+3+4+5+6+7+8=35)

#include "stdio.h"

int main(void)

{

    int i, j, n, sum;

    int repeat, ri;

    int a[6][6];

    scanf("%d", &repeat);

    for(ri = 1; ri <= repeat; ri++){

        scanf("%d", &n);

        for(i = 0; i < n; i++)

            for(j = 0; j < n; j++)

                scanf("%d", &a[i][j]);

sum=0;

for(i = 0; i < n; i++){

for(j = 0; j < n; j++)

sum+=a[i][j];

}

for(i = 1; i < n-1; i++)

sum-=a[i][n-i-1];  /* 减去副对角线的元素 */

for(j =0; j <n; j++)

sum-=a[n-1][j];   /* 减去最下一行 */

for(i =0; i <n; i++)

sum-=a[i][n-1];   /* 减去最右一列 */

sum=sum+a[n-1][n-1];    /*  右下角元素减了两次,补回一次 */

        printf("sum = %d\n", sum);

    }

}

70023    九九乘法表  

程序填空,不要改变与输入输出有关的语句。

输入一个正整数 repeat (0<repeat<10),做 repeat 次下列运算:

输入一个正整数 n(0<n<10),输出九九乘法表的前n×n项。

提示:将乘数、被乘数和乘积放入一个二维数组中,再输出该数组。

输入输出示例:括号内为说明

输入:

1       (repeat=1)

3       (n=3)

输出:

*   1   2   3

1   1

2   2   4

3   3   6   9

#include "stdio.h"

int main(void)

{

    int i, j, n;

    int a[10][10];

    int repeat, ri;

    scanf("%d",&repeat);

    for(ri = 1; ri <= repeat; ri++){

        scanf("%d", &n);

for(j = 0; j <= n; j++)

a[0][j]=j;   /* 乘数 */

for(i = 0; i <= n; i++)

a[i][0]=i;   /* 被乘数 */

for(i = 1; i <= n; i++)

            for(j = 1; j <= n; j++)

a[i][j]=i*j;  /* 结果 */

        for(i = 0; i <= n; i++){

            for(j = 0; j <= n; j++)

                if(i == 0 && j == 0) printf("%-4c", '*');

                else if(i == 0 || j <= i) printf("%-4d", a[i][j]);

            printf("\n");

        }

    

}

}

70024    判断上三角矩阵

输入一个正整数 repeat (0<repeat<10),做 repeat 次下列运算:

输入1 个正整数 n (1≤n≤6)和 n 阶方阵 a 中的元素,如果 a 是上三角矩阵, 输出"YES", 否则, 输出"NO"。上三角矩阵指主对角线以下的元素都为0的矩阵, 主对角线为从矩阵的左上角至右下角的连线。

输入输出示例:括号内为说明

输入:

2       (repeat=2)

3   (n=3)

1  2  3   

0  4  5  

0  0  6

2   (n=2)

1  0  

-8  2   

输出:

YES

NO

#include "stdio.h"

#include "math.h"

int main(void)

{

    int flag, i, j, n;

    int a[6][6];

    int repeat, ri;

    scanf("%d", &repeat);

    for(ri = 1; ri <= repeat; ri++){

        scanf("%d", &n);

        for(i = 0; i < n; i++)

            for(j = 0; j < n; j++)

                scanf("%d", &a[i][j]);

flag=1;

for(i = 0; i < n; i++){

for(j = 0; j < i; j++){     

if(a[i][j]!=0){

flag=0; break;   /* 只要一个数值不满足就可以否定 */

}

}

if (!flag) break;

}

        if(flag != 0)  printf("YES\n");

        else  printf("NO\n");

    }

}

                                                                            

第六章课后习题:

谭浩强 C语言程序设计第五版 第六章 习题 答案_月已满西楼的博客-CSDN博客_月已满西楼谭浩强

3. 求一个3 X 3的整形矩阵对角线元素之和

【答案解析】

矩阵:即二维数组,矩阵行和列相等的二维数组称为方阵。

1 2 3

4 5 6

7 8 9

左上角到右下角对角线上数字:行下标和列下标相加

右上角到左下角对角线上数字:列下标减1 行下标加一

通过两个循环来取到对角线上的元素,并对其求和即可。

【代码实现】

#include<stdio.h>

int main()

{

int array[3][3];

int sumLT2RB = 0;  // 标记左上角到右下角对角线元素之和

int sumRT2LB = 0;  // 标记右上角到左下角对角线元素之和

printf("请输入3行3列的矩阵:\n");

for (int i = 0; i < 3; ++i)

{

for (int j = 0; j < 3; ++j)

scanf("%d", &array[i][j]);

}

// 左上角到右下角对角线

for (int i = 0; i < 3; ++i)

sumLT2RB += array[i][i];

for (int i = 0, j = 2; i < 3; ++i, j--)

sumRT2LB += array[i][j];

printf("左上角到右下角对角线元素之和: %d\n", sumLT2RB);

printf("右上角到左下角对角线元素之和: %d\n", sumRT2LB);

return 0;

}

5. 将一个数组中的值按逆序重新存放。例如:原来顺序为8,6,5,4,1。要求改为1,4,5,6,8

【答案解析】

该题为数组的逆置,具体处理方式如下:

如果begin < end时,则循环进行一下操作

给定两个下标begin和end,begin放在数组起始的位置,end放在数组最后一个元素的位置

交换begin和end位置的元素

begin往后移动,end往前移动

【代码实现】

#include<stdio.h>

int main()

{

int array[5] = {8,6,5,4,1};

int begin = 0, end = 4;

printf("逆序之前数组为:");

for (int i = 0; i < 5; ++i)

printf("%d ", array[i]);

printf("\n");

// 逆序:begin在数组最左侧,end在数组最右侧

// 只要begin < end,将begin和end位置元素进行交换

// 然后begin往后移动一步,end往前移动一步

while (begin < end)

{

int temp = array[begin];

array[begin] = array[end];

array[end] = temp;

begin++;

end--;

}

printf("逆置之后数组为:");

for (int i = 0; i < 5; ++i)

printf("%d ", array[i]);

printf("\n");

return 0;

}

 

第六章目录回顾:

6.1怎样定义和引用一维数组139

6.1.1怎样定义一维数组140

6.1.2怎样引用一维数组元素140

6.1.3一维数组的初始化142

6.1.4一维数组程序举例142

6.2怎样定义和引用二维数组145

6.2.1怎样定义二维数组146

6.2.2怎样引用二维数组的元素147

6.2.3二维数组的初始化148

6.2.4二维数组程序举例149

6.3字符数组151

6.3.1怎样定义字符数组151

6.3.2字符数组的初始化152

6.3.3怎样引用字符数组中的元素152

6.3.4字符串和字符串结束标志153

6.3.5字符数组的输入输出156

6.3.6使用字符串处理函数157

6.3.7字符数组应用举例162

  1. 用函数实现模块化程序设计

7.1为什么要用函数

为了更好的实现模块化程序设计    函数(function)也有功能的意思

                                                                                             7.2怎样定义函数

为什么要定义函数?

事先编辑好的功能,编译系统按照定义的功能去执行

定义函数应包括以下内容:

  1. 指定函数的名字,以便以后按名调用
  2. 指定函数的类型,即函数返回值的类型
  3. 指定函数的参数的名字和类型,以便在调用函数时向它们传递数据。对无参函数不需要这项
  4. 指定函数应该完成什么操作,也就是函数是做什么工作的,即函数的功能

定义函数的方法:

  1. 定义无参函数的方法:

函数名()

函数体

或者

函数名(void)

函数体

函数体内包含声明部分和语句部分

  1. 定义有参函数

函数程序设计

编写一个函数,利用参数传入一个3位数number,找出101~number之间所有满足下列两个条件的数:

它是完全平方数,又有两位数字相同,如144、676等,函数返回找出这样的数据的个数。请同时编写主函数。

例:(括号内为说明)

输入

3 (repeat=3)

150

500

999

输出

count=2

count=6

count=8

#include <stdio.h>

#include <string.h>

int search(int n);           //对函数的声明

int main(void)

{

int number,ri,repeat;

scanf("%d",&repeat);

for(ri=1;ri<=repeat;ri++){

do{

scanf("%d",&number);

}while(number<101||number>999);

printf("count=%d\n",search(number));

}

return 0;

}

int search(int n)            //对函数的定义

{

int i,j,a1,a2,a3, flag=0;

for (i = 101; i <= n; i++)

{

for (j = 1; j <= i; j++)

{

if (j * j == i)

{

a1 = i % 10;

a2 = i / 10 % 10;

a3 = i /100 % 10;

if ( (a1==a2)||(a1==a3) || (a2==a3) )

{

flag += 1;

break;

}

}

}

}

return flag;

}

定义有参函数的一般形式:

函数名(形式参数表列)

函数体

 

7.3调用函数

7.3.1函数调用的形式171

计算函数P(n,x) (函数递归)

输入一个正整数repeat (0<repeat<10),做repeat次下列运算:

输入一个整数n (n>=0)和一个双精度浮点数x,输出函数p(n,x)的值(保留2位小数)。

                [1                                    (n=0)

      p(n, x) =   [x                                    (n=1)

                [((2*n-1)*p(n-1,x)-(n-1)*p(n-2,x))/n        (n>1)

例:括号内是说明

输入

3  (repeat=3)

0 0.9 (n=0,x=0.9)

1 -9.8 (n=1,x=-9.8)

10 1.7 (n=10,x=1.7)

输出

p(0, 0.90)=1.00

p(1, -9.80)=-9.80

p(10, 1.70)=3.05

#include <stdio.h>

double p(int n, double x);       //对函数的声明

int main(void)

{

   int repeat, ri;

   int n;

   double x, result;

   scanf("%d", &repeat);

   for(ri = 1; ri <= repeat; ri++)

   {

       scanf("%d%lf", &n, &x);

       result = p(n, x);

       printf("p(%d, %.2lf)=%.2lf\n", n, x, result);

   }

}

double p(int n, double x)        //对函数的定义

{

double t;

if(n==0) t=1;

else if(n==1)

t=x;

else

t=((2*n-1)*p(n-1,x)-(n-1)*p(n-2,x))/n;

return t;

}

7.3.2函数调用时的数据传递

  1. 形式参数和实际参数

在定义函数时函数名后面的括号中的变量名称为“形式参数”“虚拟参数”

在主调函数中调用一个函数时,函数名后面括号中的参数称为“实际参数”

实际参数可以是常量,变量或表达式。

  1. 实参和形参间的数据传递

在调用函数过程中,系统会把实参的值传递给被调用函数的形参。也可以这么理解,形参从实参得到一个值。该值在函数调用期间有效,可以参加该函数的运算。

7.3.3函数调用的过程

一个函数只能有一个返回值,(想返回多个可以参考后面的指针中的内容)

如果函数不需要返回值,则不需要return语句,这是函数的类型应定义为void类型。

7.4对被调用函数的声明和函数原型

在一个函数中调用另一个函数(即被调用函数)需要具备以下条件:

  1. 首先被调用的函数必须是已经定义的函数(是库函数或用户自己定义的函数)
  2. 如果使用库函数,应该在本文件开头用#include指令将调用有关库函数时所需用到的信息“包含”到本文件中去

#include<stdio.h>在stdio.h文件中包含了输入输出库函数的声明

使用数学库中的函数,应使用#include<math.h>

(3)如果使用用户自己定义的函数,而该函数的位置在调用它的函数(即主调函数)的后面(在同一文件中),应该在主调函数中被调用的函数作声明(declaration)。声明的作用是把函数名,函数参数的个数和参数类型等信息通知编译系统,以便在遇到函数调用时,编译系统能正确识别函数并检查调用是否合法。

思考总结:

可以发现,函数的声明和函数定义中的第1行(函数首部)基本上是相同的。只差一个分号(函数声明比函数定义中的首行多一个分号)

为什么要用函数的首部来作为函数声明呢?

这是为了对函数调用的合法性进行检查。因为在函数的首部包含了检查调用函数是否合法的基本信息(包含了函数名,函数值类型,参数个数,参数类型和参数顺序),在检查函数调用时要求函数名,函数类型,参数个数,参数类型和参数顺序必须和函数声明一致,实参类型必须与函数声明中的形参类型相同,这样就能保证函数的正确调用。

函数的“定义”和‘声明’并不是同一回事

函数的定义是指对函数功能的确立,包括指定函数名,函数值类型,形参及其类型以及函数体等,它是一个完整的,独立的函数单位。

而函数的声明的作用则是把函数名,函数值类型,参数个数,参数类型和参数顺序通知编译系统,以便在调用该函数时系统按此进行对照检查(例如:函数名,函数值类型,参数个数,参数类型和参数顺序),他不包含函数体。

7.5函数的嵌套调用

C语言的函数定义是互相平行,独立的,也就是说,在定义函数时,一个函数内不能再定义另一个函数,即不能嵌套定义,但可以嵌套调用函数,即在调用一个函数的过程中,又调用另一个函数。

 

7.6函数的递归调用

在调用一个函数的过程中又出现直接或间接地调用该函数本身,称为函数的递归调用。

C语言函数递归调用_外太空程序员的博客-CSDN博客_c语言函数的递归调用

范例:计算5的阶乘,其实就是计算 1*2*3*4*5,

我们并不知道5的阶乘是多少,但是我们知道一点,4的阶乘 * 5他就等于5的阶乘。

我们并不知道4的阶乘是多少,但是我们知道一点,3的阶乘 * 4他就等于4的阶乘。

我们并不知道3的阶乘是多少,但是我们知道一点,2的阶乘 * 3他就等于3的阶乘。

所以递归调用的出口肯定是在1的阶乘,因为1的阶乘是1,可以作为出口,我们能够求出2的阶乘,也就是1*2;以此类推。

#include<stdio.h>

// 函数声明

int dg_jiecheng(int n);

int main()  //主函数

{

    int result = dg_jiecheng(5);

    printf("result=%d\n",result);  // 结果为120

}

// 函数定义

int dg_jiecheng(int n)

{

    int result;  // 局部变量,保存阶乘结果

    if(n==1) // 1的阶乘就是1

    {

        return 1;  // 这里就是该递归调用的出口

    }

    else

    {

      // 第一次是 result = dg_jiecheng(4)*5,然后进入到了 dg_jiecheng(4),这行代码就被暂存了;

         第二次是 result = dg_jiecheng(3)*4,然后进入到了 dg_jiecheng(3),这行代码就被暂存了;

         第三次是 result = dg_jiecheng(2)*3,然后进入到了 dg_jiecheng(2),这行代码就被暂存了;

         第四次是 result = dg_jiecheng(1)*2,然后进入到了 dg_jiecheng(1),这行代码就被暂存了;

       此时,dg_jiecheng(1)的出口条件成立了,终于,能够执行return 1,这可是 return 语句第一次捞着执行。

       第一次:return 1,返回的是1,返回到dg_jiecheng(2)这里:

       return =1*2 并且也执行return result;,返回1*2=2;

返回到dg_jiecheng(3)这里:

       return =2 并且也执行return result;,返回2*3=6;

返回到dg_jiecheng(4)这里:

       return =6 并且也执行return result;,返回6*4=24;

返回到dg_jiecheng(5)这里:

       return =24 并且也执行return result;,返回24*5=120;

        result = dg_jiecheng(n-1)* n;

    }

    return result;

递归的优缺点

优点:

        代码少,代码看起来简洁,精妙。

缺点:

        虽然代码简洁,精妙,但是不好理解。

         如果调用的层次太深,调用栈(内存),可能会溢出,如果真出现这种情况,那么说明不能用递归解决该问题。

        效率和性能都不高,这深层次的调用,要保存的东西很多,所以效率和性能肯定高不起来。有些问题用不用递归都行,有些问题可能必须用递归解决。汉诺塔

递归函数的直接和间接调用:

递归函数的直接调用:调用递归函数f的过程中,f函数有调用自己,这就是直接调用。

递归函数的间接调用:调用函数f1的过程中要调用f2函数,然后f2函数又调用f1函数,这就是间接调用。                                                            

7.7数组作为函数参数

编程题(指针数组)

输入一个正整数repeat (0<repeat<10),做repeat次下列运算:

编写程序,输入一个月份,输出对应的英文名称,要求用指针数组表示12个月的英文名称。

若输入月份错误,输出提示信息。

输入输出示例:括号内为说明

输入:

3 (repeat=3)

5

9

14

输出:

May

September

Wrong input!

#include<stdio.h>

void main()

{

int ri,repeat;

int month;

char *month_name[]={"","January","February","March","April","May","June","July","August","September","October","November","December"};

scanf("%d",&repeat);

for(ri=1;ri<=repeat;ri++){

scanf("%d",&month);

if((month>=1)&&(month<=12))

puts(month_name[month]);

else

printf("Wrong input!");

}

}

        

编程题 (指针数组,查找相同的字符串)

程序填空,不要改变与输入输出有关的语句。

输入一个正整数repeat (0<repeat<10),做repeat次下列运算:

定义一个指针数组将下表的星期信息组织起来,输入一个字符串,在表中查找,若存在,输出该字符串在表中的序号,否则输出-1。

(表格详见实验教材P99)

输入输出示例:括号内为说明

输入:

3 (repeat=3)

Tuesday

Wednesday

year

输出:

3

4

-1

#include<stdio.h>

#include<string.h>

void main()

{

int i,ri,repeat,dateNum;

char *date[]={"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"};

char str[80];

scanf("%d",&repeat);

getchar();

for(ri=1;ri<=repeat;ri ++){

scanf("%s",str);

/*---------*/

dateNum=sizeof(date)/sizeof(char *);

for (i = 0; i<dateNum; i++){

if (strcmp(date[i],str) == 0) {

printf("%d\n",i+1); break;

}

}

if (i==dateNum) printf("%d\n",-1);

}

}

 计算最长的字符串长度

编写一个函数int max_len(char *s[ ],  int n),用于计算有n(n<10)个元素的指针数组s中最长的字符串的长度,并编写主程序验证。

例:(括号内为说明)

输入

4 (n=4)

blue

yellow

red

green

输出

length=6

#include <stdio.h>

#include <string.h>

int max_len(char *s[],int n);

void main()

{

 int i,n;

 char s[10][80],*p[10];

 scanf("%d",&n);

 for(i=0;i<n;i++){

  scanf("%s",s[i]);

  p[i]=s[i];

 }

 printf("length=%d\n",max_len(p,n));

}

int max_len(char *s[],int n)

{

 int max,i;

 max=strlen(s[0]);

 for(i=1;i<n;i++)

if(max<strlen(s[i]))   

max=strlen(s[i]);

  return max;

}

       

 

静态局部变量的值(static局部变量)

有时希望函数中的局部变量的值在函数调用结束后不消失而继续保留原值,即其占用的存储单元不释放,在下一次调用该函数时,该变量已有值(就是上一次函数调用结束时的值),这时就应该指定该局部变量为“静态局部变量”,用关键字static进行声明。

自动变量(auto变量)

函数中的局部变量,如果不专门声明为static(静态)存储类别,都是动态地分配存储空间的,数据存储在动态存储去中,函数中的形参和在函数中定义的局部变量(包括在复合语句中定义的局部变量),都属于此类。在调用该函数时,系统会给这些变量分配存储空间,在函数调用结束时就自动释放这些存储空间。

实际上,关键字auto可以省略,不写auto则隐含指定为“自动存储类别”

int b,c=3    ==========   auto int b,c=3

第七章课后题

C语言程序设计第五版谭浩强 第七章答案_月已满西楼的博客-CSDN博客_c语言第七章课后题答案

第七章目录回顾:

第7章用函数实现模块化程序设计167

7.1为什么要用函数167

7.2怎样定义函数169

7.2.1为什么要定义函数169

7.2.2定义函数的方法170

7.3调用函数171

7.3.1函数调用的形式171

7.3.2函数调用时的数据传递172

7.3.3函数调用的过程174

7.3.4函数的返回值174

7.4对被调用函数的声明和函数原型176

7.5函数的嵌套调用179

7.6函数的递归调用181

7.7数组作为函数参数189

7.7.1数组元素作函数实参189

7.7.2一维数组名作函数参数191

7.7.3多维数组名作函数参数194

7.8局部变量和全局变量196

7.8.1局部变量196

7.8.2全局变量197

*7.9变量的存储方式和生存期201

7.9.1动态存储方式与静态存储方式201

7.9.2局部变量的存储类别202

7.9.3全局变量的存储类别205

7.9.4存储类别小结209

7.10关于变量的声明和定义211

*7.11内部函数和外部函数212

7.11.1内部函数212

7.11.2外部函数213

 

 

第8章善于利用指针

指针?什么是指针?从根本上看,指针(pointer)是一个值为内存地址 

的变量(或数据对象)。正如char类型变量的值是字符,int类型变量的值是 

整数,指针变量的值是地址。在C语言中,指针有许多用法。本章将介绍如 

何把指针作为函数参数使用,以及为何要这样用。 

假设一个指针变量名是ptr,可以编写如下语句: 

ptr = &pooh; // 把pooh的地址赋给ptr

对于这条语句,我们说ptr“指向”pooh。ptr和&pooh的区别是ptr是变量, 

而&pooh是常量。或者,ptr是可修改的左值,而&pooh是右值。还可以把ptr

指向别处: 

ptr = &bah; // 把ptr指向bah,而不是pooh

现在ptr的值是bah的地址。

数组和指针 

数组形式和指针形式有何不同?以上面的声明为例,数组形式(

ar1[]) 

在计算机的内存中分配为一个内含29个元素的数组(每个元素对应一个字 

符,还加上一个末尾的空字符'\0'),每个元素被初始化为字符串字面量对 

应的字符。通常,字符串都作为可执行文件的一部分储存在数据段中。当把 

程序载入内存时,也载入了程序中的字符串。字符串储存在静态存储区 

static memory)中。但是,程序在开始运行时才会为该数组分配内存。此 

743时,才将字符串拷贝到数组中(第 12 章将详细讲解)。注意,此时字符串 

有两个副本。一个是在静态内存中的字符串字面量,另一个是储存在ar1数 

组中的字符串。 

此后,编译器便把数组名ar1识别为该数组首元素地址(&ar1[0])的别 

名。这里关键要理解,在数组形式中,ar1是地址常量。不能更改ar1,如果 

改变了ar1,则意味着改变了数组的存储位置(即地址)。可以进行类似 

ar1+1这样的操作,标识数组的下一个元素。但是不允许进行++ar1这样的操 

作。递增运算符只能用于变量名前(或概括地说,只能用于可修改的左 

值),不能用于常量。 

指针形式(

*pt1)也使得编译器为字符串在静态存储区预留29个元素的 

空间。另外,一旦开始执行程序,它会为指针变量pt1留出一个储存位置, 

并把字符串的地址储存在指针变量中。该变量最初指向该字符串的首字符, 

但是它的值可以改变。因此,可以使用递增运算符。例如,++pt1将指向第 

2 个字符(

o)。 

字符串字面量被视为const数据。由于pt1指向这个const数据,所以应该 

把pt1声明为指向const数据的指针。这意味着不能用pt1改变它所指向的数 

据,但是仍然可以改变pt1的值(即,pt1指向的位置)。如果把一个字符串 

字面量拷贝给一个数组,就可以随意改变数据,除非把数组声明为const。 

总之,初始化数组把静态存储区的字符串拷贝到数组中,而初始化指针 

只把字符串的地址拷贝给指针。程序清单11.3演示了这一点

让你不再害怕指针——C指针详解(经典,非常详细)_唐大麦的博客-CSDN博客_指针

C语言*p、p以及&p的区别,*p和**p的区别_14skyang的博客-CSDN博客_c语言*p

int p; //这是一个普通的整型变量

int *p; //首先从P 处开始,先与*结合,所以说明P 是一个指针,然后再与int 结合,说明指针所指向的内容的类型为int 型.所以P是一个返回整型数据的指针

int p[3]; //首先从P 处开始,先与[]结合,说明P 是一个数组,然后与int 结合,说明数组里的元素是整型的,所以P 是一个由整型数据组成的数组

int *p[3]; //首先从P 处开始,先与[]结合,因为其优先级比*高,所以P 是一个数组,然后再与*结合,说明数组里的元素是指针类型,然后再与int 结合,说明指针所指向的内容的类型是整型的,所以P 是一个由返回整型数据的指针所组成的数组

int (*p)[3]; //首先从P 处开始,先与*结合,说明P 是一个指针然后再与[]结合(与"()"这步可以忽略,只是为了改变优先级),说明指针所指向的内容是一个数组,然后再与int 结合,说明数组里的元素是整型的.所以P 是一个指向由整型数据组成的数组的指针

int **p; //首先从P 开始,先与*结合,说是P 是一个指针,然后再与*结合,说明指针所指向的元素是指针,然后再与int 结合,说明该指针所指向的元素是整型数据.由于二级指针以及更高级的指针极少用在复杂的类型中,所以后面更复杂的类型我们就不考虑多级指针了,最多只考虑一级指针.

int p(int); //从P 处起,先与()结合,说明P 是一个函数,然后进入()里分析,说明该函数有一个整型变量的参数,然后再与外面的int 结合,说明函数的返回值是一个整型数据

Int (*p)(int); //从P 处开始,先与指针结合,说明P 是一个指针,然后与()结合,说明指针指向的是一个函数,然后再与()里的int 结合,说明函数有一个int 型的参数,再与最外层的int 结合,说明函数的返回类型是整型,所以P 是一个指向有一个整型参数且返回类型为整型的函数的指针

int *(*p(int))[3]; //可以先跳过,不看这个类型,过于复杂从P 开始,先与()结合,说明P 是一个函数,然后进入()里面,与int 结合,说明函数有一个整型变量参数,然后再与外面的*结合,说明函数返回的是一个指针,,然后到最外面一层,先与[]结合,说明返回的指针指向的是一个数组,然后再与*结合,说明数组里的元素是指针,然后再与int 结合,说明指针指向的内容是整型数据.所以P 是一个参数为一个整数据且返回一个指向由整型指针变量组成的数组的指针变量的函数.

指针的四方面的内容:

指针的类型、指针所指向的类型、指针的值或者叫指针所指向的内存区、指针本身所占据的内存区。

1.指针的类型

从语法的角度看,你只要把指针声明语句里的指针名字去掉,剩下的部分就是这个指针的类型。这是指针本身所具有的类型。让我们看看例一中各个指针的类型:
(1)int*ptr;//指针的类型是int*
(2)char*ptr;//指针的类型是char*
(3)int**ptr;//指针的类型是int**
(4)int(*ptr)[3];//指针的类型是int(*)[3]
(5)int*(*ptr)[4];//指针的类型是int*(*)[4]

2.指针所指向的类型

当你通过指针来访问指针所指向的内存区时,指针所指向的类型决定了编译器将把那片内存区里的内容当做什么来看待。
从语法上看,你只须把指针声明语句中的指针名字和名字左边的指针声明符*去掉,剩下的就是指针所指向的类型。例如:
(1)int*ptr; //指针所指向的类型是int
(2)char*ptr; //指针所指向的的类型是char
(3)int**ptr; //指针所指向的的类型是int*
(4)int(*ptr)[3]; //指针所指向的的类型是int()[3]
(5)int*(*ptr)[4]; //指针所指向的的类型是int*()[4]

在指针的算术运算中,指针所指向的类型有很大的作用。
指针的类型(即指针本身的类型)和指针所指向的类型是两个概念。当你对C 越来越熟悉时,你会发现,把与指针搅和在一起的"类型"这个概念分成"指针的类型"和"指针所指向的类型"两个概念,是精通指针的关键点之一。我看了不少书,发现有些写得差的书中,就把指针的这两个概念搅在一起了,所以看起书来前后矛盾,越看越糊涂。

3.指针的值----或者叫指针所指向的内存区或地址

指针的值是指针本身存储的数值,这个值将被编译器当作一个地址,而不是一个一般的数值。在32 位程序里,所有类型的指针的值都是一个32 位整数,因为32 位程序里内存地址全都是32 位长。指针所指向的内存区就是从指针的值所代表的那个内存地址开始,长度为si zeof(指针所指向的类型)的一片内存区。以后,我们说一个指针的值是XX,就相当于说该指针指向了以XX 为首地址的一片内存区域;我们说一个指针指向了某块内存区域,就相当于说该指针的值是这块内存区域的首地址。指针所指向的内存区和指针所指向的类型是两个完全不同的概念。在例一中,指针所指向的类型已经有了,但由于指针还未初始化,所以它所指向的内存区是不存在的,或者说是无意义的。
以后,每遇到一个指针,都应该问问:这个指针的类型是什么?指针指的类型是什么?该指针指向了哪里?(重点注意)

4 指针本身所占据的内存区

指针本身占了多大的内存?你只要用函数sizeof(指针的类型)测一下就知道了。在32 位平台里,指针本身占据了4 个字节的长度。指针本身占据的内存这个概念在判断一个指针表达式(后面会解释)是否是左值时很有用。

指针的算术运算

指针可以加上或减去一个整数。指针的这种运算的意义和通常的数值的加减运算的意义是不一样的,以单元为单位。

例二:

char a[20];

int *ptr=(int *)a; //强制类型转换并不会改变a 的类型

ptr++;

在上例中,指针ptr 的类型是int*,它指向的类型是int,它被初始化为指向整型变量a。接下来的第3句中,指针ptr被加了1,编译器是这样处理的:它把指针ptr 的值加上了sizeof(int),在32 位程序中,是被加上了4,因为在32 位程序中,int 4 个字节。由于地址是用字节做单位的,故ptr 所指向的地址由原来的变量a 的地址向高地址方向增加了4 个字节。由于char 类型的长度是一个字节,所以,原来ptr 是指向数组a 的第0 号单元开始的四个字节,此时指向了数组a 中从第4 号单元开始的四个字节。

我们可以用一个指针和一个循环来遍历一个数组,看例子:
例三:

int array[20]={0};

int *ptr=array;

for(i=0;i<20;i++)

    (*ptr)++;

    ptr++;

}

这个例子将整型数组中各个单元的值加1。由于每次循环都将指针ptr加1 个单元,所以每次循环都能访问数组的下一个单元。

再看例子:
例四:

char a[20]="You_are_a_girl";

int *ptr=(int *)a;

ptr+=5;

在这个例子中,ptr 被加上了5,编译器是这样处理的:将指针ptr 的值加上5 sizeof(int),在32 位程序中就是加上了5 4=20由于地址的单位是字节,故现在的ptr 所指向的地址比起加5 后的ptr 所指向的地址来说,向高地址方向移动了20 个字节。
在这个例子中,没加5 前的ptr 指向数组a 的第0 号单元开始的四个字节,加5 后,ptr 已经指向了数组a 的合法范围之外了。虽然这种情况在应用上会出问题,但在语法上却是可以的。这也体现出了指针的灵活性。如果上例中,ptr 是被减去5,那么处理过程大同小异,只不过ptr 的值是被减去5 乘sizeof(int),新的ptr 指向的地址将比原来的ptr 所指向的地址向低地址方向移动了20 个字节。
下面请允许我再举一个例子:(一个误区)

例五:

#include<stdio.h>

int main()

{

char a[20]=" You_are_a_girl";

    char *p=a;

    char **ptr=&p;

    //printf("p=%d\n",p);

    //printf("ptr=%d\n",ptr);

    //printf("*ptr=%d\n",*ptr);

    printf("**ptr=%c\n",**ptr);

    ptr++;

    //printf("ptr=%d\n",ptr);

    //printf("*ptr=%d\n",*ptr);

    printf("**ptr=%c\n",**ptr);

}

误区一、输出答案为Y 和o
误解:ptr 是一个char 的二级指针,当执行ptr++;时,会使指针加一个sizeof(char),所以输出如上结果,这个可能只是少部分人的结果.
误区二、输出答案为Y 和a误解:ptr 指向的是一个char *类型,当执行ptr++;时,会使指针加一个sizeof(char *)(有可能会有人认为这个值为1,那就会得到误区一的答案,这个值应该是4,参考前面内容), 即&p+4; 那进行一次取值运算不就指向数组中的第五个元素了吗?那输出的结果不就是数组中第五个元素了吗?答案是否定的.
正解: ptr 的类型是char **,指向的类型是一个char *类型,该指向的地址就是p的地址(&p),当执行ptr++;时,会使指针加一个sizeof(char*),即&p+4;那*(&p+4)指向哪呢,这个你去问上帝吧,或者他会告诉你在哪?所以最后的输出会是一个随机的值,或许是一个非法操作.
总结一下:
一个指针ptrold 加(减)一个整数n 后,结果是一个新的指针ptrnew,ptrnew 的类型和ptrold 的类型相同,ptrnew 所指向的类型和ptrold所指向的类型也相同。ptrnew 的值将比ptrold 的值增加(减少)n sizeof(ptrold 所指向的类型)个字节。就是说,ptrnew 所指向的内存区将比ptrold 所指向的内存区向高(低)地址方向移动了n 乘sizeof(ptrold 所指向的类型)个字节。指针和指针进行加减:两个指针不能进行加法运算,这是非法操作,因为进行加法后,得到的结果指向一个不知所向的地方,而且毫无意义。两个指针可以进行减法操作,但必须类型相同,一般用在数组方面,不多说了。

 

运算符&*

&是取地址运算符,*是间接运算符。
&a 的运算结果是一个指针,指针的类型是a 的类型加个*,指针所指向的类型是a 的类型,指针所指向的地址嘛,那就是a 的地址。
*p 的运算结果就五花八门了。总之*p 的结果是p 所指向的东西,这个东西有这些特点:它的类型是p 指向的类型,它所占用的地址是p所指向的地址。

int a=12; int b; int *p; int **ptr;

分析:

p=&a; //&a 的结果是一个指针,类型是int*,指向的类型int,指向的地址是a 的地址。

*p=24; //*p 的结果,在这里它的类型是int,它所占用的地址是p 所指向的地址,显然,*p 就是变量a。

ptr=&p; //&p 的结果是个指针,该指针的类型是p 的类型加个*,在这里是int **。该指针所指向的类型是p 的类型,这里是int*。该指针所指向的地址就是指针p 自己的地址。

*ptr=&b; //*ptr 是个指针,&b 的结果也是个指针,且这两个指针的类型和所指向的类型是一样的,所以用&b 来给*ptr 赋值就是毫无问题的了。

**ptr=34; //*ptr 的结果是ptr 所指向的东西,在这里是一个指针,对这个指针再做一次*运算,结果是一个int 类型的变量。

 

指针表达式

一个表达式的结果如果是一个指针,那么这个表达式就叫指针表式。

由于指针表达式的结果是一个指针,所以指针表达式也具有指针所具有的四个要素:指针的类型,指针所指向的类型,指针指向的内存区,指针自身占据的内存。
好了,当一个指针表达式的结果指针已经明确地具有了指针自身占据的内存的话,这个指针表达式就是一个左值,否则就不是一个左值。在例七中,&a 不是一个左值,因为它还没有占据明确的内存。*ptr 是一个左值,因为*ptr 这个指针已经占据了内存,其实*ptr 就是指针pa,既然pa 已经在内存中有了自己的位置,那么*ptr 当然也有了自己的位置。

 

数组和指针的关系

数组的数组名其实可以看作一个指针。看下例:

例九:

int array[10]={0,1,2,3,4,5,6,7,8,9},value;

value=array[0]; //也可写成:value=*array;

value=array[3]; //也可写成:value=*(array+3);

value=array[4]; //也可写成:value=*(array+4);

上例中,一般而言数组名array 代表数组本身,类型是int[10],但如果把array 看做指针的话,它指向数组的第0 个单元,类型是int* 所指向的类型是数组单元的类型即int。因此*array 等于0 就一点也不奇怪了。同理,array+3 是一个指向数组第3 个单元的指针,所以*(array+3)等于3。其它依此类推。

例十:

char *str[3]={

    "Hello,thisisasample!",

    "Hi,goodmorning.",

    "Helloworld"

};

char s[80];

strcpy(s,str[0]); //也可写成strcpy(s,*str);

strcpy(s,str[1]); //也可写成strcpy(s,*(str+1));

strcpy(s,str[2]); //也可写成strcpy(s,*(str+2));

上例中,str 是一个三单元的数组,该数组的每个单元都是一个指针,这些指针各指向一个字符串。把指针数组名str 当作一个指针的话,它指向数组的第0 号单元,它的类型是char **,它指向的类型是char *。

*str 也是一个指针,它的类型是char *,它所指向的类型是char,它指向的地址是字符串"Hello,thisisasample!"的第一个字符的地址,即'H'的地址。注意:字符串相当于是一个数组,在内存中以数组的形式储存,只不过字符串是一个数组常量,内容不可改变,且只能是右值.如果看成指针的话,他即是常量指针,也是指针常量.

str+1 也是一个指针,它指向数组的第1 号单元,它的类型是char**,它指向的类型是char*。

*(str+1)也是一个指针,它的类型是char*,它所指向的类型是char,它指向"Hi,goodmorning."的第一个字符'H'

下面总结一下数组的数组名(数组中储存的也是数组)的问题:
声明了一个数组TYPE array[n],则数组名称array 就有了两重含义:
第一,它代表整个数组,它的类型是TYPE[n];
第二,它是一个常量指针,该指针的类型是TYPE*,该指针指向的类型是TYPE,也就是数组单元的类型,该指针指向的内存区就是数组第0 号单元,该指针自己占有单独的内存区,注意它和数组第0 号单元占据的内存区是不同的。该指针的值是不能修改的,即类似array++的表达式是错误的。在不同的表达式中数组名array 可以扮演不同的角色。在表达式sizeof(array)中,数组名array 代表数组本身,故这时sizeof 函数测出的是整个数组的大小。
在表达式*array 中,array 扮演的是指针,因此这个表达式的结果就是数组第0 号单元的值。sizeof(*array)测出的是数组单元的大小。
表达式array+n(其中n=0,1,2,.....)中,array 扮演的是指针,故array+n 的结果是一个指针,它的类型是TYPE *,它指向的类型是TYPE,它指向数组第n号单元。故sizeof(array+n)测出的是指针类型的大小。在32 位程序中结果是4
例十一:

int array[10];

int (*ptr)[10];

ptr=&array;:

上例中ptr 是一个指针,它的类型是int(*)[10],他指向的类型是int[10] ,我们用整个数组的首地址来初始化它。在语句ptr=&array中,array 代表数组本身。
本节中提到了函数sizeof(),那么我来问一问,sizeof(指针名称)测出的究竟是指针自身类型的大小呢还是指针所指向的类型的大小?
答案是前者。例如:
int(*ptr)[10];
则在32 位程序中,有:
sizeof(int(*)[10])==4
sizeof(int[10])==40
sizeof(ptr)==4
实际上,sizeof(对象)测出的都是对象自身的类型的大小,而不是别的什么类型的大小。

 

指针和结构类型的关系

可以声明一个指向结构类型对象的指针。

例十二:

struct MyStruct

{

    int a;

    int b;

    int c;

};

struct MyStruct ss={20,30,40};

//声明了结构对象ss,并把ss 的成员初始化为20,30 和40。

struct MyStruct *ptr=&ss;

//声明了一个指向结构对象ss 的指针。它的类型是

//MyStruct *,它指向的类型是MyStruct。

int *pstr=(int*)&ss;

//声明了一个指向结构对象ss 的指针。但是pstr 和

//它被指向的类型ptr 是不同的。

请问怎样通过指针ptr 来访问ss 的三个成员变量?

答案:

ptr->a; //指向运算符,或者可以这们(*ptr).a,建议使用前者

ptr->b;

ptr->c;

又请问怎样通过指针pstr 来访问ss 的三个成员变量?

答案:

*pstr; //访问了ss 的成员a。

*(pstr+1); //访问了ss 的成员b。

*(pstr+2) //访问了ss 的成员c。

虽然我在我的MSVC++6.0 上调式过上述代码,但是要知道,这样使用pstr 来访问结构成员是不正规的,为了说明为什么不正规,让我们看看怎样通过指针来访问数组的各个单元: (将结构体换成数组)

例十三:

int array[3]={35,56,37};

int *pa=array;

//通过指针pa 访问数组array 的三个单元的方法是:

*pa; //访问了第0 号单元

*(pa+1); //访问了第1 号单元

*(pa+2); //访问了第2 号单元

从格式上看倒是与通过指针访问结构成员的不正规方法的格式一样。

所有的C/C++编译器在排列数组的单元时,总是把各个数组单元存放在连续的存储区里,单元和单元之间没有空隙。但在存放结构对象的各个成员时,在某种编译环境下,可能会需要字对齐或双字对齐或者是别的什么对齐,需要在相邻两个成员之间加若干个"填充字节",这就导致各个成员之间可能会有若干个字节的空隙。

所以,在例十二中,即使*pstr 访问到了结构对象ss 的第一个成员变量a,也不能保证*(pstr+1)就一定能访问到结构成员b。因为成员a 和成员b 之间可能会有若干填充字节,说不定*(pstr+1)就正好访问到了这些填充字节呢。这也证明了指针的灵活性。要是你的目的就是想看看各个结构成员之间到底有没有填充字节,嘿,这倒是个不错的方法。

不过指针访问结构成员的正确方法应该是象例十二中使用指针ptr 的方法。

指针和函数的关系

可以把一个指针声明成为一个指向函数的指针。

int fun1(char *,int);

int (*pfun1)(char *,int);

pfun1=fun1;

int a=(*pfun1)("abcdefg",7); //通过函数指针调用函数。

可以把指针作为函数的形参。在函数调用语句中,可以用指针表达式来作为实参。

例十四:

int fun(char *);

inta;

char str[]="abcdefghijklmn";

a=fun(str);

int fun(char *s)

{

    int num=0;

    for(int i=0;;)

    {

        num+=*s;s++;

    }

    return num;

}

这个例子中的函数fun 统计一个字符串中各个字符的ASCII 码值之和。前面说了,数组的名字也是一个指针。在函数调用中,当把str作为实参传递给形参s 后,实际是把str 的值传递给了s,s 所指向的地址就和str 所指向的地址一致,但是str 和s 各自占用各自的存储空间。在函数体内对s 进行自加1 运算,并不意味着同时对str 进行了自加1 运算。

 

指针类型转换

当我们初始化一个指针或给一个指针赋值时,赋值号的左边是一个指针,赋值号的右边是一个指针表达式。在我们前面所举的例子中,绝大多数情况下,指针的类型和指针表达式的类型是一样的,指针所指向的类型和指针表达式所指向的类型是一样的。

例十五:

float f=12.3;

float *fptr=&f;

int *p;

在上面的例子中,假如我们想让指针p 指向实数f,应该怎么办?

是用下面的语句吗?

p=&f;

不对。因为指针p 的类型是int *,它指向的类型是int。表达式&f 的结果是一个指针,指针的类型是float *,它指向的类型是float。

两者不一致,直接赋值的方法是不行的。至少在我的MSVC++6.0 上,对指针的赋值语句要求赋值号两边的类型一致,所指向的类型也一致,其它的编译器上我没试过,大家可以试试。为了实现我们的目的,需要进行"强制类型转换":

p=(int*)&f;

如果有一个指针p,我们需要把它的类型和所指向的类型改为TYEP *TYPE, 那么语法格式是: (TYPE *)p;

这样强制类型转换的结果是一个新指针,该新指针的类型是TYPE *,它指向的类型是TYPE,它指向的地址就是原指针指向的地址。

而原来的指针p 的一切属性都没有被修改。(切记)

一个函数如果使用了指针作为形参,那么在函数调用语句的实参和形参的结合过程中,必须保证类型一致,否则需要强制转换

例十六:

void fun(char*);

int a=125,b;

fun((char*)&a);

void fun(char*s)

{

charc;

c=*(s+3);*(s+3)=*(s+0);*(s+0)=c;

c=*(s+2);*(s+2)=*(s+1);*(s+1)=c;

}

注意这是一个32 位程序,故int 类型占了四个字节,char 类型占一个字节。函数fun 的作用是把一个整数的四个字节的顺序来个颠倒。注意到了吗?在函数调用语句中,实参&a 的结果是一个指针,它的类型是int *,它指向的类型是int。形参这个指针的类型是char *,它指向的类型是char。这样,在实参和形参的结合过程中,我们必须进行一次从int *类型到char *类型的转换。

结合这个例子,我们可以这样来

想象编译器进行转换的过程:编译器先构造一个临时指针char *temp,然后执行temp=(char *)&a,最后再把temp 的值传递给s。所以最后的结果是:s 的类型是char *,它指向的类型是char,它指向的地址就是a 的首地址。

我们已经知道,指针的值就是指针指向的地址,在32 位程序中,指针的值其实是一个32 位整数。

那可不可以把一个整数当作指针的值直接赋给指针呢?就象下面的语句:

unsigned int a;

TYPE *ptr; //TYPE 是int,char 或结构类型等等类型。

a=20345686;

ptr=20345686; //我们的目的是要使指针ptr 指向地址20345686

ptr=a; //我们的目的是要使指针ptr 指向地址20345686

//编译一下吧。结果发现后面两条语句全是错的。那么我们的目的就不能达到了吗?不,还有办法:

unsigned int a;

TYPE *ptr; //TYPE 是int,char 或结构类型等等类型。

a=N //N 必须代表一个合法的地址;

ptr=(TYPE*)a; //呵呵,这就可以了。

严格说来这里的(TYPE *)和指针类型转换中的(TYPE *)还不一样。这里的(TYPE*)的意思是把无符号整数a 的值当作一个地址来看待。上面强调了a 的值必须代表一个合法的地址,否则的话,在你使用ptr 的时候,就会出现非法操作错误。想想能不能反过来,把指针指向的地址即指针的值当作一个整数取出来。完全可以。下面的例子演示了把一个指针的值当作一个整数取出来,然后再把这个整数当作一个地址赋给一个指针:

例十七:

int a=123,b;

int *ptr=&a;

char *str;

b=(int)ptr; //把指针ptr 的值当作一个整数取出来。

str=(char*)b; //把这个整数的值当作一个地址赋给指针str。

现在我们已经知道了,可以把指针的值当作一个整数取出来,也可以把一个整数值当作地址赋给一个指针。

指针的安全问题

看下面的例子:

例十八:

char s='a';

int *ptr;

ptr=(int *)&s;

*ptr=1298;

指针ptr 是一个int *类型的指针,它指向的类型是int。它指向的地址就是s 的首地址。在32 位程序中,s 占一个字节,int 类型占四个字节。最后一条语句不但改变了s 所占的一个字节,还把和s 相临的高地址方向的三个字节也改变了。这三个字节是干什么的?只有编译程序知道,而写程序的人是不太可能知道的。也许这三个字节里存储了非常重要的数据,也许这三个字节里正好是程序的一条代码,而由于你对指针的马虎应用,这三个字节的值被改变了!这会造成崩溃性的错误。

让我们再来看一例:

例十九:

char a;

int *ptr=&a;

ptr++;

*ptr=115;

该例子完全可以通过编译,并能执行。但是看到没有?第3 句对指针ptr 进行自加1 运算后,ptr 指向了和整形变量a 相邻的高地址方向的一块存储区。这块存储区里是什么?我们不知道。有可能它是一个非常重要的数据,甚至可能是一条代码。

而第4 句竟然往这片存储区里写入一个数据!这是严重的错误。所以在使用指针时,程序员心里必须非常清楚:我的指针究竟指向了哪里。在用指针访问数组的时候,也要注意不要超出数组的低端和高端界限,否则也会造成类似的错误。在指针的强制类型转换:ptr1=(TYPE *)ptr2 中,如果sizeof(ptr2的类型)大于sizeof(ptr1 的类型),那么在使用指针ptr1 来访问ptr2所指向的存储区时是安全的。如果sizeof(ptr2 的类型) 小于sizeof(ptr1 的类型),那么在使用指针ptr1 来访问ptr2 所指向的存储区时是不安全的。至于为什么,读者结合例十八来想一想,应该会明白的。

第八章典型例题:

调试示例error11_1.cpp (指针数组、内存动态分配)  

程序填空,不要改变与输入输出有关的语句。

输入若干有关颜色的英文单词,以#作为输入结束标志,对这些单词升序排列后输出。其中颜色的英文单词数数小于20个,颜色的英文单词长度均不超过10个字符。

输入输出示例:括号内为说明

输入:

red

blue

yellow

green

purple

#

输出:

blue  green  purple  red  yellow  

#include <stdio.h>

#include<stdlib.h>

#include<string.h>

void main()

{

    int i,j, index, n = 0;

    char *color[20], str[10], *temp;

   

    scanf("%s", str);

    while(str[0] != '#') {

        color[n] = (char *)malloc(sizeof(char)*(strlen(str)+1));  

        strcpy(color[n], str);

   n++;

        scanf("%s", str);

    }

/*---------*/   

for (i =0; i<n-1; i++) {

index = i;

for (j = i+1; j<n; j++)

if (strcmp(color[j],color[index]) < 0) index = j;

strcpy(str, color[i]);

strcpy(color[i], color[index]);

strcpy(color[index], str);

}

    for(i = 0; i < n; i++)

        printf("%s  ", color[i]);

    printf("\n");

}

10022    编程题(指针数组)

输入一个正整数repeat (0<repeat<10),做repeat次下列运算:

编写程序,输入一个月份,输出对应的英文名称,要求用指针数组表示12个月的英文名称。

若输入月份错误,输出提示信息。

输入输出示例:括号内为说明

输入:

3 (repeat=3)

5

9

14

输出:

May

September

Wrong input!

#include<stdio.h>

void main()

{

int ri,repeat;

int month;

char *month_name[]={"","January","February","March","April","May","June","July","August","September","October","November","December"};

scanf("%d",&repeat);

for(ri=1;ri<=repeat;ri++){

scanf("%d",&month);

if((month>=1)&&(month<=12))

puts(month_name[month]);

else

printf("Wrong input!");

}

}

10023    编程题 (指针数组,查找相同的字符串)

程序填空,不要改变与输入输出有关的语句。

输入一个正整数repeat (0<repeat<10),做repeat次下列运算:

定义一个指针数组将下表的星期信息组织起来,输入一个字符串,在表中查找,若存在,输出该字符串在表中的序号,否则输出-1。

(表格详见实验教材P99)

输入输出示例:括号内为说明

输入:

3 (repeat=3)

Tuesday

Wednesday

year

输出:

3

4

-1

#include<stdio.h>

#include<string.h>

void main()

{

int i,ri,repeat,dateNum;

char *date[]={"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"};

char str[80];

scanf("%d",&repeat);

getchar();

for(ri=1;ri<=repeat;ri ++){

scanf("%s",str);

/*---------*/

dateNum=sizeof(date)/sizeof(char *);

for (i = 0; i<dateNum; i++){

if (strcmp(date[i],str) == 0) {

printf("%d\n",i+1); break;

}

}

if (i==dateNum) printf("%d\n",-1);

}

}

第八章目录回顾

8.1指针是什么217

8.2指针变量219

8.2.1使用指针变量的例子219

8.2.2怎样定义指针变量220

8.2.3怎样引用指针变量222

8.2.4指针变量作为函数参数224

8.3通过指针引用数组229

8.3.1数组元素的指针229

8.3.2在引用数组元素时指针的运算229

8.3.3通过指针引用数组元素231

8.3.4用数组名作函数参数236

*8.3.5通过指针引用多维数组243

8.4通过指针引用字符串254

8.4.1字符串的引用方式254

8.4.2字符指针作函数参数258

8.4.3使用字符指针变量和字符数组的比较262

*8.5指向函数的指针265

8.5.1什么是函数的指针265

8.5.2用函数指针变量调用函数265

*8.5.3怎样定义和使用指向函数的指针变量267

*8.5.4用指向函数的指针作函数参数269

*8.6返回指针值的函数273

*8.7指针数组和多重指针276

8.7.1什么是指针数组276

8.7.2指向指针数据的指针变量279

8.7.3指针数组作main函数的形参281

*8.8动态内存分配与指向它的指针变量284

8.8.1什么是内存的动态分配284

8.8.2怎样建立内存的动态分配284

8.8.3void指针类型286

8.9有关指针的小结288

第9章用户自己建立数据类型

书上典型例题总结:

如何去定义一个结构体数组:

#include<stdio.h>

#include<string.h>

struct Person

{

    char name[20];

    int count;

}leader[3]={"Li",0,"Zhang",0,"Sun",0};

int main()

{

    int i,j;

    char leader_name[20];

    for (i=1;i<=10;i++)

    {

        scanf("%s",leader_name);

        for(j=0;i<3;i++)

        if(strcmp(leader_name,leader[j].name)==0)leader[j].count++;

    

    }

printf("\nResult:\n");

for(i=0;i<3;i++)

    printf("%5s:%d\n",leader[i].name,leader[i].count);

return 0;

}

指向结构体变量的指针:

#include<stdio.h>

#include<string.h>

int main()

{

    struct Student

    {

        long num;

        char name[20];

        char sex;

        float score;

    };

struct Student_stu_1;

struct Student*p;

p=stu&_1;            //p指向stu_1

stu_1.num=10101;

strcpy(stu_1.name,"Li Lin");

stu_1.sex='M';

stu_1.score=89.5;

printf("No.:%ld\nname:%s\nsex:%c\nscore:%5.1f\n");

        stu_1.num,stu_1.name,stu_1.sex,stu_1.score);

printf("No.:%ld\nname:%s\nsex:%c\nscore:%5.1f\n");

       (*p).num,(*p).name,(*p).sex,(*p).score);    //(*p)表示p指向的结构体变量,(*p).num表示p所指向的结构体变量中的成员num。注意*两侧的括号不可省,因为成员运算符“.”优先于“*”运算符

return 0;

}

如果p指向一个结构体变量stu,以下三种用法等价

(1)stu.成员名(如stu.num);

(2)(*p).成员名(如(*p).num);

(3)p->成员名(如p->num)。

306页例9.7:

学生成绩管理系统

#include<stdio.h>

#include<stdlib.h>

#include<stdlib.h>

#include<string.h>

struct Student{   //每个学生对应一个结构体

char ID[20];//学号

char Name[10];//姓名

float Mark1;//语文成绩

float Mark2;//数学成绩 //四个变量

float Mark3;//英语成绩

float Mark4;//计算机成绩

float All; //总分

float Average;//平均成绩

}students[1000];

int num=0; //计数器

void Copy(struct Student *arr,int i,int j)

{

strcpy(arr[i].ID,arr[j].ID); //strcpy()函数的简介:

是将一个字符串复制到另一块空间地址中 的函数,‘\0’是停止拷贝的终止条件,

也复制到目标空间。下面是库中的strcpy()函数声明:

strcpy(arr[i].Name,arr[j].Name);

arr[i].Mark1 = arr[j].Mark1;

arr[i].Mark2 = arr[j].Mark2;

arr[i].Mark3 = arr[j].Mark3;

arr[i].Mark4 = arr[j].Mark4;

arr[i].All = arr[j].All;

arr[i].Average = arr[j].Average;

}

int Student_SearchByName(char name[])//通过姓名来检索学生

{

     int i;

     for (i=0;i<num;i++)

     {

         if (strcmp(students[i].Name,name)==0)  //通过strcmp函数来对比学生姓名,找到返回在数组的位置

         {

              return i;

       }

     }

     return -1;    //未找到返回 -1

}

int Student_SearchByIndex(char id[])//通过学号来检索学生信息

{

     int i;

     for (i=0;i<num;i++)

     {

         if (strcmp(students[i].ID,id)==0)  //通过strcmp函数来对比学生id,找到返回位置

         {

              return i;

         }  

     }

     return -1;   //未找到返回 -1

}

void Student_DisplaySingle(int index)//输出表头

{

     printf("%10s%10s%8s%8s%8s%10s\n","学号","姓名","语文","数学","英语","计算机","总成绩","平均成绩");

     printf("-------------------------------------------------------------\n");

     printf("%10s%10s%8.2f%8.2f%8.2f%8.2f%10.2f%10.2f\n",students[index].ID,students[index].Name,

              students[index].Mark1,students[index].Mark2,students[index].Mark3,students[index].Mark4,students[index].All,students[index].Average);

}

void inputt()//利用循环录入学生信息

{

     while(1)

     {

         printf("请输入学号:");

         scanf("%s",&students[num].ID);

         getchar();

         printf("请输入姓名:");

         scanf("%s",&students[num].Name);

         getchar();

         printf("请输入成绩:");

         scanf("%f",&students[num].Mark1);

         getchar();

         printf("请输入成绩:");

         scanf("%f",&students[num].Mark2);

         getchar();

         printf("请输入成绩:");

         scanf("%f",&students[num].Mark3);

         getchar();

         printf("请输入成绩:");

         scanf("%f",&students[num].Mark4);   //依次输入各项数据

         getchar();

         students[num].All=students[num].Mark1+students[num].Mark2+students[num].Mark3+students[num].Mark4;    //输完数据后自动计算总成绩与平均成绩

         students[num].Average=(students[num].Mark1+students[num].Mark2+students[num].Mark3+students[num].Mark4)/4;

         if(Student_SearchByIndex(students[num].ID) == -1)

         {

          num++;  //移向下一个位置

}

else

{

printf("学号重复,输入数据无效 !!!\n");

}

  printf("是否继续?(y/n)");

         if (getchar()=='n')

         {

              break;

         }

     }

}

void modify()//修改成绩

{

 while(1)

     {

         char id[20];

         int index;

         printf("请输入要修改的学生的学号:");

         scanf("%s",&id);

         getchar();

         index=Student_SearchByIndex(id);  //调用搜查id函数,根据其返回值确定位置

         if (index==-1)

         {

              printf("学生不存在!\n");

         }

         else

         {

              printf("你要修改的学生信息为:\n");

              Student_DisplaySingle(index);

              printf("-- 请输入新值--\n");

              printf("请输入学号:");

              scanf("%s",&students[index].ID);

              getchar();

              printf("请输入姓名:");

              scanf("%s",&students[index].Name);

              getchar();

              printf("请输入语文成绩:");

              scanf("%f",&students[index].Mark1);

   getchar();

              printf("请输入数学成绩:");

              scanf("%f",&students[index].Mark2);

              getchar();

              printf("请输入英语成绩:");

              scanf("%f",&students[index].Mark3);

              getchar();

              printf("请输入计算机成绩:");

              scanf("%f",&students[index].Mark4);  //重新录入一套新的数据替代

              getchar();

              students[index].All=students[index].Mark1+students[index].Mark2+students[index].Mark3+students[index].Mark4;

          students[index].Average=(students[index].Mark1+students[index].Mark2+students[index].Mark3+students[index].Mark4)/4;   

         }

         printf("是否继续?(y/n)");

         if (getchar()=='n')

         {

              break;

         }

     }

}

void deletee()//删除学生信息

{

 int i;

     while(1)

     {

         char id[20];

         int index;

         printf("请输入要删除的学生的学号:");

         scanf("%s",&id);

         getchar();

         index=Student_SearchByIndex(id);   //调用搜查id函数,根据其返回值确定位置

         if (index==-1)

         {

              printf("学生不存在!\n");

         }

         else

         {

              printf("你要删除的学生信息为:\n");

              Student_DisplaySingle(index);

              printf("是否真的要删除?(y/n)");

              if (getchar()=='y')

              {

                   for (i=index;i<num-1;i++)

                   {

                      Copy(students,i,i+1);

                       //students[i]=students[i+1];    //把后边的对象都向前移动

                   }

                   num--;

              }

             getchar();

         }

         printf("是否继续?(y/n)");

         if (getchar()=='n')

         {

              break;

         }

     }

}

void display()//打印已录入的学生信息

{

int a;

     printf("%10s%10s%8s%8s%8s%8s%10s%10s\n","学号","姓名","语文","数学","英语","计算机","总成绩","平均成绩");

     printf("-------------------------------------------------------------\n");

     for (a=0;a<num;a++)

     {

         printf("%10s%10s%8.2f%8.2f%8.2f%8.2f%10.2f%10.2f\n",students[a].ID,students[a].Name,

              students[a].Mark1,students[a].Mark2,students[a].Mark3,students[a].Mark4,students[a].All,students[a].Average);

     }

}

void insert()//指定位置插入学生信息

{

int a,b,c;

printf("请输入你要插入的位置");

scanf("%d",&a);

if(a>num) {

printf("输入的位置有误,请重新输入,当前共%d条数据\n",num);

scanf("%d",&a);}

      b=num-1;

      for(;b>=a-1;b--)

      {

      //strcpy(students[b+1].ID,students[b].ID);

      //strcpy(students[b+1].Name,students[b].Name);

      //students[b+1].Mark1=students[b].Mark1;

      //students[b+1].Mark2=students[b].Mark2;

      //students[b+1].Mark3=students[b].Mark3;

      //students[b+1].Mark4=students[b].Mark4;

      //students[b+1].All=students[b].All;

      //students[b+1].Average=students[b].Average;  

Copy(students,b+1,b); //根据其输入的位置,将其及以后的数据向后移动一个位置

     

}

num++;

         printf("请输入学号:");

         scanf("%s",&students[a-1].ID);

         getchar();

         printf("请输入姓名:");

         scanf("%s",&students[a-1].Name);

         getchar();

         printf("请输入语文成绩:");

         scanf("%f",&students[a-1].Mark1);

         getchar();

         printf("请输入数学成绩:");

         scanf("%f",&students[a-1].Mark2);

         getchar();

         printf("请输入英语成绩:");

         scanf("%f",&students[a-1].Mark3);

         getchar();

         printf("请输入计算机成绩:");

         scanf("%f",&students[a-1].Mark4);  //输入新数据

         getchar();  

         students[a-1].All=students[a-1].Mark1+students[a-1].Mark2+students[a-1].Mark3+students[a-1].Mark4;

         students[a-1].Average=(students[a-1].Mark1+students[a-1].Mark2+students[a-1].Mark3+students[a-1].Mark4)/4;

}

void search()//查询学生信息

{

while(1)

     {

         char name[20];

         int index;

         printf("请输入要查询的学生的姓名:");

         scanf("%s",&name);

         getchar();

         index=Student_SearchByName(name);   //调用搜查name函数,根据其返回值确定位置

         if (index==-1)

         {

              printf("学生不存在!\n");

         }

         else

         {

              printf("你要查询的学生信息为:\n");

              Student_DisplaySingle(index);

         }

         printf("是否继续?(y/n)");

         if (getchar()=='n')

         {

              break;

         }

     }

}

voidsort()//根据平均分排序

(此时注意按照题目要求应该排序两个)

{

int i,j;

//struct students tmp;

     for (i=0;i<num;i++)

     {

      students[i].Average=(students[i].Mark1+students[i].Mark2+students[i].Mark3+students[i].Mark4)/4;

     }

     for (i=0;i<num;i++)

     {

         for (j=1;j<num-i;j++)

         {

              if (students[j-1].Average<students[j].Average)

              {

                  

  Copy(students,num,j-1);

  Copy(students,j-1,j);

  Copy(students,j,num);

  //tmp=students[j-1];

                   //students[j-1]=students[j];

                   //students[j]=tmp;      //冒泡排序

            }  

         }

     }

     int a;

     printf("%10s%10s%8s%8s%8s%10s\n","学号","姓名","语文","数学","英语","计算机","总成绩","平均成绩");

     printf("-------------------------------------------------------------\n");

     for (a=0;a<num;a++)

     {

         printf("%10s%10s%8.2f%8.2f%8.2f%8.2f%10.2f%10.2f\n",students[a].ID,students[a].Name,

              students[a].Mark1,students[a].Mark2,students[a].Mark3,students[a].Mark4,students[a].All,students[a].Average);

     }

}

void SearchLow()//搜索不及格的并输出

{

int a;

printf("           语文不及格的有%10s%10s%8s\n","学号","姓名","语文");

for(a=0;a<num;a++)

{

if(students[a].Mark1<60)

printf("%10s%10s%8.2f\n",students[a].Name,students[a].ID,students[a].Mark1);    //从头搜索到尾,若小于60就输出

}

printf("           数学不及格的有%10s%10s%8s\n","学号","姓名","数学");

for(a=0;a<num;a++)

{

if(students[a].Mark2<60)

printf("%10s%10s%8.2f\n",students[a].Name,students[a].ID,students[a].Mark2);

}

printf("           英语不及格的有%10s%10s%8s\n","学号","姓名","英语");

for(a=0;a<num;a++)

{

if(students[a].Mark3<60)

printf("%10s%10s%8.2f\n",students[a].Name,students[a].ID,students[a].Mark3);

}

printf("           计算机不及格的有%10s%10s%8s\n","学号","姓名","计算机");

for(a=0;a<num;a++)

{

if(students[a].Mark4<60)

printf("%10s%10s%8.2f\n",students[a].Name,students[a].ID,students[a].Mark4);

}

system("pause"); //这个好像没作用

}

void SearchHigh()//搜索成绩最高者输出

{

int a;

int max ;

printf("           语文最高分为%10s%10s%8s\n","学号","姓名","语文");

max=students[0].Mark1;

for(a=1;a<num;a++)

{

if(students[a].Mark1>max)

max=students[a].Mark1;

}

for(a=0;a<num;a++)

{

if(max==students[a].Mark1)

printf("%10s%10s%8.2f\n",students[a].Name,students[a].ID,students[a].Mark1);

}

printf("           数学最高分为%10s%10s%8s\n","学号","姓名","数学");

max=students[0].Mark2;

for(a=1;a<num;a++)

{

if(students[a].Mark2>max)

max=students[a].Mark2;

}

for(a=0;a<num;a++)

{

if(max==students[a].Mark2)

printf("%10s%10s%8.2f\n",students[a].Name,students[a].ID,students[a].Mark2);

}

printf("           英语最高分为%10s%10s%8s\n","学号","姓名","英语");

max=students[0].Mark3;

for(a=1;a<num;a++)

{

if(students[a].Mark3>max)

max=students[a].Mark3;

}

for(a=0;a<num;a++)

{

if(max==students[a].Mark3)

printf("           %10s%10s%8.2f\n",students[a].Name,students[a].ID,students[a].Mark3);

}

printf("           计算机最高分为%10s%10s%8s\n","学号","姓名","计算机");

max=students[0].Mark4;

for(a=1;a<num;a++)

{

if(students[a].Mark4>max)

max=students[a].Mark4;

}

for(a=0;a<num;a++)

{

if(max==students[a].Mark4)

printf("           %10s%10s%8.2f\n",students[a].Name,students[a].ID,students[a].Mark4);

}

system("pause");

}

void Save()

{

FILE*fp = fopen("temp.txt","w+");

fprintf(fp,"%d\n",num);

for(int i = 0 ; i< num ;i++)

{

fprintf(fp,"%s %s %f %f %f %f %f %f\n",students[i].ID,students[i].Name,students[i].Mark1,students[i].Mark2,students[i].Mark3,students[i].Mark4,students[i].All,students[i].Average);

}

fclose(fp);

}

void Load()

{

FILE*fp = fopen("temp.txt","r");

fscanf(fp,"%d",&num);

for(int i = 0 ; i< num ;i++)

{

fscanf(fp,"%s %s %f %f %f %f %f %f\n",students[i].ID,students[i].Name,&students[i].Mark1,&students[i].Mark2,&students[i].Mark3,&students[i].Mark4,&students[i].All,&students[i].Average);

}

fclose(fp);

}

/*主程序*/

int main(){

int i;

while(1){

Load();

printf("\t\t\t\t\t-------- 学生成绩管理系统-------\n\n\n\n");     //菜单

         printf("\t\t\t\t\t1. 增加学生记录\n\n");

         printf("\t\t\t\t\t2. 修改学生记录\n\n");

         printf("\t\t\t\t\t3. 删除学生记录\n\n");

         printf("\t\t\t\t\t4. 插入学生记录\n\n");

         printf("\t\t\t\t\t5. 显示所有记录\n\n");

         printf("\t\t\t\t\t6. 查询学生记录\n\n");

         printf("\t\t\t\t\t7. 按平均成绩排序\n\n");

         printf("\t\t\t\t\t8. 输出各科目不及格学生\n\n");

         printf("\t\t\t\t\t9. 输出各科目最高分\n\n");

         printf("\t\t\t\t\t0. 退出\n\n\n");

         printf("请选择(0-9):");

  scanf("%d",&i);

switch(i){

case 1:inputt();break;

case 2:modify();break;

case 3:deletee();break;

case 4:insert();break;

case 5:display();break;

case 6:search();break;

case 7:sort();break;

case 8:SearchLow();break;

case 9:SearchHigh();break;

case 0:exit(0);

default:  ;

}

Save();

}

return 0;

}

void Save() {

FILE *fp = fopen("temp.txt", "w+");

fprintf(fp, "%d\n", num);

for (int i = 0 ; i < num ; i++) {

fprintf(fp, "%s %s %f %f %f %f %f %f\n", students[i].ID, students[i].Name, students[i].Mark1, students[i].Mark2,

       students[i].Mark3, students[i].Average1);

}

fclose(fp);

}

void Load() {

FILE *fp = fopen("temp.txt", "r");

fscanf(fp, "%d", &num);

for (int i = 0 ; i < num ; i++) {

fscanf(fp, "%s %s %f %f %f %f %f %f\n", students[i].ID, students[i].Name, &students[i].Mark1, &students[i].Mark2,

      &students[i].Mark3, &students[i].Average1);

}

fclose(fp);

调试示例error09_1.cpp(结构)

输入一个正整数n(3≤n≤10),再输入n个雇员的信息,包括姓名、基本工资、浮动工资和支出,输出每人的姓名和实发工资,实发工资=基本工资+浮动工资-支出。

输入输出示例:括号内为说明

输入:

3 (n=3)

zhao 240 400 75

qian 360 120 50

zhou 560 0 80

输出: 

 zhao 实发工资: 565.00

 qian 实发工资: 430.00

 zhou 实发工资: 480.00

#include <stdio.h>

int main (void)

{

    int i, n;

      struct emp{

        char  name[10];

        double jbg;

        double fdg;

        double zc;

    } s[10];

    scanf("%d", &n);

for(i=0;i<n;i++)

scanf("%s%lf%lf%lf",s[i].name, &s[i].jbg,&s[i].fdg,&s[i].zc);

    for (i = 0; i < n; i++)

        printf ("%5s 实发工资:%7.2f\n", s[i].name, s[i].jbg + s[i].fdg - s[i].zc);

}

90002    时间换算(结构)

输入一个正整数 repeat (0<repeat<10),做 repeat 次下列运算:

输入一个时间数值,再输入秒数 n,输出该时间再过 n 秒后的时间值,时间的表示形式为时:分:秒,超过 24 时从 0 时重新开始计时。

输入输出示例:括号内为说明

输入:

3   (repeat=3)

0:0:1

59   (秒数n=59)

11:59:40   

30   (秒数n=30)

23:59:40   

301   (秒数n=301)

输出:

time: 0:1:0   (0:0:01加上59秒的新时间)   

time: 12:0:10   (11:59:40加上30秒的新时间)

time: 0:4:41   (23:59:40加上301秒的新时间)

#include <stdio.h>

int main(void)

{

    int n;

    int repeat, ri;

    struct time{

        int hour, minute, second;

    }time;

    scanf("%d", &repeat);

    for(ri = 1; ri <= repeat; ri++){

        scanf("%d:%d:%d", &time.hour, &time.minute, &time.second);

        scanf("%d",&n);

time.second=time.second+n;

if(time.second>=60){

time.minute=time.minute+time.second/60;

time.second=time.second%60;

if(time.minute>=60){

time.hour=time.hour+time.minute/60;

time.minute=time.minute%60;

if(time.hour>=24)

time.hour=time.hour-24;

}

}

        printf("time: %d:%d:%d\n", time.hour, time.minute, time.second);

   }

}

90003    计算平均成绩(结构)

输入整数n(n<10),再输入n个学生的基本信息,包括序号、姓名和成绩,要求计算并输出他们的平均成绩(保留2位小数)。

输入输出示例:括号内为说明

输入:

3 (n=3)

1  zhang  70

2  wang  80

3  qian  90

输出:

average: 80.00

#include <stdio.h>

int main(void)

{

    int i, n;

    double average, sum;

    struct student{

        int num;

        char name[10];

        int score;

    }s[10];

    scanf("%d", &n);

sum=0;

for(i=0;i<n;i++){

scanf("%d%s%d",&s[i].num,s[i].name,&s[i].score);

sum+=s[i].score;

}

average=sum/n;

    printf("average: %.2f\n", average);

}

90004    计算两个复数之积(结构)

输入4个整数a1,b1,a2,b2,分别表示两个复数的实部与虚部,求两个复数之积(a1+b1i)*(a2+b2i),乘积的实部为:a1*a2-b1*b2,虚部为:a1*b2+a2*b1。

输入输出示例:括号内为说明

输入:

3 4 5 6

输出:

(3+4i) * (5+6i) = -9 + 38i

#include <stdio.h>

int main(void)

{

    struct complex{

        int real;

        int imag;

    }product, x, y;

    scanf("%d%d%d%d", &x.real, &x.imag, &y.real, &y.imag);

product.real=x.real*y.real-x.imag*y.imag;

product.imag=x.real*y.imag+x.imag*y.real;

    printf("(%d+%di) * (%d+%di) = %d + %di\n", x.real, x.imag, y.real, y.imag, product.real, product.imag);

}

示例问题:创建图书目录 

Gwen Glenn要打印一份图书目录。她想打印每本书的各种信息:书名、 

作者、出版社、版权日期、页数、册数和价格。其中的一些项目(如,书 

名)可以储存在字符数组中,其他项目需要一个int数组或float数组。用 7 个 

不同的数组分别记录每一项比较繁琐,尤其是 Gwen 还想创建多份列表:一 

份按书名排序、一份按作者排序、一份按价格排序等。如果能把图书目录的 

信息都包含在一个数组里更好,其中每个元素包含一本书的相关信息。 

因此,Gwen需要一种即能包含字符串又能包含数字的数据形式,而且 

还要保持各信息的独立。C结构就满足这种情况下的需求。我们通过一个示 

例演示如何创建和使用数组。但是,示例进行了一些限制。第一,该程序示 

例演示的书目只包含书名、作者和价格。第二,只有一本书的数目。当然, 

别忘了这只是进行了限制,我们在后面将扩展该程序。请看程序清单14.1及 

其输出,然后阅读后面的一些要点。 

程序清单14.1 book.c程序 

//* book.c -- 一本书的图书目录 */

#include <stdio.h>

#include <string.h>

char * s_gets(char * st, int n);

#define MAXTITL41 /* 书名的最大长度 + 1 */

#define MAXAUTL31 /* 作者姓名的最大长度 + 1*/

struct book { /* 结构模版:标记是 book */

char title[MAXTITL];

1007char author[MAXAUTL];

float value;

}; /* 结构模版结束 */

int main(void)

{

struct book library; /* 把 library 声明为一个 book 类型的变量 */

printf("Please enter the book title.\n");

s_gets(library.title, MAXTITL); /* 访问title部分*/

printf("Now enter the author.\n");

s_gets(library.author, MAXAUTL);

printf("Now enter the value.\n");

scanf("%f", &library.value);

printf("%s by %s: $%.2f\n", library.title,

library.author, library.value);

printf("%s: \"%s\" ($%.2f)\n", library.author,

library.title, library.value);

printf("Done.\n");

return 0;

}

1008char * s_gets(char * st, int n)

{

char * ret_val;

char * find;

ret_val = fgets(st, n, stdin);

if (ret_val)

{

find = strchr(st, '\n'); // 查找换行符 

if (find) // 如果地址不是 NULL,

*find = '\0'; // 在此处放置一个空字符 

else

while (getchar() != '\n')

continue; //处理输入行中剩余的字符 

}

return ret_val;

}

我们使用前面章节中介绍的s_gets()函数去掉fgets()储存在字符串中的换 

行符。下面是该例的一个运行示例: 

Please enter the book title.

1009Chicken of the Andes

Now enter the author.

Disma Lapoult

Now enter the value.

29.99

Chicken of the Andes by Disma Lapoult: $29.99

Disma Lapoult: "Chicken of the Andes" ($29.99)

Done.

程序清单14.1中创建的结构有3部分,每个部分都称为成员(member) 

或字段(

field)。这3部分中,一部分储存书名,一部分储存作者名,一部 

分储存价格。下面是必须掌握的3个技巧: 

为结构建立一个格式或样式; 

声明一个适合该样式的变量; 

访问结构变量的各个部分

建立结构声明 

结构声明(

structure declaration)描述了一个结构的组织布局。声明类 

似下面这样: 

struct book {

char title[MAXTITL];

char author[MAXAUTL];

float value;

};

该声明描述了一个由两个字符数组和一个float类型变量组成的结构。该 

声明并未创建实际的数据对象,只描述了该对象由什么组成。〔有时,我们 

把结构声明称为模板,因为它勾勒出结构是如何储存数据的。如果读者知道 

C++的模板,此模板非彼模板,C++中的模板更为强大。〕我们来分析一些 

细节。首先是关键字 struct,它表明跟在其后的是一个结构,后面是一个可 

选的标记(该例中是 book),稍后程序中可以使用该标记引用该结构。所 

以,我们在后面的程序中可以这样声明: 

struct book library;

这把library声明为一个使用book结构布局的结构变量。 

在结构声明中,用一对花括号括起来的是结构成员列表。每个成员都用 

自己的声明来描述。例如,title部分是一个内含MAXTITL个元素的char类型 

数组。成员可以是任意一种C的数据类型,甚至可以是其他结构!右花括号 

后面的分号是声明所必需的,表示结构布局定义结束。可以把这个声明放在 

所有函数的外部(如本例所示),也可以放在一个函数定义的内部。如果把 

结构声明置于一个函数的内部,它的标记就只限于该函数内部使用。如果把 

结构声明置于函数的外部,那么该声明之后的所有函数都能使用它的标记。 

1011例如,在程序的另一个函数中,可以这样声明: 

struct book dickens;

这样,该函数便创建了一个结构变量dickens,该变量的结构布局是 

book。 

结构的标记名是可选的。但是以程序示例中的方式建立结构时(在一处 

定义结构布局,在另一处定义实际的结构变量),必须使用标记。我们学完 

如何定义结构变量后,再来看这一点。 

 访问结构成员 

结构类似于一个“超级数组”,这个超级数组中,可以是一个元素为char

类型,下一个元素为forat类型,下一个元素为int数组。可以通过数组下标单 

独访问数组中的各元素,那么,如何访问结构中的成员?使用结构成员运算 

符——点(

.)访问结构中的成员。例如,library.value即访问library的value

部分。可以像使用任何float类型变量那样使用library.value。与此类似,可以 

像使用字符数组那样使用 library.title。因此,程序清单 14.1 中的程序中有 

s_gets(library.title, MAXTITL);和scanf("%f", &library.value);这样的代码。 

本质上,.title、.author和.value的作用相当于book结构的下标。 

注意,虽然library是一个结构,但是library.value是一个float类型的变 

量,可以像使用其他 float 类型变量那样使用它。例如,scanf("%f",...)需要一 

个 float 类型变量的地址,而&library.float正好符合要求。.比&的优先级高, 

因此这个表达式和&(library.float)一样。

指向结构的指针 

喜欢使用指针的人一定很高兴能使用指向结构的指针。至少有 4 个理由 

可以解释为何要使用指向结构的指针。第一,就像指向数组的指针比数组本 

身更容易操控(如,排序问题)一样,指向结构的指针通常比结构本身更容 

易操控。第二,在一些早期的C实现中,结构不能作为参数传递给函数,但 

是可以传递指向结构的指针。第三,即使能传递一个结构,传递指针通常更 

有效率。第四,一些用于表示数据的结构中包含指向其他结构的指针。 

下面的程序(程序清单14.4)演示了如何定义指向结构的指针和如何用 

这样的指针访问结构的成员。 

程序清单14.4 friends.c程序 

/* friends.c -- 使用指向结构的指针 */

#include <stdio.h>

#define LEN 20

struct names {

char first[LEN];

char last[LEN];

};

struct guy {

struct names handle;

char favfood[LEN];

char job[LEN];

1031float income;

};

int main(void)

{

struct guy fellow[2] = {

{ { "Ewen", "Villard" },

"grilled salmon",

"personality coach",

68112.00

},

{ { "Rodney", "Swillbelly" },

"tripe",

"tabloid editor",

432400.00

}

};

struct guy * him; /* 这是一个指向结构的指针 */

printf("address #1: %p #2: %p\n", &fellow[0], &fellow[1]);

him = &fellow[0]; /* 告诉编译器该指针指向何处 */

1032printf("pointer #1: %p #2: %p\n", him, him + 1);

printf("him->income is $%.2f: (*him).income is $%.2f\n",

him->income, (*him).income);

him++; /* 指向下一个结构 */

printf("him->favfood is %s: him->handle.last is %s\n",

him->favfood, him->handle.last);

return 0;

}

该程序的输出如下: 

address #1: 0x7fff5fbff820 #2: 0x7fff5fbff874

pointer #1: 0x7fff5fbff820 #2: 0x7fff5fbff874

him->income is $68112.00: (*him).income is $68112.00

him->favfood is tripe: him->handle.last is Swillbelly

关键概念 

我们在编程中要表示的信息通常不只是一个数字或一些列数字。程序可 

能要处理具有多种属性的实体。例如,通过姓名、地址、电话号码和其他信 

息表示一名客户;或者,通过电影名、发行人、播放时长、售价等表示一部 

电影DVD。C结构可以把这些信息都放在一个单元内。在组织程序时这很重 

要,因为这样可以把相关的信息都储存在一处,而不是分散储存在多个变量 

中。 

设计结构时,开发一个与之配套的函数包通常很有用。例如,写一个以 

结构(或结构的地址)为参数的函数打印结构内容,比用一堆printf()语句强 

得多。因为只需要一个参数就能打印结构中的所有信息。如果把信息放到零 

散的变量中,每个部分都需要一个参数。另外,如果要在结构中增加一个成 

员,只需重写函数,不必改写函数调用。这在修改结构时很方便。 

联合声明与结构声明类似。但是,联合的成员共享相同的存储空间,而 

且在联合中同一时间内只能有一个成员。实质上,可以在联合变量中储存一 

个类型不唯一的值。 

enum 工具提供一种定义符号常量的方法,typedef 工具提供一种为基本 

或派生类型创建新标识符的方法。 

指向函数的指针提供一种告诉函数应使用哪一个函数的方法。 

本章小结 

C 结构提供在相同的数据对象中储存多个不同类型数据项的方法。可以 

使用标记来标识一个具体的结构模板,并声明该类型的变量。通过成员点运 

算符(.)可以使用结构模版中的标签来访问结构的各个成员。 

如果有一个指向结构的指针,可以用该指针和间接成员运算符(->)代 

替结构名和点运算符来访问结构的各成员。和数组不同,结构名不是结构的 

地址,要在结构名前使用&运算符才能获得结构的地址。 

一贯以来,与结构相关的函数都使用指向结构的指针作为参数。现在的 

C允许把结构作为参数传递,作为返回值和同类型结构之间赋值。然而,传 

递结构的地址通常更有效。 

联合使用与结构相同的语法。然而,联合的成员共享一个共同的存储空 

间。联合同一时间内只能储存一个单独的数据项,不像结构那样同时储存多 

种数据类型。也就是说,结构可以同时储存一个int类型数据、一个double类 

型数据和一个char类型数据,而相应的联合只能保存一个int类型数据,或者 

一个double类型数据,或者一个char类型数据。 

通过枚举可以创建一系列代表整型常量(枚举常量)的符号和定义相关 

联的枚举类型。 

typedef工具可用于建立C标准类型的别名或缩写。 

函数名代表函数的地址,可以把函数的地址作为参数传递给其他函数, 

然后这些函数就可以使用被指向的函数。如果把特定函数的地址赋给一个名 

为pf的函数指针,可以通过以下两种方式调用该函数: 

#include <math.h> /* 提供sin()函数的原型:double sin(double) */

...

double (*pdf)(double);

1121double x;

pdf = sin;

x = (*pdf)(1.2); // 调用sin(1.2)

x = pdf(1.2); // 同样调用 sin(1.2)

第九章目录:

9.1定义和使用结构体变量293

9.1.1自己建立结构体类型293

9.1.2定义结构体类型变量295

9.1.3结构体变量的初始化和引用296

9.2使用结构体数组300

9.2.1定义结构体数组300

9.2.2结构体数组的应用举例301

9.3结构体指针303

9.3.1指向结构体变量的指针303

9.3.2指向结构体数组的指针304

9.3.3用结构体变量和结构体变量的指针作函数参数306

9.6使用枚举类型322

*9.7用typedef声明新类型名326

第九章课后题答案:

 C语言程序设计第五版谭浩强课后答案 第九章习题答案_月已满西楼的博客-CSDN博客_在第三题的基础上,编写一个函数input

第10章对文件的输入输出

 C语言文件操作(含详细步骤)_zjruiiiiii的博客-CSDN博客_c文件操作

与文件进行通信 

有时,需要程序从文件中读取信息或把信息写入文件。这种程序与文件 

交互的形式就是文件重定向。这种方法很简单,但是有一定限制。例如,假设要编写一个交互程序,询问用户书名并把完整的书名列表保存在文件中。如果使用重定向,应该类似于: 

books > bklist

用户的输入被重定向到 bklist 中。这样做不仅会把不符合要求的文本写 

入 bklist,而且用户也看不到要回答什么问题。 

C提供了更强大的文件通信方法,可以在程序中打开文件,然后使用特 

殊的I/O函数读取文件中的信息或把信息写入文件。在研究这些方法之前, 

先简要介绍一下文件的性质。 

文件是什么 

文件(file)通常是在磁盘或固态硬盘上的一段已命名的存储区。对我 

们而言,stdio.h就是一个文件的名称,该文件中包含一些有用的信息。然 

而,对操作系统而言,文件更复杂一些。例如,大型文件会被分开储存,或 

者包含一些额外的数据,方便操作系统确定文件的种类。然而,这都是操作 

系统所关心的,程序员关心的是C程序如何处理文件(除非你正在编写操作 

系统)。 

C把文件看作是一系列连续的字节,每个字节都能被单独读取。这与UNIX环境中(C的发源地)的文件结构相对应。由于其他环境中可能无法完 

全对应这个模型,C提供两种文件模式:文本模式和二进制模式。 

文本模式和二进制模式 

首先,要区分文本内容和二进制内容、文本文件格式和二进制文件格 

式,以及文件的文本模式和二进制模式。 

948所有文件的内容都以二进制形式(0或1)储存。但是,如果文件最初使 

用二进制编码的字符(例如, ASCII或Unicode)表示文本(就像C字符串那 

样),该文件就是文本文件,其中包含文本内容。如果文件中的二进制值代 

表机器语言代码或数值数据(使用相同的内部表示,假设,用于long或 

double类型的值)或图片或音乐编码,该文件就是二进制文件,其中包含二 

进制内容。 

UNIX用同一种文件格式处理文本文件和二进制文件的内容。不奇怪, 

鉴于C是作为开发UNIX的工具而创建的,C和UNIX在文本中都使用\n(换行 

符)表示换行。UNIX目录中有一个统计文件大小的计数,程序可使用该计 

数确定是否读到文件结尾。然而,其他系统在此之前已经有其他方法处理文 

件,专门用于保存文本。也就是说,其他系统已经有一种与UNIX模型不同 

的格式处理文本文件。例如,以前的OS X Macintosh文件用\r (回车符)表 

示新的一行。早期的MS-DOS文件用\r\n组合表示新的一行,用嵌入的Ctrl+Z

字符表示文件结尾,即使实际文件用添加空字符的方法使其总大小是256的 

倍数(在Windows中,Notepad仍然生成MS-DOS格式的文本文件,但是新的 

编辑器可能使用类UNIX格式居多)。其他系统可能保持文本文件中的每一 

行长度相同,如有必要,用空字符填充每一行,使其长度保持一致。或者, 

系统可能在每行的开始标出每行的长度。 

为了规范文本文件的处理,C 提供两种访问文件的途径:二进制模式和 

文本模式。在二进制模式中,程序可以访问文件的每个字节。而在文本模式 

中,程序所见的内容和文件的实际内容不同。程序以文本模式读取文件时, 

把本地环境表示的行末尾或文件结尾映射为C模式。例如,C程序在旧式 

Macintosh中以文本模式读取文件时,把文件中的\r转换成\n;以文本模式写 

入文件时,把\n转换成\r。或者,C文本模式程序在MS-DOS平台读取文件 

时,把\r\n转换成\n;写入文件时,把\n转换成\r\n。在其他环境中编写的文本 

模式程序也会做类似的转换。 

除了以文本模式读写文本文件,还能以二进制模式读写文本文件。如果 

读写一个旧式MS-DOS文本文件,程序会看到文件中的\r 和\n 字符,不会发 

生映射(图 13.1 演示了一些文本)。如果要编写旧式 Mac格式、MS-DOS格 

949式或UNIX/Linux格式的文件模式程序,应该使用二进制模式,这样程序才能 

确定实际的文件内容并执行相应的动作。 

虽然C提供了二进制模式和文本模式,但是这两种模式的实现可以相 

同。前面提到过,因为UNIX使用一种文件格式,这两种模式对于UNIX实现 

而言完全相同。Linux也是如此

关键概念 

C程序把输入看作是字节流,输入流来源于文件、输入设备(如键 

盘),或者甚至是另一个程序的输出。类似地,C程序把输出也看作是字节 

流,输出流的目的地可以是文件、视频显示等。 

C 如何解释输入流或输出流取决于所使用的输入/输出函数。程序可以 

不做任何改动地读取和存储字节,或者把字节依次解释成字符,随后可以把 

这些字符解释成普通文本以用文本表示数字。类似地,对于输出,所使用的 

函数决定了二进制值是被原样转移,还是被转换成文本或以文本表示数字。 

如果要在不损失精度的前提下保存或恢复数值数据,请使用二进制模式以及 

fread()和fwrite()函数。如果打算保存文本信息并创建能在普通文本编辑器查 

看的文本,请使用文本模式和函数(如getc()和fprintf())。 

要访问文件,必须创建文件指针(类型是FILE *)并把指针与特定文件 

名相关联。随后的代码就可以使用这个指针(而不是文件名)来处理该文 

件。 

要重点理解C如何处理文件结尾。通常,用于读取文件的程序使用一个 

循环读取输入,直至到达文件结尾。C 输入函数在读过文件结尾后才会检测 

到文件结尾,这意味着应该在尝试读取之后立即判断是否是文件结尾。可以 

使用13.2.4节中“设计范例”中的双文件输入模式。 

本章小结 

对于大多数C程序而言,写入文件和读取文件必不可少。为此,绝大对 

数C实现都提供底层I/O和标准高级I/O。因为ANSI C库考虑到可移植性,包 

含了标准I/O包,但是未提供底层I/O。 

标准 I/O 包自动创建输入和输出缓冲区以加快数据传输。fopen()函数为 

标准 I/O 打开一个文件,并创建一个用于存储文件和缓冲区信息的结构。 

fopen()函数返回指向该结构的指针,其他函数可以使用该指针指定待处理的 

文件。feof()和ferror()函数报告I/O操作失败的原因。 

C把输入视为字节流。如果使用fread()函数,C把输入看作是二进制值 

并将其储存在指定存储位置。如果使用fscanf()、getc()、fgets()或其他相关函 

数,C则将每个字节看作是字符码。然后fscanf()和scanf()函数尝试把字符码 

翻译成转换说明指定的其他类型。例如,输入一个值23,%f转换说明会把 

23翻译成一个浮点值,%d转换说明会把23翻译成一个整数值,%s转换说明 

则会把23储存为字符串。getc()和 fgetc()系列函数把输入作为字符码储存, 

将其作为单独的字符保存在字符变量中或作为字符串储存在字符数组中。类 

似地,fwrite()将二进制数据直接放入输出流,而其他输出函数把非字符数 

据转换成用字符表示后才将其放入输出流。 

ANSI C提供两种文件打开模式:二进制和文本。以二进制模式打开文 

件时,可以逐字节读取文件;以文本模式打开文件时,会把文件内容从文本 

的系统表示法映射为C表示法。对于UNIX和Linux系统,这两种模式完全相 

同。 

通常,输入函数getc()、fgets()、fscanf()和fread()都从文件开始处按顺序 

读取文件。然而, fseek()和ftell()函数让程序可以随机访问文件中的任意位 

置。fgetpos()和fsetpos()把类似的功能扩展至更大的文件。与文本模式相 

比,二进制模式更容易进行随机访问。 

第十章课后习题:

C语言程序设计第五版 谭浩强 第五版课后答案_月已满西楼的博客-CSDN博客_c程序设计谭浩强第五版课后答案

第十章目录回顾

10.1C文件的有关基本知识331

10.1.1什么是文件331

10.1.2文件名332

10.1.3文件的分类332

10.1.4文件缓冲区333

10.1.5文件类型指针333

10.2打开与关闭文件334

10.2.1用fopen函数打开数据文件335

10.2.2用fclose函数关闭数据文件337

10.3顺序读写数据文件338

10.3.1怎样向文件读写字符338

10.3.2怎样向文件读写一个字符串341

10.3.3用格式化的方式读写文本文件345

10.3.4用二进制方式向文件读写一组数据345

10.4随机读写数据文件350

10.4.1文件位置标记及其定位350

10.4.2随机读写353

10.5文件读写的出错检测355

 

 

 

 

 

一些典型题:

C语言——经典200道实例【基础例题100道——进阶例题100道】_刘鑫磊up的博客-CSDN博客_c语言基础编程100道

输出单个字符

(注意scanf后面跟的不一样的格式化字符)

#include <stdio.h>

int main() {

   char c;        // 声明 char 变量

   c = 'A';       // 定义 char 变量

   printf("c 的值为 %c", c);

   return 0;

}

使用 printf() 与 %c 格式化输出一个字符

输出结果:

c 的值为 A

输出浮点数

#include <stdio.h>

int main() {

   float f;             // 声明浮点数变量

   f = 12.001234;       // 定义浮点数变量

   printf("f 的值为 %f", f);

   return 0;

}

使用 printf() %f 输出浮点数

输出结果:

f 的值为 12.001234

输出双精度(double)数

#include <stdio.h>

int main() {

   double d;            // 声明双精度变量

   d = 12.001234;       // 定义双精度变量

   printf("d 的值为 %le", d);

   return 0;

}

使用 printf() %e 输出双精度数

输出结果:

d 的值为 1.200123e+01

字符转 ASCII

https://blog.csdn.net/liu17234050/article/details/104219239

#include <stdio.h>

int main()

{

    char c;

    printf("输入一个字符: ");

    // 读取用户输入

    scanf("%c", &c);  

    // %d 显示整数

    // %c 显示对应字符

    printf("%c 的 ASCII 为 %d", c, c);

    return 0;

}

输出结果:

输入一个字符: a

a 的 ASCII 为 97

1)一次读取长字符,并一个一个显示 ASCII

#include <stdio.h>

#define MAX_STRING_LENGTH 65535 // 最大字符串长度

int main(){

  char s[MAX_STRING_LENGTH];

  printf("请输入长度小于 %d 的任意字符:",MAX_STRING_LENGTH);

  scanf("%s",s);    // 读取字符串。

  for(int i = 0; s[i]; i++){

    printf("%c的ASCII:%d\t",s[i],s[i]);

  }

}

 2ASCII 转字符

#include <stdio.h>

#define MAX_ASCII 127

int main()

{

    char num,enter;

    int temp=1;

    for(;temp>0;)

    {

        printf("----------------------------\n");

        printf("|**      开始            **|\n");

        printf("|**ASCII  转  字符  按:1 **|\n");

        printf("|**字符   转  ASCII 按:2 **|\n");

        printf("|**      结束       按:0 **|\n");

        printf("----------------------------\n");

        scanf("%d",&temp);

        if(temp==1)

        {

            printf("请输入数值小于 %d 的任意字符:",MAX_ASCII);

            scanf("%d",&num);

            printf("ASCII为 %d ,对应的字符为 %c \n",num,num);

        }

        if(temp==2)

        {

            printf("输入一个字符: \n");     

            scanf("%c", &enter);      //回车键也算字符,所以这里使用其他变 量替之.

            scanf("%c", &num);  

            printf("     %c 的 ASCII 为 %d    \n", num, num);

        }

    }

return 0;

计算字节大小

(1)计算 int, float, double 和 char 字节大小

使用 sizeof 操作符计算int, float, double 和 char四种变量字节大小

sizeof C 语言的一种单目操作符,如C语言的其他操作符++--等,它并不是函数sizeof 操作符以字节形式给出了其操作数的存储大小

#include <stdio.h>

int main()

{

    int integerType;

    float floatType;

    double doubleType;

    char charType;

    // sizeof 操作符用于计算变量的字节大小

    printf("Size of int: %ld bytes\n",sizeof(integerType));

    printf("Size of float: %ld bytes\n",sizeof(floatType));

    printf("Size of double: %ld bytes\n",sizeof(doubleType));

    printf("Size of char: %ld byte\n",sizeof(charType));

    return 0;

}

输出结果:

Size of int: 4 bytes

Size of float: 4 bytes

Size of double: 8 bytes

Size of char: 1 byte

不使用临时变量交换两个整数的值

(其实交换两个指针变量的值也可以)

#include <stdio.h>

int main() {

   int a, b;

   a = 11;

   b = 99;

   printf("交换之前 - \n a = %d, b = %d \n\n", a, b);

   a = a + b;  // ( 11 + 99 = 110)  此时 a 的变量为两数之和,b 未改变

   b = a - b;  // ( 110 - 99 = 11)  

   a = a - b;  // ( 110 - 11 = 99)

   printf("交换后 - \n a = %d, b = %d \n", a, b);

}

奇偶数判断(利用二进制)

奇偶数判断其实有个更简单高效的办法,我们的整数,在计算机中存储的都是二进制奇数的最后一位必是1,所以我们可以这样写:

#include <stdio.h>

int main()

{

    int number;

    printf("请输入一个整数: ");

    scanf("%d", &number);

    // 判断这个数最后一位是1这为奇数

    if(number&1)

        printf("%d 是奇数。", number);

    else

        printf("%d 是偶数。", number);

    return 0;

}

循环输出 26 个字母

C语言中‘a‘和“a“有什么区别?_嵌入式Linux系统开发的博客-CSDN博客_"a\"和'a'的区别"

#include <stdio.h>

int main()

{

    char c;

    for(c = 'A'; c <= 'Z'; ++c)

       printf("%c ", c);

    return 0;

}

输出结果:

A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

直接条件判断闰年

(四年一闰,百年不闰) || 四百年在闰年

#include <stdio.h>

int main()

{

    int year;

    printf("输入年份: ");

    scanf("%d",&year);

    // year = 400;

    // (四年一闰,百年不闰) || 四百年在闰年

    if((year % 4 == 0 && year % 100 != 0) || year % 400 == 0)    

    {

        printf("y\n");

    }

    else

    {

        printf("n\n");

    }

    return 0;

}

判断素数

质数(prime number)又称素数,有无限个。质数定义为在大于1的自然数中,除了1和它本身以外不再有其他因数,这样的数称为质数

(1)

#include <stdio.h>

int main()

{

    int n, i, flag = 0;

    printf("输入一个正整数: ");

    scanf("%d",&n);

    for(i=2; i<=n/2; ++i)

    {

        // 符合该条件不是素数

        if(n%i==0)

        {

            flag=1;

            break;

        }

    }

    if (flag==0)

        printf("%d 是素数",n);

    else

        printf("%d 不是素数",n);

    return 0;

}

输出结果:

输入一个正整数: 29

29 是素数

2)判断两个数之间的素数

#include <stdio.h>

int main()

{

    int low, high, i, flag;

    printf("输入两个整数: ");

    scanf("%d %d", &low, &high);

    printf("%d 与 %d 之间的素数为: ", low, high);

    while (low < high)

    {

        flag = 0;

        for(i = 2; i <= low/2; ++i)

        {

            if(low % i == 0)

            {

                flag = 1;

                break;

            }

        }

        if (flag == 0)

            printf("%d ", low);

        ++low;

    }

    return 0;

}

输出结果:

输入两个整数: 100  200

100 与 200 之间的素数为: 101 103 107 109 113 127 131 137 139 149 151 157 163 167 173 179 181 191 193 197 199

3)使用函数判断两数间的素数

#include <stdio.h>

int checkPrimeNumber(int n);

int main()

{

    int n1, n2, i, flag;

    printf("输入两个正整数: ");

    scanf("%d %d", &n1, &n2);

    printf("%d 和 %d 间的素数为: ", n1, n2);

    for(i=n1+1; i<n2; ++i)

    {

        // 判断是否为素数

        flag = checkPrimeNumber(i);

        if(flag == 1)

            printf("%d ",i);

    }

    return 0;

}

// 函数定义

int checkPrimeNumber(int n)

{

    int j, flag = 1;

    for(j=2; j <= n/2; ++j)

    {

        if (n%j == 0)

        {

            flag =0;

            break;

        }

    }

    return flag;

}

输出结果:

输入两个正整数: 10 30

10 和 30 间的素数为: 11 13 17 19 23 29

4)用sqrt降低时间复杂度

#include<stdio.h>

#include<math.h>

int issushu(int k);

int main(){

    int n,count=0;

    scanf("%d",&n);

    for(int i=n;i>3;i--)

        if(issushu(i))

            if(issushu(i-2))

                count++;

    printf("%d",count);

    return 0;

}

int issushu(int k){

    for(int i=2;i<sqrt(k)+1;i++){

        if(k%i==0)

        return 0;

    }

    return 1;

}

求一个整数的所有因数

假如a*b=cabc都是整数),那么我们称ab就是c的因数

#include <stdio.h>

int main()

{

    int number, i;

    printf("输入一个整数: ");

    scanf("%d",&number);

    printf("%d 的因数有: ", number);

    for(i=1; i <= number; ++i)

    {

        if (number%i == 0)

        {

            printf("%d ",i);

        }

    }

    return 0;

}

输出结果:

输入一个整数: 60

60 的因数有: 1 2 3 4 5 6 10 12 15 20 30 60

寻找两数的所有公因数:

#include <stdio.h>

int main()

{

    int num1, num2, i;

    printf("输入两个数,使用空格分隔:");

    scanf("%d %d", &num1, &num2);

    for (i = 1; i <= (num1 > num2 ? num2 : num1) ; ++i)

    {

        if (num1%i == 0 && num2%i == 0)

        {

            printf("%d ", i);

        }

    }

return 0;

}

计算一个数是否可为两个素数之和

#include <stdio.h>

int checkPrime(int n);

int main()

{

    int n, i, flag = 0;

    printf("输入正整数: ");

    scanf("%d", &n);

    for(i = 2; i <= n/2; ++i)

    {

        // 检测判断

        if (checkPrime(i) == 1)

        {

            if (checkPrime(n-i) == 1)

            {

                printf("%d = %d + %d\n", n, i, n - i);

                flag = 1;

            }

        }

    }

    if (flag == 0)

        printf("%d 不能分解为两个素数。", n);

    return 0;

}

// 判断素数(定义了一个新函数)

int checkPrime(int n)

{

    int i, isPrime = 1;

    for(i = 2; i <= n/2; ++i)

    {

        if(n % i == 0)

        {

            isPrime = 0;

            break;

        }  

    }

return isPrime;

}

二进制与十进制相互转换

1)二进制转换为十进制

#include <stdio.h>

#include <math.h>

int convertBinaryToDecimal(long long n);

int main()

{

    long long n;

    printf("输入一个二进制数: ");

    scanf("%lld", &n);

    printf("二进制数 %lld 转换为十进制为 %d", n, convertBinaryToDecimal(n));

    return 0;

}

int convertBinaryToDecimal(long long n)

{

    int decimalNumber = 0, i = 0, remainder;

    while (n!=0)

    {

        remainder = n%10;

        n /= 10;

        decimalNumber += remainder*pow(2,i);

        ++i;

    }

    return decimalNumber;

}

输出结果:

输入一个二进制数: 110110111

二进制数 110110111 转换为十进制为 439

2)十进制转换为二进制

#include <stdio.h>

#include <math.h>

long long convertDecimalToBinary(int n);

int main()

{

    int n;

    printf("输入一个十进制数: ");

    scanf("%d", &n);

    printf("十进制数 %d 转换为二进制位 %lld", n, convertDecimalToBinary(n));

    return 0;

}

long long convertDecimalToBinary(int n)

{

    long long binaryNumber = 0;

    int remainder, i = 1, step = 1;

    while (n!=0)

    {

        remainder = n%2;

        printf("Step %d: %d/2, 余数 = %d, 商 = %d\n", step++, n, remainder, n/2);

        n /= 2;

        binaryNumber += remainder*i;

        i *= 10;

    }

    return binaryNumber;

}

输出结果:

输入一个十进制数: 100

Step 1: 100/2, 余数 = 0, 商 = 50

Step 2: 50/2, 余数 = 0, 商 = 25

Step 3: 25/2, 余数 = 1, 商 = 12

Step 4: 12/2, 余数 = 0, 商 = 6

Step 5: 6/2, 余数 = 0, 商 = 3

Step 6: 3/2, 余数 = 1, 商 = 1

Step 7: 1/2, 余数 = 1, 商 = 0

十进制数 100 转换为二进制位 1100100

删除字符串中的特殊字符

#include <stdio.h>

#include <string.h>

int main() {

char line[100];

int i, j, len;

printf("输入一个字符串: ");

scanf("%s", line);

len = strlen(line);

for (i = 0; i < len + 1; i++)

{

if ((line[i] >= 'a' && line[i] <= 'z') || (line[i] >= 'A' && line[i] <= 'Z'))

continue;

for (j = i; j < len; j++) {

line[j] = line[j + 1];

}

len--;

i--;

}

//line[len]='\0';

printf("%s\n", line);

return 0;

}

后记:

2022年12月31日星期六完成了对C语言的总结,是对未来的一次铺垫,也是对自己过去的一次总结,新的一年里要努力提升自己的能力,不要沉沦于过去,努力看向未来,卷起来吧,少年!

进阶学习:

学C/C++想提升功底 可以花点时间看看这篇博客---C语言程序环境和预处理 (csdn.net)

让你不再害怕指针——C指针详解(经典,非常详细)_唐大麦的博客-CSDN博客_k指针

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/572792
推荐阅读
相关标签
  

闽ICP备14008679号