C语言程序设计 第五版 谭浩强 知识点汇总+复习

第一章 程序设计与C语言

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

  • 程序:一组计算机能识别和执行的指令
  • 程序设计:创建程序的过程

2. 概述C语言的主要特点

  • 语言简洁、紧凑,使用方便、灵活
  • 运算符丰富
  • 数据类型丰富
  • 具有结构化的控制语句
  • 语法限制不太严格,程序设计自由度大
  • 能直接访问物理地址,能进行位操作
  • 可移植性好
  • 生成目标代码质量高,程序执行效率高

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

源程序、目标程序、可执行程序

  • 源程序:用高级语言编写的程序,源程序通常包括:
    • 预处理指令
    • 全局声明
    • 函数定义
  • 目标程序:源程序经过编译器处理后转换为机器指令的程序
  • 可执行程序:经过链接器处理目标程序和其他必要的库文件后,生成的最终产品就是可执行程序。它是可以直接在计算机上运行的程序。

程序编辑、程序编译、程序连接

  • 程序编辑:编写和修改源代码的过程
    • 程序编译:将源程序转换为目标程序的过程
    • 程序连接:将编译后的代码与其他库代码或模块合并,形成最终可执行程序的过程

程序、程序模块、程序文件

  • 程序:一组指令,用于执行特定任务的完整代码集合
  • 程序模块:程序中的一个独立部分,通常实现特定功能
  • 程序文件:包含程序代码的文件

函数、主函数、被调用函数、库函数

  • 函数:执行特定任务的独立代码块
  • 主函数:大多数编程语言(如C)程序执行的入口点
  • 被调用函数:被其他函数调用以执行特定任务的函数
  • 库函数:预先编写并存储在库中的函数,可被多个程序共用

程序调试、程序测试

  • 程序调试:找出并修正程序的错误或缺陷的过程
  • 程序测试:执行程序以验证它是否按预期工作的过程。通常包括单元测试、集成测试和系统测试等。

第二章 顺序程序设计

数据表现形式

  • 常量

    • 整形常量

    • 实型常量

      • 十进制小数形式 如123.456
      • 指数形式 如12.34e3
    • 字符常量

      • 普通字符: ‘A’,以ASCII代码存储,‘A’ = 65, ‘a’ = 97

      • 转义字符: 以 ‘’ 开头的字符序列:

        转义字符描述
        \a警报声,产生声音或视觉信号
        \b退格符
        \f换页符
        \n换行符
        \r回车符
        \t水平制表符,用于对齐文本
        \v垂直制表符
        \\反斜线字符
        \'单引号字符
        \"双引号字符
        \?问号字符,用于避免与三元运算符混淆
        \0空字符(NULL字符),用于字符串的结束
        \ooo八进制表示的字符(ooo代表八进制数)
        \xhh十六进制表示的字符(hh代表十六进制数)
    • 字符串常量:“BOY”,双撇号将诺干个字符括起来,字符串常量是双撇号中的全部字符,但不包括双撇号

    • 符号常量:用 #define 指令,指定一个符号名称代表一个常量。如:#define PI 3.1416

  • 变量

    变量代表一个有名字的、具有特定属性存储单元,用来存放数据,也就是变量的值,在程序运行期间,变量的值是可以改变的。

    • 变量必须先定义,后使用

      定义时指定该变量的名字和类型,变量名实际上是以一个名字代表的存储地址,在对程序编译链接时有编译系统给每一个变量名分配对应的内存地址。从变量中取值,实际上是通过变量名找到相应的内存地址,从该存储单元中读取数据

      • 通过先定义变量,编译器可以知道每个变量的类型,从而在编译时进行类型检查。这有助于在程序运行前发现类型错误,增加程序的稳定性和效率。
      • 变量定义的同时也是在告诉编译器为该变量分配内存空间。如果没有先定义变量,编译器就无法知道需要为变量分配多少内存,以及如何管理这块内存。
      • 变量的定义决定了它的作用域(即变量可以被访问的代码区域)和生命周期(即变量存在的时间)。这样可以更好地管理变量的可见性和生命周期,避免例如变量名冲突、内存泄漏等问题。
      • C语言编译器在编译代码时只通过一次扫描就生成目标代码。如果变量可以在使用后定义,那么编译器可能需要多次扫描代码,复杂度和编译时间都会增加。
  • 常变量

    • C99 运许使用常变量,在定义变量时,前面加一个关键字 const,如 const int a = 3,且在变量存在期间其值不能改变

    • 常变量与常量的异同?

      常变量具有变量的基本属性,只是不允许改变其值,常变量是有名字的不变量,常量是没有名字的不变量

    • 常变量与符号常量有什么不同?

      常变量是有数据类型和内存地址的不可变变量,而符号常量是在编译之前就被替换掉的字符串,没有数据类型和内存地址。

  • 标识符

    • 在计算机高级语言中,用来对变量、符号常量名、函数、数字、类型等命名的有效字符序列统称为标识符(identifier),简单来说,标识符就是一个对象的名字
    • C语言规定标识符只能有 字母数字下划线 组成,且第一个字符必须是 字母或下划线

