📑 本页目录

计算机系统 · 全书复习 · 第一讲

把八章知识
接成一条完整的线从一条程序,到一盏LED

知道每章讲过什么,还要知道它们怎样合作。跟着“每500ms翻转一次LED”,把变量、指令、存储、总线、寄存器与电路逐个接起来。

前八章整理完成后的第一轮跨章复习。集中解决对象混淆、访问链路、计时与中断,不增加原书章节编号。原书全部内容与习题仍在各章讲义中。

13节串讲7幅交互SVG12道原创复习题完整应用源码

C源文件 → 链接映像 → 复位启动 → 指令与总线 → GPIO → 电路与时间

下载本讲源码

返回计算机系统全书目录 → 回到第八章 →

先从八章的分工开始 →

全书复习 · 第一讲 · 第01节

先把八章放到同一张图上

掌握要求:能说清每一章解决什么问题,再判断题目需要哪些章一起用。

前八章已经讲完。这一讲开始做跨章复习:跟着一条“让LED闪烁”的程序,把源码、存储、指令、总线、时钟、GPIO和中断接起来。 先把这条线讲通,后面再集中做存储、寻址与通信综合题。

原章 它解决什么问题 做题必须留下的能力
第一、二章 计算机由什么组成,信息怎样表示,指令怎样执行 认清CPU、存储、I/O;会算编码、容量、地址和基本执行过程
第三章 数据放在哪里,容量/速度怎样兼顾,怎样找到它 容量与地址范围、片选、存储层次;理解Cache和虚拟存储各自作用
第四章 CPU与外设怎样交换数据,谁在何时占用通路 总线事务、地址译码、轮询/中断/DMA的区别
第五章 ARM程序能看到哪些寄存器、状态和异常机制 区分通用寄存器、SP/LR/PC、状态寄存器与外设寄存器
第六章 每条ARM指令具体做了什么 寻址、数据宽度、条件、移位、算术与访存效果
第七章 怎样把指令组织成能正确合作的程序 分支/循环、子程序、栈、接口约定、C与汇编协作
第八章 怎样让这些能力控制实际芯片与电路 型号、引脚、时钟、配置顺序、状态/中断及边界条件
复习图1-1蓝色为选中阶段;箭头表示主要因果路径,不能省略时钟、模式和接线前提。

重要程度分三层。 第一层要能独立计算和写出来:位操作、地址/宽度、时钟/定时、分支/循环、栈和外设配置。第二层要能解释因果:为何中断能打断、为何需要启动初始化、为何DMA仍需CPU配置。第三层认识用途再查表:旧工具名、冷门模式、特定芯片的完整外设清单。

这里再讲背景,是为了让你能判断题目用了哪个模型。比如“有Cache的通用计算机”和“本讲的F103裸机”条件不同,不能把一种平台的假设强行放进另一种平台。

全书复习 · 第一讲 · 第02节

先纠正对象:CPU寄存器、RAM变量和外设寄存器

掌握要求:看到R0、一个C变量、GPIOA->BSRR时,能分别说出它们是什么。

CPU里的R0与GPIO外设里的ODR/BSRR都叫寄存器,但属于不同位置、不同访问方式。

对象 在哪里 怎样使用 典型题目
R0—R12、SP、LR、PC 处理器可见寄存器组 指令直接指定寄存器操作数 执行结果、参数传递、栈和返回
volatile uint32_t ticks 通常位于SRAM,具体取决于存储布局 地址加读写指令;volatile限制相关优化 中断共享、变量初值、内存布局
GPIO配置/数据寄存器 MCU片上外设模块,映射到地址空间 对指定地址发起访问,硬件解释读写 配置引脚、读按键、控制输出

普通C变量也可能被优化到CPU寄存器、被常量折叠甚至消失,所以“写了一个变量就必然有一个固定RAM格子”并不成立。本讲的中断共享计数器明确用volatile,并依赖其32位对齐与目标平台访问特性。

复习图1-2数值和指令相同,目标地址及硬件解释不同。GPIOA时钟和相应模式假定已配置。

做题先问:我在改哪个对象? MOVS R0,#1改R0;对SRAM地址做STR改相应内存;对BSRR地址做STR则向GPIO发出置位/复位命令。写指令同为STR,不代表目标的硬件语义相同。

