开学三周,王爽的《汇编语言》(第三版)总算是基本上看完了,本文是总结的第一部分,包括了书上所有的检测点答案

第一章 基础知识

检测点 1.1

  1. 1个CPU的寻址能力为8KB,那么它的地址总线的宽度为____。
    13
    解析:CPU在内存中寻址的最小单位是Byte(字节),8KB = 2^13B,因此地址总线的宽度为13.
  2. 1KB的存储器有____个存储单元。存储单元的编号从____到____。
    1024 0 1023
  3. 1KB的存储器可以存储____个bit,____个Byte。
    2^13 2^10
  4. 1GB、1MB、1KB分别是____________Byte
    2^30 2^20 2^10
  5. 8080、8088、80286、80386的地址总线宽度分别为16根、20根、24根、32根,则他们的寻址能力分别为____(KB)、____(MB)、____(MB)、____(GB)。
    64 1 16 4
  6. 8080、8088、8086、80286、80386的数据总线宽度分别为8根、8根、16根、16根、32根。则它们一次可以传送的数据为:____(B)、____(B)、____(B)、____(B)、____(B)。
    1 1 2 2 4
  7. 从内存中读取1024字节的数据,8086至少要读取____次,80386至少要读取____次。
    512 256
  8. 在存储器中,数据和程序以____形式存放。
    二进制

第二章 寄存器

检测点 2.1

  1. 写出每条汇编指令执行后相关寄存器中的值
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    mov ax, 62627		AX =
    mov ah, 31H AX =
    mov al, 23H AX =
    add ax, ax AX =
    mov bx, 826CH BX =
    mov cx, ax CX =
    mov ax, bx AX =
    add ax, bx AX =
    mov al, bh AX =
    mov ah, bl AX =
    add ah, ah AX =
    add al, 6 AX =
    add al, al AX =
    mov ax, cx AX =
    F4A3H 31A3H 3123H 6246H 826CH 6246H 826CH 04D8H 0482H 6C82H D882H D888H D810H 6246H
  2. 只能使用目前学过的汇编指令,最多使用4条指令,编程计算2的4次方。
    1
    2
    3
    4
    mov ax, 2
    add ax, ax
    add ax, ax
    add ax, ax

检测点 2.2

  1. 给定段地址为0001H,仅通过变化偏移地址寻址,CPU的寻址范围为_____到_____。
    00010H到1000FH
  2. 有一数据存放在内存20000H的单元中,现给定段地址为SA,若想用偏移地址寻到此单元。则SA应满足的条件是:最小为_____,最大为_____。提示,反过来思考一下,当段地址给定为多少,CPU无论怎么变化偏移地址都无法寻到20000H单元?
    1001F 2000H
    20000H - 0FFFFH = 10001H
    20000H - 00000H = 20000H

检测点 2.3

下面的3条指令执行后,CPU几次修改IP?都是在什么时候?最后IP中的值是多少?

1
2
3
mov ax, bx
sub ax, ax
jmp ax

修改了四次:

  1. 第1条指令执行后,IP指向第2条指令
  2. 第2条指令执行后,IP指向第3条指令
  3. 第3条指令执行后,IP指向第4条指令
  4. JMP指令执行后,IP重新指向第1条指令

第三章 寄存器(内存访问)

检测点 3.1

  1. 在Debug中,用“d 0:0 1f”查看内存,结果如下。

    1
    2
    0000:0000 70 80 F0 30 EF 60 30 E2-00 80 80 12 66 20 22 60
    0000:0010 62 26 E6 D6 CC 2E 3C 3B-AB BA 00 00 26 06 66 88

    下面的程序执行前,AX=0,BX=0,写出每条汇编指令执行完后相关寄存器中的值。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    mov ax, 1
    mov ds, ax
    mov ax, [0000] AX =
    mov bx, [0001] BX =
    mov ax, bx AX =
    mov ax, [0000] AX =
    mov bx, [0002] BX =
    add ax, bx AX =
    add ax, [0004] AX =
    mov ax, 0 AX =
    mov al, [0002] AX =
    mov bx, 0 BX =
    mov bl, [000C] BX =
    add al, bl AX =

    提示,注意ds的设置。

    2662H E626H E626H 2662H D6E6H FD48H 2C14H 0 00E6H 0 0026H 000CH

  2. 内存中的情况如图3.6所示。
    各寄存器的初始值:CS=2000H,IP=0,DS=1000H,AX=0,BX=0;
    (1)写出CPU执行的指令序列(用汇编指令写出)
    (2)写出CPU执行每条指令后,CS、IP和相关寄存器中的数值。
    (3)再次体会:数据和程序有区别吗?如何确定内存中的信息哪些是数据,哪些是程序?