数据类型

  1. C语言中的数据类型主要有哪几类?

    类型子类型C语言表示字节数取值范围(Visual C++的安排)
    基本类型整形类型
    基本整形int4-2,147,483,648 至 2,147,483,647
    无符号整形unsigned int40 至 4,294,967,295
    短整形short2-32,768 至 32,767
    无符号短整形unsigned short20 至 65,535
    长整型long4-2,147,483,648 至 2,147,483,647
    无符号长整型unsigned long40 至 4,294,967,295
    双长整形long long8-9,223,372,036,854,775,808 至 9,223,372,036,854,775,807
    无符号双长整形unsigned long long80 至 18,446,744,073,709,551,615
    字符型char1-128 至 127 或 0 至 255)
    无符号字符型unsigned char10 至 255
    布尔型bool10 或 1
    浮点类型
    单精度浮点型float41.2E-38 至 3.4E+38
    双精度浮点型double82.3E-308 至 1.7E+308
    复数型浮点型float _Complex, double _Complex双倍于对应浮点型依赖于对应浮点型
    派生类型指针类型*
    数组类型[]
    结构体类型struct
    共用体类型union
    函数类型函数声明
    枚举类型enum
    空类型void

运算符与表达式

  • 向零取整: 5/3 = 1,-5/3 = - 1

  • % 运算符 要求 运算对象(即操作数)整数,结果也是整数

  • 自增、自减运算符

    ++i , 先++,再用 i
    i++ , 先用 i, 再++
    谁前先谁

  • 优先级:

    • 算术运算符:自左至右(左结合性)
    • 赋值运算符:自右至左(右结合性)
  • 运算符的两种性质:优先级结合性

隐式转换与显式转换

  • 隐式转换
    所谓隐式类型转换是指不用用户干预,系统自动进行的类型转换。隐式类型转换分为两种情况:

    • 1)运算时转换

      内存小 –> 内存大
      整型数据类型 –> 浮点数据类型
      有符号 –> 无符号

      int num1=12;
      double num2=10.5;
      num1+num2;
      

      在上述代码中,由于double类型的取值范围大于int类型,将int类型的num1与double类型的num2相加时,系统会自动将num1的数据类型由int转换为double类型,从而保证数据的精度不会丢失。

    • 2)赋值转换

      在赋值类型不同时,即变量的数据类型与所赋值的数据类型不同,系统会将 “=” 右边的值转换为变量的数据类型再将值赋给变量,例如给一个int类型的变量赋值为浮点数,如:int a = 10.2;
      以上代码将浮点数10.2赋值给int类型的变量a,编译器在赋值时会将10.2转换为int类型的10再赋值给a,a最终的结果为10。这种在赋值时发生的类型转换称为赋值转换。

  • 显示转换(强制转换)

    显式类型转换指的是使用强制类型转换运算符,将一个变量或表达式转化成所需的类型,其基本语法格式如下所示:

    (类型名)(表达式)
       int(x)%3
    

    在上述格式中,类型名和表达式都需要用括号括起来,具体示例如下:

    int x = 10;
    float f = 1.2;
    double d = 3.75;
    x = (int)(f + d);       //将f+d的结果强制转换为int类型,再赋值给变量x
    f = (double)(x)+d;       //将x强制转换为double类型,与d相加,再将结果赋值给f
    

数据的输入输出

printf的格式控制

printf 用于输出,其格式控制符包括:

  • printf(格式控制,输出表列)
  • %d%i:输出整数(十进制)。
  • %u:输出无符号整数(十进制)。
  • %f:输出浮点数(小数)。
    • %m.nf
      • m 表示字段的最小宽度。如果数值的位数少于 m,则输出会在左侧用空格填充,以确保整个输出至少有 m 个字符宽。
      • n 表示小数点后的数字位数。如果数值的小数部分位数多于 n,则四舍五入到 n 位;如果少于 n,则在右侧用零填充。
      • 如果 m < n,m 的设置被忽略,输出宽度由数值的整数部分和 n 指定的小数部分决定
    • %-m.nf
      • %-m.nf 的格式与 %m.nf 相同,但加了一个 - 符号,表示左对齐。
      • 在这种情况下,如果数值的宽度小于 m,则额外的空格会添加到数值的右侧,而不是左侧。
  • %lf:输出双精度浮点数。
  • %e%E:以科学计数法输出浮点数。
  • %g%G:根据数值的大小,自动选择 %f%e
  • %x%X:以十六进制格式输出无符号整数(小写/大写)。
  • %o:输出无符号整数(八进制)。
  • %c:输出单个字符。
  • %s:输出字符串。
  • %p:输出指针的值(地址)。
  • %%:输出 % 符号本身。

