C语言基础 1 - 程序化思维

Updated on in 编程语言 with 0 views and 0 comments

数的表达

1.1. 十进制数

人类的手指数量是十进制的基础,基于手指的计数方式使得十进制(decimal )作为计算的基本体系成为了一种自然而然的选择。
一个十进制数的整数是个位、十位、百位、千位等的组合。例如,123(一百二十三)的个位数是3,十位数是2,百位数是1。

$ 123=1×10^2+2×10^1+3×10^0 $表达的是:

123
百位十位个位
  • 一般地,一个数可以表达为$ a_n…a_i…a_0 $,代表的实际含义是: $ a_n \times D^n + a_{n-1} \times D^{n-1} + \ldots + a_i + \ldots + a_1 \times D^1 + a_0 \times D^0 $ ——(1-1)
    其中,$ a_i $∈(0,1,2,3,4,5,6,7,8,9),(i=0,1,2,…,n),D称为十进制的基数,D的实际含义是10(十)。
  • 一个十进制的小数,同样可以用上述形式表达为$ 0.b_1…b_i…b_m $,实际代表的含义如下: $ b_1 \times 10^{-1} + b_2 \times 10^{-2} + \ldots + b_i + \ldots + b_m \times 10^{-m} $ ——(1-2)

其中,$ b_i $∈(0,1,2,3,4,5,6,7,8,9),(i=0,1,2,…,m) 例如,$ 0.123=1×10^{-1}+2×10^{-2}+3×10^{-3} $

  • 综上所述,给定的数学表达式描述了一个十进制数,它由一个整数部分和一个小数部分组成。整数部分由 $ a_n, a_{n-1}, \ldots, a_0 $ 组成,其中 $ a_n $ 是最高位(可能是符号位,正数时为0,负数时为1后跟随数值的绝对值),而小数部分由 $ b_1, b_2, \ldots, b_m $ 组成。这里的 $ n $ 和 $ m $ 分别代表整数部分和小数部分的位数。 表达式可以重写为: $ a_n \times 10^n + a_{n-1} \times 10^{n-1} + \ldots + a_0 \times 10^0 + b_1 \times 10^{-1} + b_2 \times 10^{-2} + \ldots + b_m \times 10^{-m} $

这可以进一步简化为两个求和公式的组合:

$ \sum_{i=0}^{n} a_i \times 10^i + \sum_{j=1}^{m} b_j \times 10^{-j} $ ——(1-3)

这里,$ \sum $ 表示求和,$ i $ 和 $ j $ 是索引变量,分别用于遍历整数部分和小数部分的每一位。

  • 第一个求和公式 $ \sum_{i=0}^{n} a_i \times 10^i $ 计算整数部分的值。
  • 第二个求和公式 $ \sum_{j=1}^{m} b_j \times 10^{-j} $ 计算小数部分的值。 这两个求和公式共同构成了给定的十进制数的完整表示。 例如,考虑十进制数 $ 123.45 $:
  • 整数部分是 $ 123 $,可以表示为 $ 1 \times 10^2 + 2 \times 10^1 + 3 \times 10^0 $。
  • 小数部分是 $ 45 $(或更准确地说是 $ 0.45 $),可以表示为 $ 4 \times 10^{-1} + 5 \times 10^{-2} $。 因此,$ 123.45 $ 可以重写为: $ (1 \times 10^2 + 2 \times 10^1 + 3 \times 10^0) + (4 \times 10^{-1} + 5 \times 10^{-2}) $ 这与我们之前的通用表达式是一致的。

1.2. 二、八、十六进制数

1.2.1 二进制数

用二进制(Binary)表达一个数,二进制数是一种数制系统,它只使用两个数字:0和1。在二进制中,每一位(bit)的值是前一位的两倍(在十进制中表示)。最右边的位是最低位(也称为“0位”或“个位”),其值就是它本身(0或1)。然后,向左移动,每一位的值都是前一位的两倍。

例如一个数写为:$ a_n…a_i…a_0 $,其中,$ a_i $∈(0,1),实际意义是:
$ a_n \times 2^n + a_{n-1} \times 2^{n-1} + \ldots + a_1 \times 2^1 + a_0 \times 2^0 $
或者更一般地表示为:
$ a_n \times B^n + a_{n-1} \times B^{n-1} + \ldots + a_1 \times B^1 + a_0 \times B^0 $
(其中B是基数,对于二进制来说B=2)