全书复习 · 第一讲 · 第03节

从C源文件到Flash:编译、链接各负责什么

掌握要求:区分源文件、目标文件、链接映像、运行地址;知道初始化数据为什么可能有两份位置。

源文件先被编译/汇编成目标文件,再由链接器组合代码、常量、数据和符号,最后生成可下载映像。链接器结合存储布局决定各段和符号地址,下载工具把相应内容写进Flash等目标存储。

在典型MCU布局中:程序与常量主要在Flash;需要修改的数据主要在SRAM;有初值的可写数据可以在Flash保存初值副本,启动时复制到SRAM;零初始化数据按运行库规则清零。栈用于活动调用与局部存储,堆是否使用由程序和运行库决定。

uint32_t count = 7;
uint32_t total;

在常见链接布局中,count有Flash中的初值与SRAM中的运行位置;total的静态零初值由启动过程建立。Flash里存着初值,不等于CPU以后修改count也在改Flash。 不同工具和布局可能改变细节,题目给了段表就按段表判断。

编译器不会自动知道你的LED接哪一脚、哪个电平亮。它按代码和目标规则生成程序;引脚、时钟和电气仍由系统设计与初始化保证。这就是为什么第七章之后还必须学第八章。

全书复习 · 第一讲 · 第04节

从复位到main:第一条入口不是凭空出现的

掌握要求:能解释启动来源、向量表前两个字和Reset_Handler的职责。

F103硬件复位先使用HSI。BOOT引脚选择用户Flash、系统存储器或SRAM作为启动来源;启动代码随后可以按配置切换时钟。硬件默认时钟与main开始时的时钟,可能不同。

在Cortex-M的复位模型中,向量表首字用于初始MSP,第二字提供复位入口指针。以用户Flash映像位于0x08000000的典型布局为例,映像前两个字分别在0x08000000和0x08000004;复位时的启动映射还要结合BOOT理解。

例如第二字存0x08000101,表示Thumb入口,实际指令地址为0x08000100。最低位的状态标记与真正的指令地址要分清。不能把M系列向量表概括成“一排B指令”。

Reset_Handler和运行库建立必要环境,例如数据初始化、栈与系统初始化,然后进入main。具体执行顺序以匹配的启动模板为准。ENTRY、END、C的main与硬件复位向量,分属不同层次。

这一段考试常考过程与地址;实际调试时则用来区分“根本没进main”与“进了main但外设没配好”。不要求你无提示重写完整C运行库。

全书复习 · 第一讲 · 第05节

一次写GPIO,如何落到指令与总线事务

掌握要求:把C表达式拆成值、地址、访问宽度与硬件作用。

对本讲F103而言,GPIOA的BSRR地址为0x40010810。下面的语句向它写入bit24:

GPIOA->BSRR = 1u << (8u + 16u);

BSRR低16位写1对应置高,高16位写1对应复位到低;所以这次作用于PA8,将输出锁存值清为0。写入值为0x01000000。

用第六、七章的汇编表示,可以写成这样的教学示意:

    LDR R0, =0x40010810   ; 地址:GPIOA_BSRR
    LDR R1, =0x01000000   ; 命令:复位PA8
    STR R1, [R0]         ; 对该地址执行字写

这里LDR带等号是装载常量的伪指令写法;确切机器指令和常量池由汇编器处理。这不是承诺编译器必然把上面的C编成这三行。 它只把地址和值的职责展开给你看。

处理器发出地址与数据访问,片上总线互连/桥接/译码将它送到GPIO模块。地址决定选哪个目标,数据决定写什么,访问类型与宽度决定这次事务怎样进行。此处不需要操作系统系统调用。

本讲按F103裸机地址分析,不经过操作系统页表转换。第三章的Cache与虚拟存储仍要学:它们解释其他系统怎样处理速度与地址映射;其存在与细节由具体平台决定。

全书复习 · 第一讲 · 第06节

位操作要写成规则,别背一个神秘十六进制数

掌握要求:会计算BSRR命令、配置字段掩码,区分普通存储与命令寄存器。

对第p脚,BSRR置高写1u << p;复位到低写1u << (p + 16u)。它们是写命令,不是把一个变量原值读出来改一改。

复习图1-3上半区写1复位、下半区写1置位;0表示本次不动作,不表示把对应脚写低。

