杰瑞科技汇

C程序设计教程习题及解答如何有效学习?

第一章:C语言概述与环境

核心知识点

  • C语言的特点:结构化、高级语言与低级语言结合、可移植性好等。
  • C程序的基本结构:main函数是程序的入口,由函数构成。
  • C程序的编译和执行过程:编辑 -> 编译 -> 链接 -> 运行。

典型习题

习题1: 编写一个简单的C程序,在屏幕上输出 "Hello, World!"。

C程序设计教程习题及解答如何有效学习?-图1
(图片来源网络,侵删)

解答:

#include <stdio.h> // 1. 包含标准输入输出库,提供 printf 函数的声明
// 2. 定义主函数,程序从这里开始执行
int main() {
    // 3. 调用 printf 函数,在屏幕上打印字符串
    // \n 是一个转义字符,表示换行
    printf("Hello, World!\n");
    // 4. main 函数返回 0,表示程序正常结束
    return 0;
}

解题思路与知识点:

  1. #include <stdio.h>:这是预处理器指令。stdio.h 是 "standard input output header" 的缩写,包含了与输入输出相关的函数(如 printf)的声明,没有它,编译器就不知道 printf 是什么。
  2. int main():每个C程序都必须有一个名为 main 的函数。int 表示这个函数执行完毕后会返回一个整数值。return 0; 是一个约定,返回0表示程序成功执行,没有发生错误。
  3. printf("Hello, World!\n");printf 是一个标准库函数,用于格式化输出,双引号内的字符串是它要打印的内容。\n 是一个换行符,它会让光标移动到下一行的开头,使下一次输出从新行开始。

第二章:数据类型、运算符与表达式

核心知识点

  • 基本数据类型int (整型), float (单精度浮点型), double (双精度浮点型), char (字符型)。
  • 常量与变量:常量的值不能改变,变量的值可以改变,使用变量前必须先声明。
  • 运算符
    • 算术运算符:, , , , (取模)。
    • 赋值运算符:, , 等。
    • 自增/自减:, (注意前置和后置的区别)。
    • 关系运算符:>, <, , 等。
    • 逻辑运算符:&& (与), (或), (非)。
  • 类型转换:自动类型转换(隐式)和强制类型转换(显式)。

典型习题

习题2: 编写程序,声明一个整型变量 a 和一个浮点型变量 b,给它们赋值,然后计算并输出它们的和、差、积、商。

解答:

C程序设计教程习题及解答如何有效学习?-图2
(图片来源网络,侵删)
#include <stdio.h>
int main() {
    // 声明并初始化变量
    int a = 10;
    float b = 3.14f; // f 后缀表示这是一个 float 常量
    // 计算并输出
    printf("a = %d, b = %f\n", a, b);
    printf("a + b = %f\n", (float)a + b); // 将 a 转换为 float 以得到精确结果
    printf("a - b = %f\n", (float)a - b);
    printf("a * b = %f\n", (float)a * b);
    printf("a / b = %f\n", (float)a / b); // 注意:整数除以浮点数结果是浮点数
    return 0;
}

解题思路与知识点:

  1. 变量声明与初始化int a = 10; 声明了一个整型变量 a 并赋初值为10。float b = 3.14f; 声明了一个浮点型变量 bf 后缀告诉编译器 14 是一个 float 类型,而不是默认的 double
  2. printf 的格式化输出%d 用于输出整型,%f 用于输出浮点型。printf 函数会按照格式字符串中的占位符顺序,用后面提供的变量值来替换它们。
  3. 类型转换:当 intfloat 进行算术运算时,int 类型的 a 会被自动提升float 类型,然后再进行计算,为了清晰和避免潜在的精度问题,我们使用 (float)a 进行强制类型转换,使代码意图更明确。

习题3: 分析下面代码的输出结果,并解释原因。

int main() {
    int i = 5;
    int j = ++i + i++;
    printf("i = %d, j = %d\n", i, j);
    return 0;
}

解答与解析:

输出结果:

C程序设计教程习题及解答如何有效学习?-图3
(图片来源网络,侵删)
i = 7, j = 12

解析(C语言标准规定: 一个变量在一个表达式中只能被求值一次,但编译器实现可能不同,此分析基于常见行为):

  1. int j = ++i + i++;:这一行是关键,涉及到副作用求值顺序
  2. ++i (前置自增)i 的值先自增1(变为6),然后这个新值6用于加法运算。
  3. i++ (后置自增)i 的当前值(现在是6)被用于加法运算,然后 i 的值再自增1(变为7)。
  4. 计算过程
    • ++i 的结果是 6
    • i++ 的结果是 6
    • j = 6 + 6 = 12
    • 在整个表达式计算完毕后,i 的值因为 i++ 而变成了 7
  5. i 的值为7,j 的值为12。

易错点:这种混合使用前置和后置自增/自减的表达式是C语言中著名的“坑”,在实际编程中,应尽量避免编写这样的代码,因为它可读性差,且在不同编译器下可能产生不同结果。


第三章:顺序与选择结构

核心知识点

  • if-else 语句:实现单分支、双分支或多分支选择结构。
  • switch 语句:基于一个变量的值进行多路分支。
  • 关系运算符与逻辑运算符的结合:构建复杂的条件表达式。

典型习题

习题4: 编写一个程序,要求用户输入一个整数,然后判断该数是正数、负数还是零,并输出相应的信息。

解答:

#include <stdio.h>
int main() {
    int num;
    // 提示用户输入
    printf("请输入一个整数: ");
    // 从键盘读取一个整数并存入 num 变量
    scanf("%d", &num);
    // 使用 if-else if-else 结构进行判断
    if (num > 0) {
        printf("%d 是一个正数,\n", num);
    } else if (num < 0) {
        printf("%d 是一个负数,\n", num);
    } else {
        printf("你输入的是零,\n");
    }
    return 0;
}

解题思路与知识点:

  1. scanf("%d", &num);scanf 是标准输入函数,用于从键盘读取数据。%d 表示要读取一个整数,&num 是变量 num 的地址,scanf 需要知道把读到的数据存放到哪里去。地址运算符 & 是初学者最容易忘记的。
  2. if-else if-else 结构:这是一个阶梯式的判断结构。if 的条件为真,则执行其后的代码块,并跳过所有 else ifelseif 为假,则判断 else if,以此类推,如果所有条件都为假,则执行 else 块,这确保了只有一个分支会被执行。

第四章:循环结构

核心知识点

  • for 循环:适用于循环次数已知的情况。
  • while 循环:适用于循环次数未知,但循环条件明确的情况。
  • do-while 循环:至少执行一次循环体,然后再判断条件。
  • breakcontinuebreak 用于跳出整个循环,continue 用于跳过本次循环的剩余语句,直接进入下一次循环。

典型习题

习题5: 使用 for 循环,计算并输出 1 到 100 之间所有偶数的和。

解答:

#include <stdio.h>
int main() {
    int sum = 0; // 用于存储累加和
    // for 循环:初始化 i=1; 条件 i<=100; 每次循环后 i++
    for (int i = 1; i <= 100; i++) {
        // 使用 if 语句判断 i 是否为偶数
        if (i % 2 == 0) {
            sum = sum + i; // 或者写成 sum += i;
        }
    }
    printf("1 到 100 之间所有偶数的和是: %d\n", sum);
    return 0;
}

解题思路与知识点:

  1. 循环变量初始化int i = 1; 在循环开始前定义并初始化计数器 i
  2. 循环条件i <= 100; 只要 i 小于或等于100,循环就会继续。
  3. 循环后操作i++ 每次循环结束后,i 的值增加1。
  4. 取模运算符 i % 2 计算 i 除以2的余数,如果余数为0,说明 i 能被2整除,就是偶数。
  5. 累加sum = sum + i; 是一个累加操作,每次将符合条件的 i 加到 sum 上。

第五章:数组