例如,二进制数1011转换为十进制数的计算过程如下:
$ 1011_{(2)} = 1 \times 2^3 + 0 \times 2^2 + 1 \times 2^1 + 1 \times 2^0 $
$ = 1 \times 8 + 0 \times 4 + 1 \times 2 + 1 \times 1 $
$ = 8 + 0 + 2 + 1 $
$ = 11_{(10)} $

所以,二进制数1011等于十进制数11。

1.2.2. 八进制数

可以用八进制(Octal)表达一个数,八进制数是一种数制系统,它使用八个数字:0, 1, 2, 3, 4, 5, 6 和 7。在八进制中,每一位(digit)的值是前一位的八倍(在十进制中表示)。

给出公式:
$ a_n \times 8^n + a_{n-1} \times 8^{n-1} + \ldots + a_1 \times 8^1 + a_0 \times 8^0 $
或者更一般地表示为:
$ a_n \times O^n + a_{n-1} \times O^{n-1} + \ldots + a_1 \times O^1 + a_0 \times O^0 $
(其中8, O是基数,对于八进制来说O=8,但通常我们用8来表示八进制的基数,以避免与十进制中的0混淆。不过,在你的公式中,为了区分,你使用了O,这是可以理解的。)

注意,基数上的幂(如$ 8^n $)仍用十进制表达,这是数学上的常规做法。

现在,让我们来个例子:

八进制数127转换为十进制数的计算过程如下:
首先,按照八进制的规则,每一位上的数字乘以8的该位权重(从0开始计数):
$ 127_{(8)} = 1 \times 8^2 + 2 \times 8^1 + 7 \times 8^0 $
然后,计算这些乘积的和:
$ = 1 \times 64 + 2 \times 8 + 7 \times 1 $
$ = 64 + 16 + 7 $
$ = 87_{(10)} $

所以,八进制数127等于十进制数87。

在实际应用中,八进制数经常用于计算机科学中,特别是在处理文件权限和某些类型的计算机内存地址时。

1.2.3. 十六进制数

其实中国长期采用十六进制(Hexadecimal)作为重量的进制。十六进制数是一种数制系统,它使用十六个数字:0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, 和 F。其中,A 到 F 分别表示十进制的 10 到 15。在十六进制中,每一位(digit)的值是前一位的十六倍(在十进制中表示)。

给出的公式:
$ a_n \times H^n + a_{n-1} \times H^{n-1} + \ldots + a_1 \times H^1 + a_0 \times H^0 $

它表示了一个十六进制数如何被转换为十进制数。在这个公式中:

  • $ a_i $ 是每一位上的数字,它可以是 0 到 9 或 A 到 F 中的任何一个。
  • $ H $ 是十六进制的基数,它等于十进制的 16。
  • $ n $ 是这个十六进制数的位数(从 0 开始计数)。

注意,虽然 A 到 F 在十六进制中表示的是十进制的 10 到 15,但在进行数学运算时,我们通常会将它们转换为十进制数。然而,在表示十六进制数时,我们仍然使用 A 到 F 这些符号,以保持十六进制数的格式。

例如,十六进制数 1A3F 转换为十进制数的计算过程如下:

$ 1A3F_{(16)} = 1 \times 16^3 + A \times 16^2 + 3 \times 16^1 + F \times 16^0 $

$ = 1 \times 4096 + 10 \times 256 + 3 \times 16 + 15 \times 1 $

$ = 4096 + 2560 + 48 + 15 $

$ = 6719_{(10)} $

所以,十六进制数 1A3F 等于十进制数 6719。

在实际应用中,十六进制数经常用于计算机科学中,特别是在处理颜色代码、内存地址和某些类型的计算机指令时。

1.3. 数的进制转换

1.3.1. 十进制数转换为二进制数

本题考查的是十进制数转换为二进制数的方法。