如果目标是PA8的配置字段,PA8在CRH,字段为bit3:0。普通推挽、2MHz输出的4位编码为0010,可写成:

GPIOA->CRH = (GPIOA->CRH & ~0xFu) | 0x2u;

这是“清目标字段,再写新字段”,其他引脚配置保留。若题目改成PC6,则字段在CRL bit27:24;50MHz普通推挽编码0011,写入字段值为0x03000000,清位掩码为0x0F000000。

三种寄存器不能混成一个模板: 普通配置字段常用读改写;BSRR用直接命令写;EXTI_PR是写1清对应挂起位,也直接写目标掩码。对PR使用|=会把读到的其他1一并写回,从而可能误清别的来源。

原子地设置/复位GPIO目标位,不能自动证明整个算法并发正确;别处若仍然整字重写ODR,也需要统一访问约定。

全书复习 · 第一讲 · 第07节

从引脚电平到LED亮灭:电路补上最后一步

掌握要求:能根据接线判断有效电平;不把高电平与“亮”画等号。

本讲LED接法:3.3V → 510Ω限流电阻 → LED → PA8。GPIO输出低,电流从电源经电阻、LED流入PA8,LED亮;GPIO输出高时,通常没有足够正向压差,LED灭。

复习图1-4A为阳极、K为阴极,右侧箭头表示发光。连接为3.3V、电阻、LED、PA8;示意省略供电去耦等电路。

如果换成“PA8→电阻→LED→地”,有效电平会变成高。题目中的亮灭结果必须按电路判断,不能只看函数名SetBits。

推挽输出可以主动拉高或拉低;开漏写1表示释放引脚,需要外部上拉等条件才能建立高电平,输入读值还能被外部拉低。输入模式、输出模式、普通GPIO、复用功能也必须分清。

2MHz/10MHz/50MHz输出档描述相应驱动/边沿能力设置,不表示GPIO会自己周期输出该频率。周期波形需要程序翻转或定时器等硬件功能。限流电阻保证实际电流受控;MCU脚不能直接承担任意功率负载。

全书复习 · 第一讲 · 第08节

定时器先算时钟,再算寄存器

掌握要求:独立算出更新周期,并分清一次翻转与一次完整闪烁周期。

向上计数、边沿对齐、标准更新模型下:

Tupdate=(PSC+1)(ARR+1)fTIM

fTIM是该定时器真正的输入时钟,不必等于SYSCLK,也不总等于PCLK。 F103在APB预分频为1时,相关TIM通常取PCLK;分频不为1时取2×PCLK。USART2不会因此也自动倍频。

例如SYSCLK=HCLK=72MHz,APB1除2:PCLK1=36MHz,TIM2=72MHz。若PSC=35999、ARR=999,则更新周期0.5秒。若每次更新翻转LED,亮0.5秒、灭0.5秒,完整周期1秒,频率1Hz。

复习图1-5向上计数模型。一次翻转只完成高低电平切换,两次翻转才形成一个完整周期。

本讲完整程序沿用第八章源码策略,主动切换HSI 8MHz,并设AHB/APB无分频。TIM2设PSC=7、ARR=999,得到1ms更新;主循环累计到500ms翻灯。因此程序里的PSC与上面72MHz直算例不同,结果仍一致。

PSC通常有缓冲,写入后还要按规则使新参数生效。初始化可以生成UG,再清掉初始化产生的UIF,最后使能中断和计数。不要刚开NVIC就把一次初始化更新误认成已经过了完整周期。

全书复习 · 第一讲 · 第09节

中断负责报时,主循环负责决定动作

掌握要求:能从一个时间轴解释ISR、共享计数与业务逻辑;知道volatile的边界。

定时器每1ms更新一次,置UIF;外设UIE和NVIC允许,且内核屏蔽/优先级条件满足时,进入TIM2_IRQHandler。ISR清本次源并给ticks加1,然后尽快返回。

复习图1-6以ticks从499增至500为例,假定主循环上次起点0。硬件基本帧之外可能有对齐与软件栈消耗。

从普通线程进入一个基本IRQ时,Cortex-M硬件基本帧保存R0—R3、R12、LR、PC、xPSR,共8个字;栈对齐可能另有填充,编译器也会根据ISR用到的寄存器生成必要的软件保护。不能把基本帧32字节当作所有ISR实际只消耗32字节栈。