核心知识点

  • 一维数组:相同类型数据的线性集合,声明方式:类型 数组名[大小];
  • 数组下标:从0开始,到 大小-1 结束。
  • 数组初始化:可以在声明时直接初始化。
  • 字符串:以 '\0' (空字符) 结尾的字符数组。

典型习题

习题6: 定义一个包含10个整数的数组,使用循环为其赋值为1到10,然后逆序输出数组中的所有元素。

解答:

#include <stdio.h>
int main() {
    int arr[10]; // 声明一个有10个元素的整型数组
    // 使用 for 循环为数组赋值
    for (int i = 0; i < 10; i++) {
        arr[i] = i + 1; // 数组下标从0开始,arr[0] 存 1
    }
    printf("数组逆序输出: ");
    // 使用另一个 for 循环逆序输出
    for (int i = 9; i >= 0; i--) {
        printf("%d ", arr[i]);
    }
    printf("\n");
    return 0;
}

解题思路与知识点:

  1. 数组声明与下标int arr[10]; 创建了一个可以存放10个 int 的数组,第一个元素是 arr[0],最后一个元素是 arr[9]初学者最容易犯的错误是下标越界,例如访问 arr[10]
  2. 循环与数组for (int i = 0; i < 10; i++) 是遍历数组的经典模式。i 既作为循环计数器,也作为数组下标。
  3. 逆序遍历:第二个 for 循环的初始化部分 int i = 9;,条件 i >= 0;,以及递减 i--,共同实现了从数组末尾到开头的遍历。

第六章:函数

核心知识点

  • 函数定义返回类型 函数名(参数列表) { 函数体 }
  • 函数声明返回类型 函数名(参数列表); (告诉编译器这个函数存在)。
  • 函数调用函数名(实际参数);
  • 参数传递:C语言中,函数参数传递是值传递,函数内部无法修改主调函数中普通变量的值,但可以修改指针指向的值。

典型习题

习题7: 编写一个函数 is_prime(int num),用于判断一个数是否为素数(质数),然后在 main 函数中调用该函数,判断用户输入的数字。

解答:

#include <stdio.h>
#include <stdbool.h> // 为了使用 bool, true, false
// 函数声明
bool is_prime(int num);
int main() {
    int number;
    printf("请输入一个正整数: ");
    scanf("%d", &number);
    if (is_prime(number)) {
        printf("%d 是一个素数,\n", number);
    } else {
        printf("%d 不是一个素数,\n", number);
    }
    return 0;
}
// 函数定义:判断 num 是否为素数
bool is_prime(int num) {
    // 素数必须大于1
    if (num <= 1) {
        return false;
    }
    // 从 2 到 num-1 遍历,看是否有能整除 num 的数
    for (int i = 2; i < num; i++) {
        if (num % i == 0) {
            return false; // 如果找到,说明不是素数
        }
    }
    // 如果循环结束都没找到,说明是素数
    return true;
}

解题思路与知识点:

  1. 函数声明与定义分离bool is_prime(int num);main 函数之前声明,这样 main 函数在调用它时,编译器已经知道它的存在。
  2. #include <stdbool.h>:这是C99标准引入的头文件,提供了布尔类型 bool 以及其值 truefalse,使代码更易读。
  3. 函数实现逻辑
    • 小于等于1的数不是素数。
    • 从2开始到 num-1num 能被其中任何一个数整除,它就不是素数,函数立即返回 false
    • 如果循环正常结束,说明没有找到能整除它的数,它就是素数,返回 true
  4. main 函数中的调用if (is_prime(number)),函数调用可以放在任何表达式可以出现的地方,因为它返回一个 bool 值。

第七章:指针

核心知识点

  • 指针变量:存放内存地址的变量。
  • & (取地址运算符):获取变量的内存地址。
  • *`` (解引用/间接寻址运算符)**:获取指针所指向地址处的值。
  • 指针与数组:数组名在表达式中会“退化”为其首元素的地址,因此指针和数组关系密切。

典型习题