十进制数转换为二进制数,采用“除2取余,逆序排列”法。

  1. 对于189:189 ÷ 2 = 94......1
    94 ÷ 2 = 47......0
    47 ÷ 2 = 23......1
    23 ÷ 2 = 11......1
    11 ÷ 2 = 5......1
    5 ÷ 2 = 2......1
    2 ÷ 2 = 1......0
    1 ÷ 2 = 0......1所以,189的二进制表示为:10111101。
  2. 对于128:由于 $ 2^7 = 128 $,所以128的二进制表示为:10000000。
  3. 对于510:510 ÷ 2 = 255......0
    255 ÷ 2 = 127......1
    127 ÷ 2 = 63......1
    63 ÷ 2 = 31......1
    31 ÷ 2 = 15......1
    15 ÷ 2 = 7......1
    7 ÷ 2 = 3......1
    3 ÷ 2 = 1......1
    1 ÷ 2 = 0......1所以,510的二进制表示为:111111110。
  4. 对于1023:1023 ÷ 2 = 511......1
    511 ÷ 2 = 255......1
    255 ÷ 2 = 127......1
    127 ÷ 2 = 63......1
    63 ÷ 2 = 31......1
    31 ÷ 2 = 15......1
    15 ÷ 2 = 7......1
    7 ÷ 2 = 3......1
    3 ÷ 2 = 1......1
    1 ÷ 2 = 0......1所以,1023的二进制表示为:1111111111。

综上

189的二进制是10111101

128的二进制是10000000

510的二进制是111111110

1023的二进制是1111111111

1.3.2. 二进制数转换为十进制、八进制、十六进制和三进制数

本题考查的是二进制数转换为十进制、八进制、十六进制和三进制数的方法。

首先,把二进制数10101011转换为十进制数

从右到左,二进制数的每一位分别代表 $ 2^0, 2^1, 2^2, ... $ 的值。

10101011(B)
= $ 1 \times 2^7 + 0 \times 2^6 + 1 \times 2^5 + 0 \times 2^4 + 1 \times 2^3 + 0 \times 2^2 + 1 \times 2^1 + 1 \times 2^0 $
= 128 + 0 + 32 + 0 + 8 + 0 + 2 + 1
= 171

接下来,转换为八进制数

从右到左,每三位二进制数可以转换为一个八进制数(不足三位时,在前面补0)。

10101011(B) 可以分成 001 010 101 110(前面补0以满足三位一组)。

001 → 1(O)
010 → 2(O)
101 → 5(O)
110 → 6(O)

组合起来就是 1256(O)。但注意,我们最初没有补0,所以正确的八进制表示是 253(O)(直接按8位分)。

再转换为十六进制数

从右到左,每四位二进制数可以转换为一个十六进制数(不足四位时,在前面补0)。

10101011(B) 可以直接转换(8位刚好是2个十六进制数)。

1010 → A(H)
1011 → B(H)

组合起来就是 AB(H)。

步骤
将二进制数转换为十进制数:

最后,转换为三进制数

  • 首先,将二进制数转换为十进制数。10101011(B)
    = $ 1 \times 2^7 + 0 \times 2^6 + 1 \times 2^5 + 0 \times 2^4 + 1 \times 2^3 + 0 \times 2^2 + 1 \times 2^1 + 1 \times 2^0 $

= $ 1 \times 128 + 0 \times 64 + 1 \times 32 + 0 \times 16 + 1 \times 8 + 0 \times 4 + 1 \times 2 + 1 \times 1 $

= 128 + 32 + 8 + 2 + 1

= 171

  • 再将十进制数 171 转换为三进制数:通过不断除以 3 并记录余数来实现:

$ ( 171 \div 3 = 57 ) 余 0 $

$ ( 57 \div 3 = 19 ) 余 0 $

$ ( 19 \div 3 = 6 ) 余 1 $

$ ( 6 \div 3 = 2 ) 余 0 $

$ ( 2 \div 3 = 0 ) 余 2 $

将余数从下往上排列,得到三进制数:20100

结果
10101011(B) = 20100(T)(T表示三进制)

综上,二进制数10101011转换为十进制是171,八进制是253,十六进制是AB,三进制是202022。

二、八、十六进制的直接转换

127(八进制)

$ =1×10^2+2×10^1+7×10^0 $(八进制的127)

$ =001\space010\space111 $ (写成二进制,每3位一组成八进制的一位数)
$ =0\space0101\space0111 $ ( 每4位组成十六进制的一位数)
$ =0\space\space5\space\space7 $ (十六进制的057)
$ =0×H^2+5×H^1+7×H^0 $(十六进制)