指令 CS:IP DS AX BX
mov ax, 6622H 2000:3 1000H 6622H 0
jmp 0ff0:0100 2000:8->0ff0:0100 1000H 6622H 0
mov ax, 2000H 0ff0:0103 1000H 2000H 0
mov ds, ax 0ff0:0105 2000H 2000H 0
mov ax, [0008] 0ff0:0108 2000H C389H 0
mov ax, [0002] 0ff0:010B 2000H EA66H 0

程序和数据没有区别,本质上都是二进制01码,关键在于CPU如何解读。

检测点 3.2

  1. 补全下面的程序,使其可以将10000H~1000FH中的8个字,逆序复制到20000H~2000FH中。逆序复制的含义如图3.17所示(图中内存里的数据均为假设)。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    mov ax, 1000H
    mov ds, ax
    __________
    __________
    __________
    push [0]
    push [2]
    push [4]
    push [6]
    push [8]
    push [A]
    push [C]
    push [E]

    答案:

    1
    2
    3
    mov ax, 2000H
    mov ss, ax
    mov sp, 0010H
  2. 补全下面的程序,使其可以将10000H~1000FH中的8个字,逆序复制到20000H~2000FH中。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    mov ax, 2000H
    mov ds, ax
    __________
    __________
    __________
    pop [E]
    pop [C]
    pop [A]
    pop [8]
    pop [6]
    pop [4]
    pop [2]
    pop [0]

    答案:

    1
    2
    3
    mov ax, 1000H
    mov ss, ax
    mov sp, 0

第六章 包含多个段的程序

检测点 6.1

  1. 下面的程序实现依此用内存0:0-0:15单元中的内容改写程序中的数据,完成程序:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    assume cs:codesg

    codesg segment
    dw 0123h, 0456h, 0789h, 0abch, 0defh, 0fedh, 0cbah, 0987h

    start: mov ax, 0
    mov ds, ax
    mov bx, 0

    mov cx, 8
    s: mov ax, [bx]
    _________
    add bx, 2
    loop s

    mov ax, 4c00h
    int 21h

    codesg ends

    end start

    答案:

    1
    mov cs:[bx], ax
  2. 下面的程序实现依此用内存0:0-0:15单元中的内容改写程序中的数据,数据的传送用栈来进行。栈空间设置在程序内。完成程序:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    assume cs:codesg
    dw 0123h, 0456h, 0789h, 0abch, 0defh, 0fedh, 0cbah, 0987h

    dw 0, 0, 0, 0, 0, 0, 0, 0, 0, 0

    start: mov ax, ____
    mov ss, ax
    mov sp, ____

    mov ax, 0
    mov ds, ax
    mov bx, 0
    mov cx, 8
    s: push [bx]
    ____________
    add bx, 2
    loop s

    mov ax, 4c00h
    int 21h

    codesg ends

    end start

    答案:

    1
    2
    3
    cs
    24h
    pop cs:[bx]

第九章 转移指令的原理

检测点 9.1

  1. 程序如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    assume cs:code

    data segment
    ___________
    data ends

    code segment
    start: mov ax, data
    mov ds, ax
    mov bx, 0
    jmp word ptr [bx+1]

    code ends
    end start

    若要使程序中的jmp指令执行后,CS:IP指向程序的第一条指令,在data段中应该定义哪些数据?
    答案:

    1
    dw 0, 0
  2. 程序如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    assume cs:code

    data segment
    dd 12345678H
    data ends

    code segment

    start: mov ax, data
    mov ds, ax
    mov bx, 0
    mov [bx], ____
    mov [bx + 2], ____
    jmp dword ptr ds:[0]

    code ends

    end start

    补全程序,使jmp指令执行后,CS:IP指向程序的第一条指令。
    答案:

    1
    2
    bx
    cs

    注意:bx指向低位,bx+2指向高位,低位为IP,而高位为CS。

  3. 用Debug查看内存,结果如下:

    1
    2000:1000 BE 00 06 00 00 00 ......

    则此时,CPU执行指令:

    1
    2
    3
    mov ax, 2000H
    mov es, ax
    jmp dword ptr es:[1000H]

    后,(CS)=?,(IP)=?
    答案:
    (CS)=0006,(IP)=00BE

检测点 9.2

补全编程,利用jcxz指令,实现在内存2000H段中查找第一个值为0的字节,找到后,将它的偏移地址存储在dx中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
assume cs:code
code segment
start: mov ax, 2000H
mov ds, ax
mov bx, 0
s: __________
__________
__________
__________
jmp short s
ok: mov dx, bx
mov ax, 4c00h
int 21h
code ends
end start

答案:
1
2
3
4
mov cl, [bx]
mov ch, 0 ;注意这一步的必要性
jcxz ok
inc bx

检测点 9.3

补全编程,利用loop指令,实现在内存的2000H段中查找第一个值为0的字节,找到后,将它的偏移地址存储在dx中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
assume cs:code
code segment
start: mov ax, 2000H
mov ds, ax
mov bx, 0
s: mov cl, [bx]
mov ch, 0
_________
inc bx
loop s
ok: dec bx
mov dx, bx
mov ax, 4c00h
int 21h
code ends
end start