scanf的格式控制

scanf 用于输入,其格式控制符包括:

  • scanf(格式控制,地址表列)
  • %d:读取整数(十进制),%nd n为读取位数,不足前面需补空格
  • %u:读取无符号整数(十进制)。
  • %f:读取浮点数。
  • %lf:读取双精度浮点数。
  • %x:读取十六进制整数。
  • %o:读取八进制整数。
  • %c:读取单个字符。
  • %s:读取字符串,直到遇到空白字符(如空格、制表符或换行符)。
  • %[...]:读取符合指定字符集的字符串。
  • %n:读取到目前为止输入字符的数量。
  • %*跳过读取的输出。

附加修饰符

这些格式控制符可以与附加修饰符一起使用,以改变输出或输入的格式,如指定宽度、精度或用于 shortlong 类型的修饰符。

在使用这些函数时,需要确保传递的参数与格式控制符匹配,否则可能导致未定义行为。例如,使用 %d 读取或输出浮点数就是不正确的。

字符的输入输出

  • putchar( c )
  • getchar( c )

第三章 选择结构程序设计

if 语句

  • 一般形式:
 if ( 表达式 ) 语句1 
 	else 语句2  
  • 为了避免嵌套的条件分支语句if-else的二义性,C语言规定:C程序中的else总是与在其之前最近的未配对的if组成配对关系。

switch 语句

  • 一般形式
swithc(表达式){
case  常量1 : 语句1
case  常量2 : 语句2
... 
case  常量n : 语句n
default : 		   语句n+1
}
  • case 和 default 都是起 标号(label)作用,并不进行条件检查

  • default:

    • default 子句在 switch 语句中充当一种“后备”或“默认”情况。
    • 当没有任何其他 case 子句与 switch 表达式的值匹配时,执行 default 子句中的代码。
    • default 是可选的,如果没有提供,且没有匹配的 case,则不执行任何操作。
    • default 子句可以放在 switch 语句的任何位置,但通常放在最后以提高可读性。
  • break:

    • break 关键字用于立即退出 switch 语句。
    • 在每个 case 子句的末尾使用 break,以防止代码继续执行到下一个 case(这称为“穿透”)。
    • 如果没有 break,程序会继续执行下一个 case 无论其条件是否满足,这可能导致意外的行为。
    • 在 default 子句中使用 break 通常不是必需的,因为它已经是 switch 语句的最后部分,但有时加上 break 可以增加代码清晰度。

优先级

!> 算术运算符 > 关系运算符 > && 和 || > 赋值运算符

简答题

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

    算术运算:用于执行数学计算,如加(+)、减(-)、乘(*)、除(/)和取模(%)。
    关系运算:用于比较两个值,包括等于(==)、不等于(!=)、大于(>)、小于(<)、大于等于(>=)和小于等于(<=)。
    逻辑运算:用于组合条件判断,包括逻辑与(&&)、逻辑或(||)和逻辑非(!)。

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

    对于逻辑表达式,若其值为“真”,则以 1 表示,若其值为“假”,则以 0 表示。但是在判断一个逻辑量的值时,系统会以 0 作为“假”,以 1
    作为“真”。例如 3 && 5 的值为“真”,系统给出 3 && 5 的值为1。

第四章 循环结构程序设计

while 语句

  • 一般形式
while( 表达式 ) 语句

do while 语句

  • 一般形式
 do 
 	语句  
 while( 表达式 )

for 语句

  • 一般形式
for( 表达式1 : 表达式2 : 表达式3 ) 语句

简答题

  1. break语句与continue语句的区别
    break 语句和 continue 语句在C语言中都用于控制循环结构,但它们的作用和用法有明显的区别:

    • break 语句

      • 用途:break 用于立即退出当前的循环或 switch 语句。
      • 行为:在循环中使用时,break 会导致程序跳出包含它的最内层的 whilefordo-while 循环,继续执行循环之后的代码。
      • 示例场景:当满足特定条件需要提前结束循环时使用。
    • continue 语句

      • 用途:continue 用于跳过当前循环的剩余部分,直接开始下一次迭代。
      • 行为:在 for 循环中,continue 会导致控制跳转到迭代表达式;在 whiledo-while 循环中,控制跳转到循环的条件测试。
      • 示例场景:当在循环的某次迭代中,由于某些条件不需要执行循环的剩余部分,但仍需继续后续迭代时使用。

    总结来说,break 用于完全结束循环,而 continue 用于跳过当前迭代的剩余部分,继续执行后续的迭代。

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

定义数组

类型说明符 数组名[常量表达式]