从十六进制换算为十进制
$ =0×16^2+5×16^1+7×16^0 $(把十六进制换算为十进制)

$ =5×16+7 $(十进制)

$ =87 $(十进制)

或直接从八进制换算为十进制:
$ =1×8^2+2×8^1+7×8^0 $(换算为十进制)

$ =64+16+7 $

$ =87 $(十进制)

事实上,可以定义任何一种进制,例如,三进制、七进制等,从而形成不同的进制体系。我们日常生活中的12或24的小时进制,60秒为1分的60进制,等等。

数的计算

2.1. 手写计算

示例:1234 + 5678= ?

手写计算

通过手写运算可知,1234 + 5678 = 6912

2.2. 手工计算工具

2.2.1. 算筹

春秋战国;“筹算”的使用已经非常普遍了

  • 用“算筹”作为工具进行的计算叫“筹算”。
  • 经常讲:让我们筹算筹算!

2.2.2. 珠算

珠算(珠算文化),是以算盘为工具进行数字计算的一种方法,是中国古代众多的科技发明之一,系由“筹算”演变而来。国家级非物质文化遗产之一。

(1)以算珠靠梁表示记数开始。每颗上珠当做是五,每颗下珠当做是一,空档表示零。以档表示数位,高位在左,低位在右。

(2)在置数前,算盘上不能有任何算珠靠梁。置数时,应先由高到低(从左向右)定位,将预定数字按位逐档拨珠靠梁。

(3)珠算加减从左向右进行,可边看边打,在被加数(被减数)上连加(连减)几个数,盘面就会显示结果。

(4)乘除运算在盘上使用大九九口诀的加减运算。

传统加减法是通过口诀进行计算的,而珠算加减法依据“五升十进制”原理,通过对5与10两数的分解和合成,利用“凑数”与“补数”进行计算的。

凑数(1+4=5,2+3=5,3+2=5,4+1=5)

补数(1+9=10,2+8=10,3+7=10,4+6=10,5+5=10,6+4=10,7+3=10,8+2=10,9+1=10)
加法口诀表减法口诀表

2.3. 计算机器(computing machinery)

计算机器的发展:

  • 1275年,西班牙雷蒙德.露利(R.Lullus)发明一种思维机器(“旋转玩具”)
  • 1641年法国人帕斯卡(B.Pascal)利用齿轮技术做成加法器
  • 1673年,德国人莱布尼茨(G.W.V.Leibniz)在此基础上制造了能加、减、乘、除的计算机器。
  • 19世纪30年代,英国人巴贝奇(C.Babbage)设计了用于计算对数、三角函数和其它算术函数的“分析机”。
  • 1991年,依据 Babbage的原始设计计划,工程师们制造出来差分机(difference engine), 功能完整,验证了在9世纪Babbage的机器是能工作的。

2.4.构造一个8位二进制的加减法器

构造一个8位二进制加减法器

构造一个8位二进制加减法器通常涉及使用基本的逻辑门(如AND、OR、XOR、NOT)以及半加器(Half Adder, HA)和全加器(Full Adder, FA)的组合。这里,我将用图示如何构建一个8位二进制加减法器,包括必要的组件和它们之间的连接。

组件

2.5. 可编程计算机器(什么是编程)

编程,简单来说,就是编写一系列指令或代码的过程,这些指令或代码能够指导计算机进行各种操作。这些操作可能包括数据处理、逻辑判断、输入输出等。编程的目的是让计算机能够根据人类的意愿自动完成各种任务,从而提高工作效率和准确性。

2.5.1. 可编程计算机器的定义

可编程计算机器,顾名思义,是指能够依据预设的程序自动执行计算任务的机器。这种机器的核心在于其能够根据用户编写的程序,一步一步地执行计算步骤,从而完成复杂的计算任务。

2.5.2. Ada Lovelace的观点

Ada Lovelace认为,巴贝奇的分析机(一种早期的计算机器)与之前的计算机器的根本差别在于其能够事先编写一系列的计算步骤,这些步骤共同构成了一个程序,使得分析机能够执行复杂的计算。这一观点奠定了现代计算机器可编程性的基础。