答案:
1
inc cx ;注意loop的工作原理

第十章 CALL和RET指令

检测点 10.1

补全程序,实现从内存1000:0000处开始执行指令。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
assume cs:code

stack segment
db 16 dup (0)
stack ends

code segment
start: mov ax,stack
mov ss,ax
mov sp,16
mov ax, ____
push ax
mov ax, ____
push ax
retf
code ends

end start

答案:
1
2
1000h
0

检测点 10.2

下面的程序执行后,ax中的数值为多少?

内存地址 机器码 汇编指令
1000:0 b8 00 00 mov ax,0
1000:3 e8 01 00 call s
1000:6 40 inc ax
1000:7 58 s: pop ax

ax中的数值为6,注意执行完call s后,IP先变为6,然后将IP的值压栈,最后跳转至s。

检测点 10.3

下面的程序执行后,ax中的数值为多少?

内存地址 机器码 汇编指令
1000:0 b8 00 00 mov ax,0
1000:3 9a 09 00 00 10 call far ptr s
1000:8 40 inc ax
1000:9 58 s: pop ax
add ax,ax
pop bx
add ax,bx

ax中的数值为1010H,注意执行完call far ptr s后,IP先变为8,然后将CS、IP的值分别为1000和8依此压栈,最后再跳转至s继续执行。

检测点 10.4

下面的程序执行后,ax中的数值为多少?

内存地址 机器码 汇编指令
1000:0 b8 06 00 mov ax,6
1000:3 ff d0 call ax
1000:5 40 inc ax
1000:6 58 mov bp,sp
add ax,[bp]

ax中的数值为0BH,分析方法类似检测点10.2

检测点 10.5

  1. 下面的程序执行后,ax中的数值为多少?

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    assume cs:code
    stack segment
    dw 8 dup (0)
    stack ends
    code segment
    start: mov ax,stack
    mov ss,ax
    mov sp,16
    mov ds,ax
    mov ax,0
    call word ptr ds:[0eh]
    inc ax
    inc ax
    inc ax
    mov ax,4c00h
    int 21h
    code ends
    end start

    ax中的数值为3,注意ds与ss中存放的段地址相同,在执行了call word ptr ds:[0EH]之后,程序会先将下一条指令inc ax的偏移量压栈,然后跳转到栈顶所指向的指令的位置,即跳转至第一条inc ax的位置,故最后ax的值为3。
    注意:在使用Debug单步跟踪的时候,由于t命令所导致的中断,而影响了栈中的值。

  2. 下面的程序执行后,ax和bx中的数值为多少?

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    assume cs:codesg
    data segment
    dw 8 dup (0)
    data ends
    code segment
    start: mov ax,data
    mov ss,ax
    mov sp,16
    mov word ptr ss:[0],offset s
    mov ss:[2],cs
    call dword ptr ss:[0]
    nop
    s: mov ax,offset s
    sub ax,ss:[0ch]
    mov bx,cs
    sub bx,ss:[0eh]
    mov ax,4c00h
    int 21h
    code ends
    end start

    ax中的数值为1,bx中的数值为0,注意到程序的一开始将a的偏移量和cs放入ss:[0]和ss:[2]中,然后调用call指令,将CS和IP(nop指令的偏移量)依此压栈后跳转到s处继续执行,ax最终为s的偏移量减去nop指令所在位置的偏移量,为1,bx最终为cs的段地址相减,为0。

第十一章 标志寄存器

检测点 11.1

写出下面每条指令执行后,ZF、PF、SF等标志位的值

指令 ZF PF SF
sub al, al 1 1 0
mov al, 1 1 1 0
push ax 1 1 0
pop bx 1 1 0
add al, bl 0 0 0
add bl, 10 0 1 0
mul al 0 1 0

检测点 11.2

写出下面每条指令执行后,ZF、PF、SF、CF、OF等标志位的值

指令 CF OF SF ZF PF
sub al, al 0 0 0 1 1
mov al, 10H 0 0 0 1 1
add al, 90H 0 0 1 0 1
mov al, 80H 0 0 1 0 1
add al, 80H 1 1 0 1 1
mov al, 0FCH 1 1 0 1 1
add al, 05H 1 0 0 0 0
mov al, 7DH 1 0 0 0 0
add al, 0BH 0 1 1 0 1

检测点 11.3

  1. 补全下面的程序,统计F000:0处32个字节中,大小在[32,128]的数据的个数。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    	mov ax, 0f000h
    mov ds, ax

    mov bx, 0
    mov dx, 0
    mov cx, 32
    s: mov al, [bx]
    cmp al, 32
    __________
    cmp al, 128
    __________
    inc dx
    s0: inc bx
    loop s

    答案:

    1
    2
    jb s0
    ja s0
  2. 补全下面的程序,统计F000:0处32个字节中,大小在(32,128)的数据的个数。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    	mov ax, 0f000h
    mov ds, ax

    mov bx, 0
    mov dx, 0
    mov cx, 32
    s: mov al, [bx]
    cmp al, 32
    __________
    cmp al, 128
    __________
    inc dx
    s0: inc bx
    loop s

    答案:

    1
    2
    jna s0
    jnb s0