数组初始化

  • 在定义数组时对全部数组元素赋初值
  • 可以只给数组中一部分元素赋值
  • 给数组值全部赋为0 int a[10] = {0}
  • 给全部元素赋初值时可以不指定数组长度,注意:多维数组,只可以第一维不指定长度

字符数组

  • C语言没有字符串类型,也没有字符串变量,字符串是存放在字符型数组中的

字符串处理函数

  • puts( 字符数组 )
  • gets( 字符数组 )
  • strcat( 字符数组 1, 字符数组 2)
  • strcpy( 字符数组 1,字符串 2 )
  • strcmp( 字符串 1,字符串 2)
  • strlen( 字符数组 )
  • strlwr( 字符串 )
  • strupr( 字符串 )
  • memmove(内容移动):将内存的一部分内容从一个位置复制到另一个位置,能够正确处理源地址和目标地址重叠的情况。
  • memset(内存设置):用一个特定的值来初始化内存块,将内存块中的每个字节都设置为这个值。
  • memcmp(内存比较):比较两块内存区域,按字节进行比较,用于判断两块内存区域是否相等。
  • memchr(内存搜索):在内存块中查找特定的字符,从内存的起始位置开始搜索,直到找到指定的字符或达到指定的长度。

第六章 利用函数实现模块化设计

函数定义

// 无参
类型名 函数名(){  //或(void)
	函数体
}
// 有参
类型名 函数名(形式参数列表){
	函数体
}
// 空函数
类型名 函数名(){
}

// 函数体包括 声明部分 和 语句部分
  • 函数可以放在任何位置,如果放在主函数后,需要加一个函数声明

    函数声明:

    1. 告诉编译器有一个函数叫什么,参数是什么,返回类型是什么。
    2. 具体是不是存在,无关紧要·
    3. 函数的声明一般出现在函数的使用之前·要满足先声明后使用,
    4. 函数的声明一般要放在头文件中的。

简答题:

  1. C语言的特点: 面向过程的模块化语言
  2. 模块的实现方法一一函数,具有特定功能而且相对独立的 程序块。
  3. C程序构成: 都有一个且仅有一个主函数main(),其他的函数由main直接或间接调用
  4. 使用函数机制的优点 : 1)程序简洁明晰: 2)代码重用性高; 3)有利于程序的阅读与维护; 4)编程效率高。
  5. 函数分类:
    1) 从用户角度对函数分类
    (1)标准函数一一库函数(2)自定义函数
    2)从函数定义形式看函数分类
    (1)带参函数 (2)无参函数
    3)从函数返回值看函数分类
    (1)有返回值函数 (2)无返回值函数

函数调用