2.5.3. 图灵机与冯诺依曼计算机结构

  1. 图灵的程序:艾伦·图灵提出的图灵机模型是计算机科学和计算理论的基础。图灵机通过读取、写入和移动磁带上的符号来模拟计算过程,为现代计算机程序的编写和执行提供了理论基础。
  2. 冯诺依曼计算机结构:约翰·冯诺依曼提出的计算机结构模型是现代计算机的基本框架。该模型将计算机分为输入、输出、存储、运算和控制五大部件,并强调了程序的存储和执行过程。冯诺依曼结构为现代计算机的设计和实现提供了重要的指导。

综上所述,可编程计算机器通过预设的程序自动执行计算任务,而一个完整的程序则包含计算步骤、计算符号、运算的变量、计算公式、初始数据、中间(变量)数据和最终结果等基本要素。图灵的程序和冯诺依曼计算机结构为现代计算机的发展奠定了坚实的基础。

2.5.4. 程序的基本要素

程序的基本要素是构成程序完整性和功能性的关键组成部分。这些要素共同协作,确保程序能够按照预期执行并完成任务。以下是程序的基本要素:

  1. 指令集(或代码)
    定义:指令集是程序的核心,由一系列有序的指令组成,这些指令告诉计算机如何执行特定的操作。
    作用:每条指令都代表一个基本的计算或控制操作,如数据加载、存储、算术运算、逻辑运算、条件判断、循环控制等。
  2. 数据结构
    定义:数据结构是组织、管理和存储数据的方式。
    作用:程序通过数据结构来管理和操作数据,如数组、链表、栈、队列、树、图等。合适的数据结构可以提高程序的效率和性能。
  3. 算法
    定义:算法是一系列解决问题的步骤或规则,通常用于计算、数据处理和信息检索等任务。
    作用:算法决定了程序如何执行特定的任务,包括选择最优的路径、处理输入数据、生成输出等。
  4. 控制结构
    定义:控制结构是程序中的逻辑框架,用于控制指令的执行顺序。
    作用:常见的控制结构包括顺序结构、选择结构(如if语句)和循环结构(如for、while循环)。这些结构使程序能够根据不同的条件执行不同的操作,或重复执行某些操作。
  5. 输入/输出
    定义:输入/输出是程序与外部世界交互的方式。
    作用:输入操作使程序能够接收用户输入的数据或外部数据源提供的信息。输出操作则使程序能够将处理结果展示给用户或保存到外部存储介质中。
  6. 变量和常量
    定义:变量是程序中用于存储数据的标识符,其值可以在程序执行过程中改变。常量则是程序中固定不变的值。
    作用:变量和常量使程序能够灵活地处理数据,并在不同的上下文中使用不同的值。
  7. 注释
    定义:注释是程序中用于解释代码的文字说明。
    作用:注释对于理解代码的功能、调试程序以及与他人协作开发具有重要意义。虽然注释不是程序执行的必需部分,但它们对于提高代码的可读性和可维护性至关重要。
    综上所述,程序的基本要素包括指令集、数据结构、算法、控制结构、输入/输出、变量和常量以及注释。这些要素共同构成了程序的骨架和灵魂,使其能够按照预期执行并完成任务。

2.5.5. 编程的过程

  1. 需求分析: 在编程之前,开发者需要对任务或问题进行详细的分析,明确计算机需要完成的具体操作。
  2. 设计算法: 算法是解决问题的步骤和方法。开发者需要根据需求分析的结果设计合适的算法,以确保计算机能够正确地执行任务。
  3. 编写代码: 在确定了算法之后,开发者就可以开始编写代码了。编写代码时,开发者需要遵循编程语言的语法和规则,确保代码的正确性和可读性。
  4. 测试与调试: 编写完代码后,开发者需要进行测试和调试。测试是为了验证代码的正确性,而调试则是为了找出并修复代码中的错误。
  5. 部署与维护: 经过测试和调试后,如果代码没有问题,就可以将其部署到实际环境中使用了。在使用过程中,开发者还需要对代码进行维护和更新,以确保其能够持续运行并适应不断变化的需求。

2.6. 从手算到程序化运算

2.6.1. 一元一次方程