检测点 11.4

下面的程序执行后:(ax)=?

1
2
3
4
5
6
7
8
9
mov ax, 0
push ax
popf ;将PSW置0(本章所学习的标志位都为0)
mov ax, 0fff0h
add ax, 0010h ;修改相关标志位
pushf
pop ax ;(将PSW保存至ax)
and al, 11000101B
and ah, 00001000B ;只考虑CF,PF,ZF,SF,OF五个标志位

答案:
(ax)=45H

第十二章 内中断

检测点 12.1

  1. 用Debug查看内存,情况如下:
    0000:0000 68 10 A7 00 8B 01 70 00-16 00 9D 03 8B 01 70 00
    则3号中断源对应的中断处理程序的入口地址为:____
    答案:
    0070:018B
    注意高地址存放段地址,低地址存放偏移地址

  2. 存储N号中断源对应的中断处理程序入口的偏移地址的内存单元的地址为:
    存储N号中断源对应的中断处理程序入口的段地址的内存单元的地址为:

    答案:4N,4N+2

第十三章 int指令

检测点 13.1

  1. 在上面的内容中,我们用7ch中断例程实现loop的功能,则上面的7ch中断例程所能进行的最大转移位移为多少?
    答案:65535

  2. 用7ch中断例程完成jmp near ptr s指令的功能,用bx向中断例程传送转移位移。
    答案:

    1
    2
    3
    4
    5
    jnp:	push bp
    mov bp, sp
    add [bp+2], bx
    pop bp
    iret

检测点 13.2

判断下面说法的正误:

  1. 我们可以编程改变FFFF:0处的指令,使得CPU不去执行BIOS中的硬件系统检测和初始化程序。
    错误:FFFF:0处的内容无法修改
  2. int 19h中断例程,可以由DOS提供。
    错误:此时DOS还未被引导

第十四章 端口

检测点 14.1

  1. 编程,读取CMOS RAM的2号单元的内容。
    1
    2
    3
    mov al, 2
    out 70h, 2
    in al, 71h
  2. 编程,向CMOS RAM的2号单元写入0。
    1
    2
    3
    4
    mov al, 2
    out 70h, 2
    mov al, 0
    out 71h, al

检测点 14.2

编程,用加法和位移指令计算(ax)=(ax)*10。
提示,(ax)*10=(ax)*2+(ax)*8。

1
2
3
4
5
mov bx, ax
shl ax, 1
mov cl, 3
shl bx, cx
add ax, bx

第十五章 外中断

检测点 15.1

  1. 仔细分析一下上面的int 9中断例程,看看是否可以精简一下?
    其实在我们的int 9中断例程中,模拟int指令调用原int 9中断例程的程序段是可以精简的,因为在进入中断例程后,IF和TF都已经置0,没有必要再进行设置了。对于程序段:

    1
    2
    3
    4
    5
    6
    7
    pushf
    pushf
    pop ax
    and ah, 11111100B
    push ax
    popf
    call dword ptr ds:[0]

    可以精简为:

    1
    2
    pushf
    call dword ptr ds:[0]

    两条指令。

  2. 仔细分析上面程序中的主程序,看看有什么潜在的问题?
    在主程序中,如果在执行设置int 9中断例程的段地址和偏移地址的指令之间发生了键盘中断,则CPU将转去一个错误的地址执行,将发生错误。
    找出这样的程序段,改写它们,排除潜在的问题。
    提示,注意sti和cli指令的用法。
    答案:

    1
    2
    3
    4
    cli
    mov word ptr es:[9*4],offset int9
    mov word ptr es:[9*4+2],cs
    sti

    以及:

    1
    2
    3
    4
    5
    6
    cli
    push ds:[0]
    pop es:[9*4]
    push ds:[2]
    pop es:[9*4+2]
    sti

第十六章 直接定址表

检测点 16.1

下面的程序将code段中a处的8个数据累加,结果存储到b处的双字中,补全程序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
assume cs:code
code segment
a dw 1, 2, 3, 4, 5, 6, 7, 8
b dd 0
start: mov si, 0
mov cx, 8
s: mov ax, ____
add ____, ax
adc ____, 0
add si, ____
loop s

mov ax, 4c00h
int 21h

code ends
end start

答案:
1
2
3
4
a[si]
word ptr b[0]
word ptr b[2]
2

注意word ptr的使用

检测点 16.2

下面的程序将data段中a处的8个数据累加,结果存储到b处的字中,补全程序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
assume cs:code, es:data

data segment
a db 1, 2, 3, 4, 5, 6, 7, 8
b dw 0
data ends