习题8: 使用指针,实现两个整数的值交换。

解答:

#include <stdio.h>
// 函数声明,参数是指针
void swap(int *ptr1, int *ptr2);
int main() {
    int a = 10, b = 20;
    printf("交换前: a = %d, b = %d\n", a, b);
    // 调用 swap 函数,传入 a 和 b 的地址
    swap(&a, &b);
    printf("交换后: a = %d, b = %d\n", a, b);
    return 0;
}
// 函数定义:交换两个指针所指向的整数的值
void swap(int *ptr1, int *ptr2) {
    int temp;
    temp = *ptr1; // 将 ptr1 指向的值(a的值)存入 temp
    *ptr1 = *ptr2; // 将 ptr2 指向的值(b的值)赋给 ptr1 指向的内存(a的内存)
    *ptr2 = temp; // 将 temp 中原来的值(a的值)赋给 ptr2 指向的内存(b的内存)
}

解题思路与知识点:

  1. 为什么必须用指针? 因为C语言是值传递swap 函数的参数是 int a, int b,那么函数内部交换的是 ab副本,对 main 函数中的原始变量没有影响。
  2. 传递地址:通过传递 &a&bab 的地址),swap 函数中的指针 ptr1ptr2 就能够直接访问到 main 函数中 ab 所在的内存空间。
  3. 解引用操作*ptr1 的意思是“获取 ptr1 这个地址上存放的值”,通过修改这个值,就等于修改了 main 函数中 a 的值。

第八章:结构体

核心知识点

  • 结构体:将不同类型的数据组合成一个有机的整体。struct 是关键字。
  • 结构体成员访问:使用点运算符 。student.name
  • 结构体指针访问成员:使用箭头运算符 ->p_student->name

典型习题

习题9: 定义一个结构体 Student,包含学号(id)、姓名(name)和成绩(score),声明一个 Student 类型的变量,并为它的成员赋值,然后输出。

解答:

#include <stdio.h>
#include <string.h> // 为了使用 strcpy
// 1. 定义 Student 结构体
struct Student {
    int id;
    char name[50];
    float score;
};
int main() {
    // 2. 声明一个 Student 类型的变量 s1
    struct Student s1;
    // 3. 为成员赋值
    s1.id = 1001;
    // 不能直接用 s1.name = "Zhang San";,因为 name 是字符数组
    // 需要使用 strcpy 函数来复制字符串
    strcpy(s1.name, "Zhang San");
    s1.score = 95.5f;
    // 4. 输出结构体成员的值
    printf("学号: %d\n", s1.id);
    printf("姓名: %s\n", s1.name);
    printf("成绩: %.1f\n", s1.score); // %.1f 表示输出一位小数
    return 0;
}

解题思路与知识点:

  1. struct 关键字:定义结构体类型时必须使用 struct Student
  2. 字符串赋值:C语言中没有字符串类型,字符串是用字符数组表示的,不能直接用赋值符 给字符数组赋值,必须使用 strcpy (string copy) 函数从字符串常量复制到字符数组中。
  3. 成员访问:使用 运算符来访问结构体变量的成员。s1.ids1.names1.score

总结与学习建议

  1. 多动手敲代码:理论看懂了不代表会写,一定要亲自把每个例子敲一遍,修改参数,观察结果变化。
  2. 调试是关键:学会使用调试器(如 GDB, VS Code 的调试功能)或简单的 printf 语句来跟踪程序执行过程和变量值的变化,这是解决 Bug 的最有效方法。
  3. 理解底层原理:特别是指针、内存、函数调用栈等概念,理解了它们,C语言的水平会有质的飞跃。
  4. 多读优秀代码:阅读一些开源的、高质量的C语言项目,学习别人的编程风格和解决问题的思路。
  5. 循序渐进:不要急于求成,把基础(数据类型、流程控制、函数)打牢,再学习指针、结构体等高级内容。

希望这份详细的习题解答对你有帮助!祝你学习顺利!

分享:
扫描分享到社交APP
上一篇
下一篇