一元一次方程是指只含有一个未知数(通常表示为 $ x $),且未知数的次数为1的方程。其一般形式为:

$ ax + b = 0 $

其中 $ a $ 和 $ b $ 是已知数,且 $ a \neq 0 $(因为如果 $ a = 0 $,则方程退化为常数方程,不再是一元一次方程)。

求解一元一次方程的步骤如下:

  1. 识别方程形式:确保方程符合一元一次方程的一般形式。
  2. 隔离未知数:通过移项操作,使方程左侧只有未知数 $ x $,右侧为常数。
  3. 求解未知数:将未知数 $ x $ 的系数化为1,得到 $ x $ 的解。

具体步骤可以表示为:

$ ax + b = 0 $

移项得:

$ ax = -b $

然后两边同时除以 $ a $ 得:

$ x = -\frac{b}{a} $

编程求解一元一次方程的过程

使用编程语言求解一元一次方程,可以通过以下步骤实现:

  1. 输入已知数:接收用户输入的 $ a $ 和 $ b $ 值。
  2. 计算未知数:根据一元一次方程的求解公式 $ x = -\frac{b}{a} $ 计算 $ x $ 的值。
  3. 输出结果:将计算得到的 $ x $ 值输出给用户。

流程图

画板

C语言示例代码

以下是一个用C语言编写的求解一元一次方程的程序示例:

#include <stdio.h>

int main() {
    double a, b, x;

    // 输入a和b的值
    printf("请输入一元一次方程的系数a和常数项b(用空格分隔): ");
    scanf("%lf %lf", &a, &b);

    // 检查a是否为零,避免除以零的错误
    if (a == 0) {
        printf("输入错误,a不能为0,否则为非法一元一次方程。\n");
        }
    } else {
        // 计算x的值
        x = -b / a;

        // 输出结果
        printf("方程的解是: x = %.2lf\n", x);
    }

    return 0;
}

程序说明

  1. 输入部分:使用 scanf 函数从用户那里接收 $ a $ 和 $ b $ 的值。
  2. 检查部分:在计算 $ x $ 之前,先检查 $ a $ 是否为零。如果 $ a $ 为零且 $ b $ 也为零,则方程有无穷多解;如果 $ a $ 为零但 $ b $ 不为零,则方程无解。
  3. 计算部分:如果 $ a $ 不为零,则根据公式 $ x = -\frac{b}{a} $ 计算 $ x $ 的值。
  4. 输出部分:使用 printf 函数将计算得到的 $ x $ 值输出给用户,保留两位小数。

这个程序能够正确处理一元一次方程的求解问题,并给出适当的错误提示。

2.6.2. 一元二次方程的求解

初中学习过的一元二次方程

$ ax^2+bx+c=0 $

其中,a,b,c 是实数。(注:等号在数学中和在程序中都是等号,但程序中需要用=号表示赋值,要注意算术中和程序中'='的区别)

  • 步骤1. 先计算出 ∆=b2−4ac注意:

这样,就有:delta=b∗b−4∗a∗c

1. ∆是个希腊字母,计算机的(英文)键盘没有这个符号,可以写为英文delta表示。
2. 键盘上没有乘法符号×,用*替代;
3. 除法符号÷用/替代
  • 步骤2. 判断delta。可以分为三种情况:
    • 情况1:delta < 0,该方程式无实数解。在纸上写出:“该方程式无实数解” 。
      计算结束。
    • 情况2:delta = 0,表明只有一个实数解,接着计算。步骤2.1:计算:x1=-b/2*a。(注:等号是啥意思?)步骤2.2:在纸上写出:“该方程只有一个实数根是:x1的值” 。
      计算结束。
    • 情况3:delta大于0。步骤3.1:对delta做二次开方$ \sqrt delta $。键盘上没有根号,我们写为 $ s= sqrt(delta) $。(注释:sqrt 是英文square root of delta的简化发音。)步骤3.2:计算出两个根,分别是:x1 = (-b + s)/(2_a)x2 = (-b - s)/(2_a)(注:等号是啥意思?)
      步骤3.3:在纸上写出:“该方程有两个根,分别是:“x1的值”和“x2的值” 。
      计算结束。

一元二次方程求解流程图

用程序实现一元二次方程求解