code segment
start: __________
__________
mov si, 0
mov cx, 8
s: mov al, a[si]
mov ah, 0
add b, ax
inc si
loop s

mov ax, 4c00h
int 21h
code ends
end start

答案:
1
2
mov ax, data
mov es, ax

第十七章 使用BIOS进行键盘输入和磁盘读写

检测点 17.1

“在int 16h中断例程中,一定有设置IF=1的指令”,这种说法对吗?

正确,int 16h中断例程在键盘缓冲区中没有数据时,会等待直到键盘缓冲区中有数据为止,因此,int 16h中需要处理int 9h中断,所以一定有设置IF=1的指令。

评论和共享

C语言第二次作业

发布在 C语言

1、哥德巴赫猜想(2)——改编自习题5.14


题目描述:

哥德巴赫作了如下猜想:一个大于等于4的偶数都是两个素数之和。编写一个程序证明对于在正整数Begin、End之间的偶数,这一猜测成立。将判断一个数是否是素数定义成函数。
本题包含多组测试数据。

输入格式说明:

每组测试数据输入占一行,包括两个正整数,依次为Begin,End,4≤Begin<End≤200,遇文件尾测试结束。

输出格式说明:

输出在规定范围内所有偶数的哥德巴赫猜想形式的分解,存在多种不同输出结果组合时,输出满足要求的结果中分解后第一个数最小的结果,需要在第i组测试数据结果输出前添加一行提示信息:“CASE:i”,表示是第i组测试数据对应的结果,相邻两组测试样例的输出结果之间空一行。

样例输入:

10 20
45 145

样例输出:

CASE:1
COLDBACH’S CONJECTURE:
Every even number n>=4 among 10~20 is the sum of two primes:
10=3+7
12=5+7
14=3+11
16=3+13
18=5+13
20=3+17

CASE:2
COLDBACH’S CONJECTURE:
Every even number n>=4 among 45~145 is the sum of two primes:
46=3+43
48=5+43
50=3+47
52=5+47
54=7+47
56=3+53
58=5+53
60=7+53
62=3+59
64=3+61
66=5+61
68=7+61
70=3+67
72=5+67
74=3+71
76=3+73
78=5+73
80=7+73
82=3+79
84=5+79
86=3+83
88=5+83
90=7+83
92=3+89
94=5+89
96=7+89
98=19+79
100=3+97
102=5+97
104=3+101
106=3+103
108=5+103
110=3+107
112=3+109
114=5+109
116=3+113
118=5+113
120=7+113
122=13+109
124=11+113
126=13+113
128=19+109
130=3+127
132=5+127
134=3+131
136=5+131
138=7+131
140=3+137
142=3+139
144=5+139

解答

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <stdio.h>
#include <math.h>

int isPrime (int x);
int main (void)
{
int begin, end;
int count, number;
int subnumber1, subnumber2;
for (count = 1 ; scanf("%d%d", &begin, &end) != EOF ; ++count)
{
if (count != 1)
putchar('\n');
printf("CASE:%d\n", count);
printf("COLDBACH'S CONJECTURE:\n");
printf("Every even number n>=4 among %d~%d is the sum of two primes:\n", begin, end);
if (begin % 2 != 0)
begin++;
for (number = begin ; number <= end ; number += 2)
{
for (subnumber1 = 2 ; subnumber1 <= (number/ 2) ; ++subnumber1)
{
subnumber2 = number - subnumber1;
if (isPrime(subnumber2) && isPrime(subnumber1))
{
printf("%d=%d+%d\n", number, subnumber1, subnumber2);
break;
}
}
}
}
return 0;
}

/*************************************************
Function: isPrime
Description: 判断正整数是否为素数
Input: 一个正整数x
Return: 1表示素数 0表示合数
*************************************************/
int isPrime (int x)
{
int i;
for (i = 2 ; i <= sqrt(x) ; ++i)
if (x % i == 0) return 0;
return 1;
}

2、第k个数——改编自习题5.13


题目描述:

输入正整数n和k,输出n中从右端开始的第k个数字的值(k从1开始)。将求n中右端第k个数字定义成函数,如果k超过了n的位数,则函数返回-1;否则返回n中第k个数字。
本题包含多组测试数据。

输入格式说明:

每组测试数据输入占一行,包括两个正整数,依次为n,k,n在1~4000000000之间(闭区间),遇文件尾测试结束。

输出格式说明:

每组测试样例的输出结果占一行,输出函数返回结果。

样例输入:

321 3
421 4
42 12

样例输出:

3
-1
-1

解答:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#include <stdio.h>
int getKFromN (unsigned long n , int k);

int main (void)
{
unsigned long n;
int k;
while (scanf("%lu%d", &n, &k) != EOF)
{
printf("%d\n", getKFromN(n, k));
}
return 0;
}