主循环不断读取ticks,计算无符号差值是否达到500。它不用在ISR里等待500ms,也不用靠“执行若干条空指令”猜时间。

now = ticks;
if ((uint32_t)(now - since) >= 500u) {
    since = now;
    on ^= 1u;
    GPIOA->BSRR = on ? (1u << 24) : (1u << 8);
}
复习图1-7初始灭:0—500ms灭,500—1000ms亮,再循环;标注值为翻转完成后的理想状态,忽略响应延迟。

无符号减法可在合理检查周期内处理计数回绕。例如since=0xFFFFFF00、now=0x000000F4,差值正好500。前提是仍能区分要测的时间间隔,不能隔了完整2³²ms甚至多圈后再猜经过了多久。

volatile使相关访问按目标语言/实现规则保留,不等于互斥锁,也不自动让count++成为不可打断的读改写。 本例只有ISR更新32位对齐ticks,主循环只做32位读取;若两边都更新复合状态,就要重新分析临界区。

全书复习 · 第一讲 · 第10节

完整应用源码:先说清构建条件,再看main

掌握要求:能按文件和时钟条件构建,修改周期,并解释每段代码服务于哪个环节。

下面给出一个完整应用源文件。目标固定STM32F103RCT6,采用匹配的官方CMSIS设备头、system文件和高密度启动模板,向量中TIM2入口需指向本文件的TIM2_IRQHandler;避免模板另有同名ISR。文件不捆绑厂商设备包。官方模板在进入main之前可能配置过时钟,本文件随后显式切回HSI 8MHz。

连接使用第07节低有效LED,仅PA8。应用采用HSI 8MHz、AHB/APB无分频,TIM2产生1ms节拍。HSI有容差,程序用于解释原理与练习配置,不承诺晶体级计时精度。

/* STM32F103RC; official CMSIS/device startup environment required. */
#include "stm32f10x.h"
#include <stdint.h>

static volatile uint32_t ticks;

static int clock_hsi_8mhz(void)
{
    uint32_t guard = 1000000u;
    RCC->CR |= 1u;                       /* HSION */
    while (!(RCC->CR & 2u) && --guard) { }
    if (!guard) return 0;
    RCC->CFGR &= ~3u;                    /* SW = HSI */
    guard = 1000000u;
    while ((RCC->CFGR & 12u) && --guard) { }
    if (!guard) return 0;                /* wait for SWS = HSI */
    RCC->CFGR &= ~((15u << 4) | (7u << 8) | (7u << 11));
    RCC->CR &= ~(1u << 24);              /* PLL no longer the source */
    SystemCoreClock = 8000000u;
    return 1;
}

static void led_init(void)
{
    RCC->APB2ENR |= 1u << 2;             /* GPIOA clock */
    (void)RCC->APB2ENR;                  /* allow clock enable to propagate */
    GPIOA->BSRR = 1u << 8;               /* inactive high before output */
    GPIOA->CRH = (GPIOA->CRH & ~15u) | 2u; /* PA8: PP, 2MHz */
}

static void tick_init(void)
{
    RCC->APB1ENR |= 1u;                  /* TIM2 clock */
    (void)RCC->APB1ENR;
    TIM2->CR1 = 0;
    TIM2->DIER = 0;
    TIM2->PSC = 7;
    TIM2->ARR = 999;                     /* 8MHz / 8 / 1000 = 1kHz */
    TIM2->EGR = 1;                       /* UG loads the prescaler */
    TIM2->SR = 0;                        /* discard initialization UIF */
    TIM2->CNT = 0;
    ticks = 0;
    NVIC_SetPriorityGrouping(3);
    NVIC_SetPriority(TIM2_IRQn, 2);
    NVIC_ClearPendingIRQ(TIM2_IRQn);
    NVIC_EnableIRQ(TIM2_IRQn);
    TIM2->DIER = 1;                      /* UIE */
    TIM2->CR1 = 1;                       /* CEN */
}

void TIM2_IRQHandler(void)
{
    if (TIM2->SR & 1u) {
        TIM2->SR = (uint16_t)~1u;        /* write 0 to clear UIF only */
        ++ticks;
    }
}