/*  用#include 引入程序包:
  1.标准输入输出stdio.h
  2.数学库:math.h, 里面有开平方sart(x)的函数
*/
#include <stdio.h>
#include <math.h>


int main()   // 声明一个主函数
{
	// 下面声明计算中用的变量
	double a, b, c; // 声明 一元二次方程的三个系数变量为双精度浮点数
	double  x1, x2; // 声明 一元二次方程的两个根,为双精度浮点数变量
	double  Delta;  // 声明一个中间临时变量,用于计算b*b-4*a*c
	//  变量声明区结束


	// 接收用户的输入,计算机得到:a,b,c三个值
	printf("请输入一元二次方程的三个系数:\n");  //输入前的提示

	printf("请输入系数 a = \t"); //输入的提示
	scanf("%lf", &a);  //输入语句

	printf("请输入系数 b = \t");   //输入的提示
	scanf("%lf", &b);   //输入语句

	printf("请输入系数 c = \t");  //输入的提示
	scanf("%lf", &c);  //输入的提示

	//验证一下a,b,c三个系数是否输入正确?

	printf("a=%.2f,\nb=%.2f,\nc=%.2f\n", a, b, c);

	// 输入部分结束

	 //计算机开始计算:
	 //1.先计算出delta的值;
	Delta = b * b - 4 * a * c;

	// 分别考虑a,b,c和delta 的情况,分别做根的计算
	if (fabs(a) >= 0.00000001)   //判断a是否为0? 为安全起见,最好用 if (abs(a) >= 0.0000001),即,a大于一个很小的数
		//abs(x)是math.h里额一个函数,即,求出x的绝对值
	{

		if (Delta < 0)  //判断是否有实数解?
		{
			printf("该方程式无实数解!\n");

			/* 当Delta小于0时,方程无实数解,但是可以有虚数解。
			realpart = - b/(2*a); //实部
			mipart = sqrt(-Delta) / (2*a);
			printf("方程有两个共轭复根:\n");
			printf("x1 = %.2f+%.5fi\n",realpart,mipart);
			//人为地加了i,以表示虚部
			printf("x2 = %.2f-%.5fi\n",realpart,mipart);
			*/
		}
		else if (fabs(Delta) < 0.0000001) //安全起见,用if (abs(Delta) < 0.0000001), Delta小于一个很小的数即Delta=0
		{
			x1 = x2 = -b / (2 * a);
			printf("方程有两个相等的根:\nx1=x2 = %.2f\n", x1); //输出 计算结果
		}
		else
		{
			Delta = sqrt(Delta); // sqrt是math.h里的函数,用来开平方
			x1 = (-b + Delta) / (2 * a); //得到第一个根
			x2 = (-b - Delta) / (2 * a); //得到第一个根
			printf("方程的两个根为\nx1 = %.2f,\nx2 = %.2f\n", x1, x2); //输出 计算结果
		}
	}
	else if (fabs(b) >= 0.00000001)  //安全起见,用if (abs(b) > 0.0000001), 即,b大于一个很小的数即,b不为0
	{
		x1 = -c / b;
		printf("该方程是一元一次方程,根 x =%.2f\n", x1); //输出 计算结果
	}
	else if (fabs(c) < 0.0000001)//安全起见,用if (abs(c) < 0.0000001), 即,c小于一个很小的数即c=0
		printf("x可以是任意值。\n"); //输出 计算结果
	else
		printf("该方程不成立。\n"); //输出 计算结果

	return 0;
}

2.6.3. 二元一次方程组的求解

初中学习过的一元二次方程

$$
\begin{cases}
a_1x + b_1y = c_1 \
a_2x - b_2y = c_2
\end{cases}

$$

C语言,写 $ a_1,a_2 $ 太麻烦,将其改写为:

$$
a1x+b1y==c1 \
a2x+b2y==c2

$$

注:C语言中“==”表示恒等,“=”表示赋值(传递值),其中,a1,a2,b1,b2,c1,c2 是实数。

  • 第1步

$$
\begin{cases}
a_1x + b_1y = c_1 \quad \text{①} \
a_2x - b_2y = c_2 \quad \text{②}
\end{cases}

$$

首先确定① ②哪个方程更容易解除一个变量,假设由方程①得到方程③。