/*************************************************
Function: getKFromN
Description: 返回n中从右端开始的第k个值
Input: 一个1~4000000000的正整数n 一个正整数k
Return: 一个正整数 n中的第k个数字 -1表示越位
*************************************************/
int getKFromN (unsigned long n , int k)
{
int unit = 0;//表示最后一位
int count;//表示当前位数
unsigned long result = n;//存储结果
for (count = 1 ; count <= k ; count++)
{
unit = result % 10;
result = result / 10;
}
if (!(unit == 0 && result == 0))
return unit;
else
return -1;
}
```

## 3、冰雹数——改编自习题5.11

* * *

### 题目描述:

n(0)是一个给定的正整数,对于i=0,1,2,...,定义:(1)若n(i)是偶数,则n(i+1)=n(i)/2;(2)若n(i)是奇数,则n(i+1)=3n(i)+1;(3)若n(i)是1,则序列结束。
本题包含多组测试数据。


### 输入格式说明:

每组测试数据输入占一行,包括一个正整数n0,n0在1~4000000000之间(闭区间),遇文件尾测试结束。

### 输出格式说明:

输出冰雹数序列以及序列数的总个数,相邻两组测试样例的输出结果之间空一行。

### 样例输入:

77
92


### 样例输出:

Hailstone generated by 77:
77 232 116 58 29 88 44 22 11 34 17 52 26 13 40 20 10 5 16 8 4 2 1
Number of hailstone generated:23

Hailstone generated by 92:
92 46 23 70 35 106 53 160 80 40 20 10 5 16 8 4 2 1
Number of hailstone generated:18

### 解答:

#include <stdio.h>
unsigned long long getNextHailstoneNumber (unsigned long long n);

int main (void)
{
unsigned long long number;
int count;
int flag = 1;
while (scanf(“%llu”, &number) != EOF)
{
if (!flag)
putchar(‘\n’);
printf(“Hailstone generated by %llu:\n”, number);
count = 0;
while (number != 1)
{
count += 1;
printf(“%llu “, number);
number = getNextHailstoneNumber(number);
}
printf(“1\n”);
printf(“Number of hailstone generated:%d\n”, count + 1);
flag = 0;
}
return 0;
}

/*
Function: getNextHailstoneNumber
Description: 返回以n为起始数的下一个冰雹数
Input: 一个的正整数n
Return: 一个正整数,表明n的下一个冰雹数
*/

unsigned long long getNextHailstoneNumber (unsigned long long n)
{
if (n % 2 == 0)
return n / 2;
else
return 3 * n + 1;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

## 4、计算题——改编自习题5.10

* * *

### 题目描述:

编程计算s=1+1/2!+1/3!+1/4!+...+1/n!。n由终端输入,将计算n!定义成函数。
本题包含多组测试数据。

### 输入格式说明:

每组测试数据输入占一行,包括一个正整数n,n在1~20之间(闭区间),遇文件尾测试结束。

### 输出格式说明:

每组测试样例的输出结果占一行,输出s,保留18位小数。
注:20!=2432902008176640000。

### 样例输入:

1
2

### 样例输出:

1.000000000000000000
1.500000000000000000

### 解答:

#include <stdio.h>
long long getFactorial (int n);

int main (void)
{
int number;
int i;//循环变量
double s;
while (scanf(“%d”, &number) != EOF)
{
s = 0;
for (i = 1 ; i <= number ; ++i)
{
s += 1.0 / getFactorial(i);
}
printf(“%.18f\n”, s);
}
return 0;
}

/*
Function: getFactorial
Description: 返回n的阶乘
Input: 一个正整数n
Return: 一个正整数n!
*/
long long getFactorial (int n)
{
int count;
long long result = 1;
for (count = 2 ; count <= n; ++count)
{
result *= count;
}
return result;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

## 5、掷骰子游戏——改编自习题5.1

* * *

### 题目描述:

编写一个模拟“投掷双骰子”的游戏程序。游戏规则:每轮投两次,取两次的和,第一轮若和为7或11则获胜,游戏结束;若和为2,3,或12则输了,失败结束;若和为其他数字,则将此值作为自己的点数,继续第二轮,第三轮⋯⋯直到某轮的和等于该点数则获胜,若出现和为7,则输掉游戏。模拟每掷一次骰子的随机取数规则:输入一个非负整数,称为启动数,你需要计算得到启动数各位数之和记为sum,现将1~6这6张数字卡片按顺时钟方向摆放一个环,一只蚂蚁每次从数字为1的卡片摆放的位置出发,这时你告诉蚂蚁按顺时钟方向走sum步,蚂蚁最终停留位置上的卡片数字即视为本次掷骰子得到的点数。
本题包含多组测试数据。


### 输入格式说明:

第一行输入n表示测试样例的个数,后面紧接着有n行输入,每行输入包含两个非负整数记为a,b,整数范围在0~10000之间(闭区间),视为第一轮两次投掷的启动数,若进行到了第n轮时,则第n轮两次投掷的启动数分别看作是a+n-1,b+n-1;


### 输出格式说明:

每组测试样例的输出结果占一行,输出本次游戏的最终结果。

### 样例输入:

3
1234 1
1 42
0 422

### 样例输出:

success!
fail!
success!

### 解答:

#include <stdio.h>
int calcSum (int startNumber);

int main (void)
{
int number;
int count;
int a, b;
int dieA, dieB, dieSum;
int winNumber;
int n;
scanf(“%d”, &number);
for (count = 0 ; count < number ; ++count)
{
scanf(“%d%d”, &a, &b);
dieA = calcSum(a) % 6 + 1;
dieB = calcSum(b) % 6 + 1;
dieSum = dieA + dieB;
if (dieSum == 7 || dieSum == 11)
{
printf(“success!\n”);
continue;
} else if (dieSum == 2 || dieSum == 3 || dieSum == 12)
{
printf(“fail!\n”);
continue;
} else
{
winNumber = dieSum;
}
n = 1;//此处与题目描述不符
do
{
dieA = calcSum(a + n) % 6 + 1;
dieB = calcSum(b + n) % 6 + 1;
dieSum = dieA + dieB;
n += 1;
if (dieSum == 7)
{
printf(“fail!\n”);
break;
}
}while (dieSum != winNumber);
if (dieSum == winNumber)
printf(“success!\n”);
}

return 0;

}

/*
Function: calcSum
Description: 计算启动数的各位的和sum
Input: 一个正整数n(启动数)
Return: 一个正整数n(启动数各个位数的和)
*/
int calcSum (int startNumber)
{
int unit = 0;
int result = startNumber;
int sum = 0;
while (result != 0)
{
unit = result % 10;
sum += unit;
result = result / 10;
}
return sum;
}
`