int main(void)
{
    uint32_t since, now;
    unsigned on = 0;
    if (!clock_hsi_8mhz()) for (;;) { }  /* bounded startup failed */
    led_init();
    tick_init();
    since = ticks;
    for (;;) {
        now = ticks;
        if ((uint32_t)(now - since) >= 500u) {
            since = now;
            on ^= 1u;
            GPIOA->BSRR = on ? (1u << 24) : (1u << 8);
        }
    }
}

改成亮1秒、灭1秒: 在保持1ms节拍的前提下,把主循环阈值500改1000,整周期变2秒、频率0.5Hz。改成1kHz PWM: 需要重新选择定时器输出通道、复用引脚、PSC/ARR/CCR和输出使能;仅把这个阈值改小,不能声称已经配置了硬件PWM。

程序用主循环观察节拍,因此实际翻转时刻还包括服务与主循环响应延迟。若主循环停顿很久,本例只翻转一次并从当前时刻重新计时;它不会追赶过去漏掉的所有翻转。严格边沿时序应考虑定时器硬件输出和相应误差分析。

全书复习 · 第一讲 · 第11节

出题人换条件时,你该改哪一层

掌握要求:面对变式先定位条件,不把前一题答案整段照搬。

题目变动 应先修改/重算的层 最常见的错法
LED改成高有效 接线与输出逻辑 仍把BSRR复位当作灭
PA8改PC6 端口时钟、CRL字段、输出掩码、接线 只换GPIOA为GPIOC,仍写第8位
8MHz改成72MHz Flash/PLL/总线设置、fTIM与PSC/ARR 只改SystemCoreClock变量
每500ms翻转改为500ms完整周期 事件与波形周期的关系 仍每500ms只翻转一次
按键能立即改变状态 输入上/下拉、EXTI来源、消抖、事件传递与状态机 在长延时后才读按键
ISR和主循环共同改计数 并发约定、临界区与恢复旧屏蔽状态 只加volatile就认为没有竞争
改成DMA搬数据 外设/DMA配置、存储区、计数、触发与完成/错误处理 认为CPU从此完全不参与
给出带Cache/虚拟地址的另一平台 该平台存储与地址模型 直接套F103裸机地址结论

排错也按链路切开: 能否稳定复位和进main;时钟源/分频是否真生效;GPIO时钟和模式是否正确;BSRR值是否作用于正确脚;TIM的CNT是否推进、UIF是否出现;NVIC是否允许且向量正确;ticks是否变化;主循环是否满足条件;最后查实际引脚与LED接线。

这些检查能区分程序层、外设层与电路层,不需要先背几十个不相关函数名。观察寄存器时还要记住:有些状态通过读/写特定寄存器会被清除,调试器观察也可能产生影响,应查清语义。

全书复习 · 第一讲 · 第12节

先做这十二题,再看答案

掌握要求:能把本讲主线变成独立判断,而不是看到答案才觉得熟悉。

下面是本讲原创复习题,原书题仍在对应八章讲义中。建议先写下计算过程,再展开答案。

复习1.1 / 三个“寄存器”

R0、SRAM中的ticks、GPIOA_BSRR分别属于哪里?都能用MOVS R0,#1直接修改吗?

展开答案与判断理由

R0属于处理器寄存器组;ticks是按本例布局分配的SRAM对象;BSRR属于地址映射的GPIO外设。MOVS只改所指定的CPU寄存器,后两者需按地址访问,且外设另有语义。

复习1.2 / 初值放哪里

典型MCU布局中count=7的初值在Flash,运行时把count改成8,是改Flash还是SRAM?

展开答案与判断理由

修改运行位置,通常是SRAM。Flash可保存下载映像中的初值副本,启动时复制到SRAM;具体按链接布局判断。

复习1.3 / 复位向量

向量第二字为0x08000101,真正指令地址是什么?第一字有什么用途?

展开答案与判断理由

实际指令地址0x08000100,最低位指示Thumb入口;首字用于建立初始MSP。向量位置存入口值,与入口处的指令区分。

复习1.4 / BSRR命令

PA8低有效LED,向BSRR写0x01000000,LED亮还是灭?若改成高有效接法呢?

展开答案与判断理由

bit24复位PA8到低。按本讲低有效接法亮;高有效接法低则灭。结果必须结合电路,不能从“复位”一词猜。

复习1.5 / 字段掩码