函数调用的三种形式:
(1)函数语句
例:printf(“C程序设计\n”);
(2)函数表达式
例:a =max(x,y)*2;
(3)函数作为实参
例:printf(”%d\n",max(x,y));
m=max(a,max(b,c));

嵌套调用与递归调用

  • c语言中不允许函数作嵌套定义。因此各函数之间是平行的,不存在上一级函数和下一级函数的问题。但是C语言允许在一个函数的定义中出现对另一个函数的调用
  • 在调用函数的过程中,系统会把实参的值传递给被调用函数的形参。或者说,形参从实参中得到一个值。该值在函数调用期间有效。
  • 在调用一个函数的过程中又出现直接或间接地调用该函数
    本身,称为函数的递归调用。C语言允许函数的递归调用。

参数的数据传递

  • 函数的形参和实参具有以下特点:
    (1)形参只在本函数内部有效,函数调用结束后,则不
    能在别的函数中使用。
    (2)实参可以是常量、变量、表达式、函数等,无论实
    参是何种类型的量,在进行函数调用时,它们都必须具
    有确定的值,以便把这些值传送给形参。因此应预先用
    赋值、输入等方法使实参获得确定值
    ;而形参只能是变
    量。

    (3)实参和形参在数量、类型、顺序上应严格一致,否
    则会发生类型不匹配的错误。

  • 数组元素作函数实参时,把实参的值传给形参,是 值传递 的方式。数据传递的方向是从实参到形参,单项传递

  • 数组名作为函数参数时,实际上是将数组的首地址传递给函数,这是一种引用传递的方式。数据传递的方向仍然是从实参到形参,但传递的是内存地址而非值本身。在函数内部对数组的任何更改都会影响到原始数组

库函数

C语言中的库函数是一系列预先编写好的、可重用的函数,它们提供了执行各种常见任务的能力。这些函数被组织在标准库中,可以在不同的程序中使用。

  1. 标准输入输出库(stdio.h

    • 提供输入输出功能,如printfscanffgetsfputsfopenfclose等。
    • 用于处理文件操作和控制台输入输出。
  2. 标准库(stdlib.h

    • 提供转换函数、随机数生成、内存分配、控制进程的函数,例如mallocfreeatoirandexit等。
    • 包含各种通用的实用函数。
  3. 字符串处理库(string.h

    • 提供字符串处理的函数,如strcpystrcatstrlenstrcmp等。
    • 用于操作和处理字符数组。
  4. 数学库(math.h

    • 提供数学运算的函数,如sincossqrtpow等。
    • 用于进行各种数学计算。
  5. 字符处理库(ctype.h

    • 提供字符类型测试和转换,例如isalphaisdigittolowertoupper等。
    • 用于检查字符的类型和进行字符转换。
  6. 时间日期库(time.h

    • 提供处理日期和时间的函数,如timelocaltimestrftime等。
    • 用于获取和格式化日期和时间。
  7. 断言库(assert.h

    • 提供断言机制,用于程序调试。
    • 包含assert函数,用于测试假设和快速发现错误。

预处理指令

C提供的预处理功能主要有以下三种:

  1. 宏定义
    #define
  2. 文件包含
    #include
  3. 条件编译
    #if-- #else-- #endif

这些功能分别用宏定义命令、文件包含命令、条件编译
命令来实现。为了与一般C语句相区别, 这些命令以
符号“ # “ 开头

局部变量

  • 定义在函数内部或者复合语句内部声明的变量

  • 作用域:仅限于函数或复合语句内,离开该函数或复合语句后再使用这种变量是非法的。

  • 生命周期:仅限于函数或复合语句的执行期间。

  • 初始化:应在使用前初始化。

void function() {
    int localVar = 5; // 局部变量
}

全局变量

  • 定义在函数外部声明的变量,通常在程序的最上方。
  • 作用域:从声明初开始到程序结束
  • 生命周期:贯穿整个程序的执行周期。
  • 初始化:如果未显式初始化,自动初始化为0。
int globalVar; // 全局变量

void function() {
    globalVar = 5;
}

C语言中,对于同名变量的处理应遵循下面的规则:

  • 在同一个作用域内不允许出现同名变量的定义。
  • 相互独立的两个作用域内的同名变量分配不同的存储单元,代表不同的变量,互不影响.
  • 如果在一个作用域和其所包含的子作用域内出现同名变
    量,则在子作用域中,内层变量有效,外层变量被屏蔽

动态存储方式与静态存储方式

动态存储方式
  • 定义:在程序运行时分配和释放内存的方法。
  • 特点
    • 运行时分配:使用 malloc, calloc, realloc 等函数进行内存分配。
    • 手动管理:使用 free 函数释放内存。
    • 灵活性:允许动态增加或减少数据结构的大小。
    • 风险:可能导致内存泄漏或无效指针访问。
int *arr = malloc(10 * sizeof(int)); // 动态分配内存
if (arr != NULL) {
    // 使用 arr
}
free(arr); // 释放内存
静态存储方式
  • 定义:在程序编译时分配固定大小的内存。
  • 特点
    • 编译时分配:为全局变量和静态变量分配内存。
    • 自动管理:由编译器管理内存。
    • 限制性:内存大小在编译时确定。
    • 生命周期:在程序整个运行周期内存在。
int globalVar; // 全局变量
static int staticVar; // 静态变量

  • 静态存储方式是指在程序运行期间由系统分配固定的存储空间的方式。
  • 动态存储方式则是在程序运行期间根据需要进行动态的分配存储空间的方式。

自动的(auto)
静态的(static)
寄存器的(register)
外部的(extern)

1.auto变量

函数中的局部变量,如不专门声明为static存储类别,都是动态地分配存储空间的,数据存储在动态存储区中。函数中的形参和在函数中定义的变量(包括在复合语句中定义的变量),都属此类,在调用该函数时系统会给它们分配存储空间,在函数调用结束时就自动释放这些存储空间。因此这类局部变量称为自动变量。
关键字auto作存储类别的声明。 例如:

int f(int a)/*定义f函数,a为形参*/
/*定义b、c为自动变量*/
auto int b,c=3
  1. static声明局部
  • 定义:static 数据类型 内部变量表;

  • 存储特点:

    • 作用域:只在本函数内有效,其它函数不可引用
    • 生存期:扩展到整个程序(即函数调用结束也
      不释放)。
    • 初始化:定义但不初始化,则系统自动初始化:0(整
      型和实型)或’\0’(字符型);
      每次调用它们所在的函数时,不再重新赋初值,只是保
      留上次调用结束时的值!
  • 局部静态变量值具有可继承性

  1. register 变量
    register 建议编译器尝试将变量存储在CPU寄存器中,以便快速访问。但这只是一个提示,编译器可以忽略它。

  2. extern声明外部变量
    外部变量是在函数的外部定义的全局变量,它的作用域
    是从变量的定义处开始,到本程序文件的末尾。在此作用域
    内,全局变量可以为程序中各个函数所引用。编译时将外部
    变量分配在静态存储区。
    有时需要用extern来声明外部变量,以扩展外部变量的作
    用城。

第七章 善于利用指针

  • 指针就是变量,用于存放地址的变量
  • 指针类型
    • 基本类型
    • 数组指针
    • 函数指针
  • 指针的解引用:取出指针指向地址的值
  • &a 是变量 a 的地址,也可称为变量 a 的指针。
  • 指针变量是存放地址的变量,也可以说,指针变量是存放指针的变量。
  • 指针变量的值是一个地址,也可以说,指针变量的值是一个指针。
  • 指针变量也可称为地址变量,它的值是地址。
  • & 是取地址运算符,&aa 的地址,也可以说,& 是取指针运算符。&aa 的指针(即指向变量 a 的指针)。
  • 数组名是一个地址,是数组首元素的地址,也可以说,数组名是一个指针,是数组首元素的指针。
  • 函数名是一个指针(指向函数代码区的首字节),也可以说函数名是一个地址(函数代码区首字节的地址)。
  • 函数的实参如果是数组名,传递给形参的是一个地址,也可以说,传递给形参的是一个指针。
#include <stdio.h>

int main() {
    int a = 10;       // 定义一个整型变量a
    int *ptr = &a;    // 定义一个指针变量ptr,指向变量a的地址

    printf("The value of a is %d\n", a);
    printf("The address of a is %p\n", (void*)&a);
    printf("The value of ptr (the pointer to a) is %p\n", (void*)ptr);
    printf("The value pointed by ptr is %d\n", *ptr);

    int arr[] = {1, 2, 3}; // 定义一个整型数组
    printf("The address of the first element of arr is %p\n", (void*)arr);
    printf("The first element of arr is %d\n", *arr);

    return 0;
}


// 输出
The value of a is 10
The address of a is 0x956f60
The value of ptr (the pointer to a) is 0x956f60
The value pointed by ptr is 10
The address of the first element of arr is 0x956e40
The first element of arr is 1

变量定义类型表示含义
int i;int定义整型变量i
int *p;int *定义p为指向整型数据的指针变量
int a[5];int[5]定义整型数组a,它有5个元素
int *p[4];int *[4]定义指针数组p,它由4个指向整型数据的指针元素组成
int (*p)[4];int (*)[4]p为指向包含4个元素的一维数组的指针变量
int f();int()f为返回整型函数值的函数
int *p();int*()p为返回一个指针的函数,该指针指向整型数据
int (*p)();int(*)()p为指向函数的指针,该函数返回一个整型值
int **p;int **p是一个指针变量,它指向一个指向整型数据的指针变量
void *p;void *p是一个指针变量,基类型为void(空类型),不指向具体的对象

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

1. 结构体(Structures)

结构体是C语言中一种允许用户自定义数据类型的机制。它可以用来存储不同类型的数据项。例如,一个结构体可以包含 intfloatchar类型的成员。结构体可以将多个不同类型的数据项组合成一个单一的单位。这在处理复杂数据时非常有用,比如当你需要一种类型来表示一个人的信息(包含姓名、年龄、身高等)时。

定义结构体

结构体通过 struct 关键字定义。结构体的定义包括结构体的名称和一系列成员(可以是不同类型的变量)。

// 一般形式
struct 结构体名{
	成员列表
}
struct Person {
    char name[50];
    int age;
    float height;
};

在这个例子中,Person 是结构体的名称,包含一个字符串 name、一个整数 age 和一个浮点数 height

声明结构体变量

一旦定义了结构体,就可以声明该类型的变量。

struct Person person1;

访问结构体成员

可以使用点号(.)操作符来访问结构体的成员。

person1.age = 30;
strcpy(person1.name, "Alice");
person1.height = 5.5;

结构体指针

也可以创建指向结构体的指针,并使用箭头(->)操作符来访问结构体成员。

struct Person *ptr;
ptr = &person1;
ptr->age = 30;

传递结构体

结构体可以作为函数的参数传递。可以传递结构体本身(这将会复制整个结构体)或结构体的指针。

结构体数组

就像基本数据类型一样,可以创建结构体的数组。

嵌套结构体

结构体可以包含其他结构体作为成员,这称为嵌套结构体。

typedef与结构体

使用 typedef 可以为结构体定义一个新的名称,简化代码。

typedef struct Person Person;
Person person1;

结构体在C语言中的应用非常广泛,它们是实现抽象数据类型(ADT)和组织复杂数据的强大工具。

2. 共用体(Unions)

共用体和结构体类似,但所有的成员共享同一块内存空间。这意味着在同一时间内,只能有效地使用其中一个成员。共用体的使用常常与特定类型的存储或转换有关。共用体(Unions)在C语言中是一种特殊的数据类型,允许在相同的内存位置存储不同的数据类型。在共用体中定义的所有成员共享同一块内存空间,这意味着任何一个成员的修改都会影响到其他成员。共用体的大小等于其最大成员的大小。

定义共用体

使用 union 关键字来定义共用体。例如:

union Data {
    int i;
    float f;
    char str[20];
};

在这个例子中,Data 是一个共用体,包含一个整数、一个浮点数和一个字符数组。

使用共用体

可以声明共用体变量,并给其中的成员赋值。但需注意,同一时间只能有效使用一个成员。

union Data data;
data.i = 10;
data.f = 220.5;  // 此时i的值被覆盖
strcpy(data.str, "C Programming");  // 此时f的值被覆盖

3. 枚举(Enumerations)

枚举类型是一种用户定义的数据类型,它允许为整数值指定更易读的符号名称,从而提高程序的可读性和可维护性。

定义枚举

使用 enum 关键字定义枚举。例如:

enum Color {RED, GREEN, BLUE};

在这个例子中,Color 是一个枚举类型,包含三个值:RED、GREEN和BLUE。

使用枚举

可以声明枚举类型的变量:

enum Color favoriteColor;
favoriteColor = RED;

4. typedef声明

typedef 用于为已有的数据类型创建一个新名称,从而提高代码的可读性和易于理解。

使用typedef

例如,可以为指针类型创建新的名称:

typedef int* intPtr;
intPtr ptr1, ptr2;

或者为结构体定义一个新名称:

typedef struct Person {
    char name[50];
    int age;
} Person;

Person person1;

第九章 对文件的输入输出

文件的基本概念

在计算机程序设计中,文件是一种用来永久存储数据的资源。文件存储在某种永久性存储设备上(如硬盘),它允许数据的存储和检索,这与内存中的临时数据存储形成对比。文件可用于存储各种类型的数据,如文本、图像、音频、视频等。

文件类型

  1. 文本文件:存储标准的文本信息,如.txt文件。它们通常可读并可由文本编辑器打开。
  2. 二进制文件:直接存储在磁盘上的二进制数据,如图像、视频文件等。这些文件通常不可读,必须使用特定的程序或函数进行处理。

文件的打开和关闭

  • 打开文件:在C语言中,使用 fopen 函数来打开一个文件。这个函数返回一个 FILE 类型的指针,如果文件无法打开,则返回 NULL
    FILE *fp;
    fp = fopen("file.txt", "r"); // "r" 表示以只读方式打开文件
    
  • 关闭文件:使用 fclose 函数来关闭一个打开的文件,释放资源。
    fclose(fp);
    

文件的读写操作

  • 写操作fprintf 函数用于向文本文件写入数据,而 fwrite 用于向二进制文件写入数据。
  • 读操作fscanf 函数用于从文本文件读取数据,而 fread 用于从二进制文件读取数据。

文件的其他操作

  • 定位函数fseek 函数用于在文件内移动文件指针到指定位置。
  • 错误处理:在进行文件操作时,检查相关函数的返回值是一个好习惯,可用于错误处理。
  • 文件的缓冲:文件操作通常涉及缓冲区,以提高读写效率。

文件与标准输入输出的关系

  • 文件操作在概念上类似于标准输入输出操作(如 printfscanf)。标准输入输出是特殊的文件流,分别指向标准输入设备(通常是键盘)和标准输出设备(通常是屏幕)。
  • 在C语言中,可以用类似的方式处理文件和标准输入输出,例如使用格式化的输入输出函数。

第十章 简单算法

简单排序算法

1. 冒泡排序

  • 冒泡排序(Bubble Sort):通过重复交换相邻逆序的元素,使得较小(或较大)的元素像泡泡一样浮到数组的一端。
void bubbleSort(int arr[], int n) {
    for (int i = 0; i < n-1; i++)    
        for (int j = 0; j < n-i-1; j++) 
            if (arr[j] > arr[j+1]) 
                swap(&arr[j], &arr[j+1]);
}

2. 选择排序

  • 选择排序(Selection Sort):不断选择剩余部分的最小(或最大)元素,放到已排序部分的末尾。
void selectionSort(int arr[], int n) {
    for (int i = 0; i < n-1; i++) {
        for (int j = i+1; j < n; j++) {
            if (arr[j] < arr[i]) {
				swap(&arr[i], &arr[j]);
            }
        }
    }
}

3. 插入排序

  • 插入排序(Insertion Sort):构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
void insertionSort(int arr[], int n) {
    int i, key, j;
    for (i = 1; i < n; i++) {
        key = arr[i];
        j = i - 1;
        while (j >= 0 && arr[j] > key) {
            arr[j + 1] = arr[j];
            j = j - 1;
        }
        arr[j + 1] = key;
    }
}

常考代码

  • -1和1 循环的项 如:编程计算sum =1-1/3+1/5-1/7+1/9……
    使用 int sign = 1;
    sign *= -1;
    (注意这里除数要使用浮点型)

  • 判断闰年
    if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0)
    return 1;

  • 逆序(回文)
    使用两个指针i,j i=0,j=n-1(n为长度),当i<j时,交换i,j的值

  • 44
    点赞
  • 51
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是第五版《代码答案形式的C语言程序设计》第八章的例题答案: 8.1 ```c #include <stdio.h> #define N 5 int main() { int a[N], max, i; printf("Enter array a:\n"); for (i = 0; i < N; i++) scanf("%d", &a[i]); max = a[0]; for (i = 1; i < N; i++) if (a[i] > max) max = a[i]; printf("max=%d\n", max); return 0; } ``` 8.2 ```c #include <stdio.h> #define N 5 int main() { int a[N], i; printf("Enter array a:\n"); for (i = 0; i < N; i++) scanf("%d", &a[i]); printf("Reverse array a:\n"); for (i = N - 1; i >= 0; i--) printf("%d ", a[i]); printf("\n"); return 0; } ``` 8.3 ```c #include <stdio.h> #define N 5 int main() { int a[N], i; printf("Enter array a:\n"); for (i = 0; i < N; i++) scanf("%d", &a[i]); printf("a[0]=%d, a[%d]=%d\n", a[0], N - 1, a[N - 1]); return 0; } ``` 8.4 ```c #include <stdio.h> #define N 5 int main() { int a[N], i, sum = 0; printf("Enter array a:\n"); for (i = 0; i < N; i++) scanf("%d", &a[i]); for (i = 0; i < N; i++) sum += a[i]; printf("sum=%d, average=%.2f\n", sum, (float)sum / N); return 0; } ``` 8.5 ```c #include <stdio.h> #define N 5 int main() { int a[N], i, temp; printf("Enter array a:\n"); for (i = 0; i < N; i++) scanf("%d", &a[i]); for (i = 0; i < N / 2; i++) { temp = a[i]; a[i] = a[N - i - 1]; a[N - i - 1] = temp; } printf("Reverse array a:\n"); for (i = 0; i < N; i++) printf("%d ", a[i]); printf("\n"); return 0; } ``` 8.6 ```c #include <stdio.h> #define N 5 int main() { int a[N], i, j, temp; printf("Enter array a:\n"); for (i = 0; i < N; i++) scanf("%d", &a[i]); for (i = 0; i < N - 1; i++) for (j = i + 1; j < N; j++) if (a[i] > a[j]) { temp = a[i]; a[i] = a[j]; a[j] = temp; } printf("Sort array a:\n"); for (i = 0; i < N; i++) printf("%d ", a[i]); printf("\n"); return 0; } ``` 8.7 ```c #include <stdio.h> #define N 5 int main() { int a[N], i, j, temp; printf("Enter array a:\n"); for (i = 0; i < N; i++) scanf("%d", &a[i]); for (i = 0; i < N - 1; i++) for (j = 0; j < N - i - 1; j++) if (a[j] > a[j + 1]) { temp = a[j]; a[j] = a[j + 1]; a[j + 1] = temp; } printf("Sort array a:\n"); for (i = 0; i < N; i++) printf("%d ", a[i]); printf("\n"); return 0; } ``` 8.8 ```c #include <stdio.h> #define N 5 int main() { int a[N], i; printf("Enter array a:\n"); for (i = 0; i < N; i++) scanf("%d", &a[i]); printf("Max 5 numbers in array a:\n"); for (i = 0; i < 5; i++) printf("%d ", a[i]); printf("\n"); return 0; } ``` 8.9 ```c #include <stdio.h> #define N 5 int main() { int a[N], i, j, temp; printf("Enter array a:\n"); for (i = 0; i < N; i++) scanf("%d", &a[i]); for (i = 0; i < N - 1; i++) for (j = 0; j < N - i - 1; j++) if (a[j] > a[j + 1]) { temp = a[j]; a[j] = a[j + 1]; a[j + 1] = temp; } printf("Median number in array a:\n"); printf("%d\n", a[N / 2]); return 0; } ``` 8.10 ```c #include <stdio.h> #define N 5 int main() { int a[N], i, j, temp; printf("Enter array a:\n"); for (i = 0; i < N; i++) scanf("%d", &a[i]); for (i = 0; i < N - 1; i++) for (j = 0; j < N - i - 1; j++) if (a[j] > a[j + 1]) { temp = a[j]; a[j] = a[j + 1]; a[j + 1] = temp; } printf("Mode number in array a:\n"); for (i = 0; i < N; i++) { int count = 0, k; for (j = i; j < N; j++) if (a[j] == a[i]) count++; for (k = i - 1; k >= 0; k--) if (a[k] == a[i]) break; if (k < 0 && count > 1) printf("%d ", a[i]); } printf("\n"); return 0; } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值