评论和共享

C语言第一次作业

发布在 C语言

2016年4月1日C语言作业答案及遇到的问题

题目及解答

2.一年中的第几天——改编自习题4.3


原题链接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include<stdio.h>;
int main (void)
{
int year, month, day;
int count;
int isLeap;
while (scanf("%d%d%d", &year, &month, &day) == 3)
{
count = 0;
isLeap = (year % 4 == 0 && year % 100 != 0) || (year % 100 == 0 && year % 400 == 0);
count += day;
month -= 1;
switch (month)
{
case 12: count += 31; month -= 1;
case 11: count += 30; month -= 1;
case 10: count += 31; month -= 1;
case 9: count += 30; month -= 1;
case 8: count += 31; month -= 1;
case 7: count += 31; month -= 1;
case 6: count += 30; month -= 1;
case 5: count += 31; month -= 1;
case 4: count += 30; month -= 1;
case 3: count += 31; month -= 1;
case 2: count += 28 + isLeap; month -= 1;
case 1: count += 31; month -= 1;
default: break;
}
printf("该日期是这一年中的第%d天\n", count);
}
return 0;
}

3.税金额度计算——改编自习题4.4


原题链接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
/* ZhihaoChen CSEE 1501 */
#include<stdio.h>;
/* Prototype Declaration */
void if_else_process (double x);
void switch_process (double x);
/* Main Function */
int main(void)
{
int number;
scanf("%d", &number);
int t; double x;
int count;
for (count = 0 ; count & number ; ++count)
{
scanf("%d%lf", &t, &x);
switch (t)
{
case 0:
if_else_process(x);
break;
case 1:
switch_process(x);
break;
case 2:
if_else_process(x);
switch_process(x);
break;
default:
break;
}
if (count != (number - 1))
printf("\n");
}
return 0;
}
/* data processing and output */
void if_else_process (double x)
{
double output;
if (x < 1000)
output = 0;
else if (x >= 1000 && x < 2000)
output = x * 0.05;
else if (x >= 2000 && x < 3000)
output = x * 0.10;
else if (x >= 3000 && x < 4000)
output = x * 0.15;
else if (x >= 4000 && x < 5000)
output = x * 0.20;
else if (x > 5000)
output = x * 0.25;
printf ("After if-else processing,the amount of tax to be paid is : %.2f\n", output);
}

void switch_process (double x)
{
double output;
switch((int)x / 1000)
{
case 0: output = 0; break;
case 1: output = x * 0.05; break;
case 2: output = x * 0.10; break;
case 3: output = x * 0.15; break;
case 4: output = x * 0.20; break;
default: output = x * 0.25; break;
}
printf("After switch processing,the amount of tax to be paid is : %.2f\n", output);
}

4.两个实数的四则运算——改编自习题4.5