PC6设50MHz普通推挽,要修改CRL还是CRH?清位掩码、写入值各是多少?

展开答案与判断理由

CRL bit27:24;清字段用~0x0F000000,字段新值0x03000000。最终(old & ~0x0F000000) | 0x03000000;另需GPIOC时钟和合适锁存初值。

复习1.6 / 总线与硬件语义

同样STR一字到SRAM地址与BSRR地址,为什么结果不能都理解成“保存一个变量”?

展开答案与判断理由

地址译码选中了不同目标。SRAM保存数据;BSRR写入由GPIO解释为置位/复位命令。访问宽度、权限、时钟与寄存器读写语义仍需符合目标规则。

复习1.7 / APB与TIM

HCLK72MHz,APB1除2,TIM2和USART2各是多少MHz?

展开答案与判断理由

PCLK1=36MHz;相关TIM在APB分频不为1时取2×PCLK,故TIM2=72MHz;USART2仍36MHz。

复习1.8 / 闪烁的二倍关系

TIM2时钟72MHz,PSC35999、ARR999,每更新翻转一次LED,更新周期、闪烁完整周期和频率是多少?

展开答案与判断理由

更新周期(36000×1000)/72000000=0.5s;两次翻转形成完整亮灭周期1s;闪烁频率1Hz。

复习1.9 / 1ms节拍

本讲8MHz程序为什么使用PSC7、ARR999?若不改其他条件,把500改成1000会怎样?

展开答案与判断理由

8MHz/(7+1)/(999+1)=1000Hz,节拍1ms。阈值1000表示每1s翻转,完整周期2s,频率0.5Hz。

复习1.10 / 回绕

since=0xFFFFFF00,now=0x000000F4,32位无符号差是多少?是否到500ms?

展开答案与判断理由

0x1F4=500;按本讲1ms计数且检查间隔满足范围条件,已经达到阈值。不能只做有符号比较或因now小于since就判错误。

复习1.11 / 中断与volatile

volatile能保证ISR与主循环同时执行count++时不丢更新吗?基本硬件异常帧32字节,是否代表ISR只用32字节栈?

展开答案与判断理由

都不能。count++是复合读改写,需要分析临界区/所有权;硬件基本帧之外还可能有对齐填充、软件保存、局部变量和嵌套等栈消耗。

复习1.12 / 定位故障

ticks不断增加、主循环每500ms写BSRR,但LED一直不亮,最先核对哪些条件?

展开答案与判断理由

核对写入作用于哪一脚/哪一电平,GPIO时钟与CRH模式、复用冲突、实际引脚电平、LED极性与接线/限流/供电。既然ticks已变化,不应先认定“中断根本没进”;仍需结合实际观测定位。

全书复习 · 第一讲 · 第13节

这一讲过关后,下一轮怎么学

掌握要求:知道已经掌握什么,知道下一轮该集中补什么。

本讲过关标准:能从“每500ms翻灯”追到时钟、寄存器、指令、总线和接线;给你换芯片时钟、引脚、有效电平或周期定义,知道该改哪里;看见SRAM变量、CPU寄存器、外设寄存器时不再混在一起。

下一轮集中做第三、六、七章的连接:容量与地址范围 → 小端字节 → LDR/STR宽度 → 基址/偏移/数组 → 栈与参数。 它们是算错地址、读错数、写错程序的共同来源。本讲先把系统主线跑通,不在同一讲再塞进所有Cache映射与页表计算。

需要回看的章节:第一、二章补信息表示和执行过程;第三章补存储;第四章补总线/中断/DMA;第五章补寄存器与异常;第六章补指令/寻址;第七章补程序/ABI;第八章补型号、时钟和外设。正文每一节都有相应的掌握要求,按错题返回即可。

学习依据与验证范围

按本项目计算机系统第一至第八章讲义重新组织跨章复习。本讲习题为原创复习题;原书习题和全部内容覆盖表保留在各章讲义中。

具体芯片采用STM32F103RCT6;核与外设定义可核对ST官方CMSIS设备头和RM0008参考手册。应用代码依赖匹配的官方启动/设备环境,未声称完成目标工程构建或实板验证。主机逻辑与网页交互检查不验证电气和外设真实时序。

交互SVG、默认静态图、公式、答案和源码下载均内嵌;离线阅读不依赖外部网络。打印展开所有答案。