$$
y = \frac{c_1 - a_1x}{b_1} \quad \text{③}

$$

  • 第2步 将方程③代入方程②可得

$$
x = \frac{c_2 - b_2 (\frac{c_1 - a_1 x}{b_1})}{a_2}=\frac{c_2b_1−b_2c_1}{b_1a_2−b_2a_1} \quad \text{④}

$$

判断$ d=b_1a_2−b_2a_1 $

  • 情况1, $ d=0 $,该方程错误
  • 情况2,$ d≠0 $,计算计算方程④
  • (注: 浮点数比较:直接使用 '==' 比较浮点数可能会因为精度问题导致错误。定义一个很小的常量 EPSILON,用于比较浮点数是否接近于零。$ \epsilon = 1 \times 10^{-6} $)
  • 第3步求解y, 把④带入③求出y的值

$$
y=\frac{c_1a_2−a_1c_2}{b_1a_2−b_2a_1}

$$

二元一次方程组流程图:

用程序实现求解二元一次方程求解

#include <stdio.h>
#include <math.h>
#define EPSILON 1e-6

int main()
{
    float a1, b1, c1, a2, b2, c2, x, y, d;

    // 读取方程组系数
    printf("请输入方程组的系数:\n");
    printf("第一个方程的 a1、b1、c1:");
    if (scanf("%f%f%f", &a1, &b1, &c1) != 3) {
        printf("输入错误,请输入三个浮点数。\n");
        return 1;
    }
    printf("第二个方程的 a2、b2、c2:");
    if (scanf("%f%f%f", &a2, &b2, &c2) != 3) {
        printf("输入错误,请输入三个浮点数。\n");
        return 1;
    }

    d = b1 * a2 - b2 * a1;
    // 解方程组, 计算机中浮点数不等于0,使用绝对值fabs(d)<1e-6无限接近0来表述,
    if (fabs(d) < EPSILON) {
        printf("方程组无解!\n");
    } else {
        x = (c2 * b1 - b2 * c1) / d;
        y = (c1 * a2 - a1 * c2) / d;
        printf("方程组的解为:x=%.2f, y=%.2f\n", x, y);
    }

    // system("pause");
    return 0;
}

程序的结构

第一部分 引用

引用其他人的程序

#include <>

第二部分 声明

  • 声明输入变量
  • 声明输出变量
  • 声明中间(临时)变量

第三部分 输入

  • 从输入设备:鼠标,键盘等接受数据
  • 从其他函数接口接受数据

第四部分 计算

  • 判断,循环
  • 表达式计算
  • 程序算法运算

第五部分 输出

  • 计算结果输出

由此我们可以总结: 所有的程序,不管是几行代码,还是十万行代码,都是由这五个部分组成, 如下图所示:

C 语言编程建议与注意事项

代码可读性

- 使用有意义的变量名和注释来提高代码的可读性。
- 避免在条件表达式中进行复杂的计算或赋值操作,以免混淆逻辑。

性能优化

- 在可能的情况下,使用位运算代替算术运算以提高性能。
- 避免不必要的类型转换和重复计算,以减少 CPU 的负担。

错误处理

- 在使用数学库函数时,检查输入参数的有效性以避免运行时错误。
- 在进行除法运算时,确保除数不为零以避免除零错误。
- 对于用户输入的数据,进行必要的验证和过滤,以防止恶意输入导致的程序崩溃或安全问题。

代码结构

- 使用函数将代码模块化,以提高代码的可维护性和可重用性。
- 将相关的函数和数据封装在结构体或类中,以形成更清晰的代码结构。
- 使用条件编译指令来包含或排除特定的代码段,以适应不同的编译环境或需求。

综上所述,C 语言的运算与选择结构是编程中的基础部分。通过深入理解这些结构的工作原理和用法,结合良好的编程习惯和最佳实践,可以编写出更高效、更可读、更安全的 C 语言程序。

备注:

因个人习惯和能力所限,该文档内容若存在表述不合理或错误之处,请大家留言多多指正

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明


标题:C语言基础 1 - 程序化思维
作者:谷国信
单位:北京邮电大学世纪学院
单位:北京谷梁科技有限公司
地址:http://blog.guoxinweb.com/articles/2024/11/03/1730636222770.html