原题链接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
/* ZhihaoChen CSEE 1501 */
#include<stdio.h>;
/* Prototype Declaration */
void if_else_process(double a, double b, char c);
void switch_process(double a, double b, char c);
/* Main Function */
int main(void)
{
int t, count;
double a, b;
char c;
for (count = 0 ; (scanf("%d%lf%lf %c", &t, &a, &b, &c) != EOF) ; ++count)
{
if(count != 0)
printf("\n");
switch (t)
{
case 0:
if_else_process(a, b, c);
break;
case 1:
switch_process(a, b, c);
break;
case 2:
if_else_process(a, b, c);
switch_process(a, b, c);
break;
default:
break;
}
}
return 0;
}
/* data processing and output */
void if_else_process (double a, double b, char c)
{
double output = 0.0;
if (c == '+')
output = a + b;
if (c == '-')
output = a - b;
if (c == '*')
output = a * b;
if (c == '/')
output = a / b;
printf ("After if-else processing,the result is : %.2f\n", output);
}

void switch_process (double a, double b, char c)
{
double output = 0.0;
switch(c)
{
case '+': output = a + b; break;
case '-': output = a - b; break;
case '*': output = a * b; break;
case '/': output = a / b; break;
default: break;
}
printf("After switch processing,the result is : %.2f\n", output);
}

5.合并空格字符输出——改编自习题4.7


原题链接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <stdio.h>
#define CHAR 0
#define SPACE 1

int main (void)
{
char character;
int status = CHAR;
while ((character = getchar()) != EOF)
{
if ((character == ' ') && (status == CHAR))
{
status = SPACE;
printf(" ");
}
if ((character != ' ') && (status == CHAR))
{
printf("%c", character);
}
if ((character != ' ') && (status == SPACE))
{
status = CHAR;
printf("%c", character);
}
}
return 0;
}

6.求π的近似值——改编自习题4.10


原题链接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>
#include <math.h>

int main (void)
{
double item = 1.0;
double pi = 0.0;
int count;
for (count = 0 ; (item >= 1.0e-5) ; ++count)
{
item = 1.0/(2 * count + 1);
if (count % 2 == 0)
pi += item;
else
pi += (-item);
}
printf("%.9f\n", pi * 4);
return 0;
}

7.验证哥德巴赫猜想——改编自习题4.13


原题链接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <stdio.h>
#include <math.h>

int isPrime (int x);

int main (void)
{
int number, testnum;
scanf ("%d", &number);
int count, subnumber1, subnumber2;
for (count = 0 ; count < number ; ++count)
{
scanf("%d", &testnum);
for (subnumber1 = 2 ; subnumber1 <= (testnum / 2) ; ++subnumber1)
{
subnumber2 = testnum - subnumber1;
if (isPrime(subnumber2) && isPrime(subnumber1))
{
printf("%d=%d+%d\n", testnum, subnumber1, subnumber2);
break;
}
}
}
return 0;
}

int isPrime (int x)
{
int i;
for (i = 2 ; i <= sqrt(x) ; ++i)
if (x % i == 0) return 0;
return 1;
}

8.正整数相加求平均数——改编自习题4.17


原题链接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <stdio.h>
int main (void)
{
int number;
scanf("%d", &number);
int count, subcount, status; int n, positiveNumber;
long sumNumber;
double averageNumber;
for (count = 0 ; count < number ; ++count)
{
n = 0; sumNumber = 0;
positiveNumber = 0;
averageNumber = 0.0;
scanf("%d", &status);
switch(status)
{
case 0:
for (subcount = 0 ; subcount < 10 ; ++subcount)
{
scanf("%d", &n);
if (n <= 0) continue;
sumNumber += n;
positiveNumber += 1;
}
averageNumber = (double)sumNumber / positiveNumber;
if (positiveNumber != 0)
printf("In \"continue\" way,numbers=%d,average=%f\n", positiveNumber, averageNumber);
break;
case 1:
for (subcount = 0 ; subcount < 10 ; ++subcount)
{
scanf("%d", &n);
if(n > 0)
{
sumNumber += n;
positiveNumber += 1;
}
}
averageNumber = (double)sumNumber / positiveNumber;
if (positiveNumber != 0)
printf("In \"no continue\" way,numbers=%d,average=%f\n", positiveNumber, averageNumber);
break;
default:
break;
}
}
return 0;
}

要注意的问题

1.注意审题,比如第六题注意到“最后一项”的绝对值,再比如说第三题注意到输出格式逗号前后都没有空格

2.注意数据类型的范围

3.注意浮点数的科学计数法表示

4.注意条件运算要不要加等于号

5.逻辑错误比语法错误更难检查,要关注程序的细节

6.注意scanf在读取的字符(%c)时候会识别’ ‘,所以在格式化字符串中用’ %c’或者’%1s’均可(后者无法通过系统)

7.注意浮点数的运算存在精度损失,例:double i = 1/e-5 而(int)i=99999,在作为循环条件时需要格外注意,一般情况下最好避免,或者+0.0001再取整

评论和共享

  • 第 1 页 共 1 页
作者的图片

码龙黑曜

iOS开发者/计算机科学/兽人控


华中科技大学 本科在读


Wuhan, China