【问题标题】:Why the following assembly code doesn't print out the new line (0xa) character?为什么下面的汇编代码没有打印出换行符(0xa)?
【发布时间】:2025-12-30 15:00:12
【问题描述】:

在下面的代码中,NASM 按预期运行所有内容,除了在末尾打印一个换行符。可能是什么原因?

%define sys_write       0x2000004           ; system call number for write
%define sys_read        0x2000003           ; system call number for read
%define sys_exit        0x2000001           ; system call number for exit
%define std_out         0x1                 ; file descriptor number for standard output
%define std_in          0x2                 ; file descriptor number for standard input
%define new_line        0xa                 ; a new line character
%define current_address $                   ; address of current location

; a macro to implement the write system call and write
; a string into standard output using two parameters
%macro write_string 2
    mov rax, sys_write                          ; system call to write
    mov rdi, std_out                            ; standard output
    mov rsi, %1                                 ; a string to write
    mov rdx, %2                                 ; length of the string
    syscall                                     ; kernel call interruption
%endmacro

; a macro to implement the exit system call
%macro exit 0
    mov rax, sys_exit                           ; system call to exit
    syscall
%endmacro

    section .data

message:    db 'Displaying 9 stars', new_line       ; a message
.length:    equ current_address - message           ; length of the message
asterisks:  times 9 db '*'                          ; a string of 9 asterisks

global start

    section .text

start:                                      ; linker entry point

    write_string message, message.length    ; print a message to standard output
    write_string asterisks, 9               ; print 9 asterisks
    write_string new_line, 1                ; print a line feed(new line)

    exit                                    ; end the program

组装后:

user$ nasm -f macho64 9stars.asm
user$ ld 9stars.o -o 9stars
ld: warning: -macosx_version_min not specified, assuming 10.10
user$ ./9stars

预期的输出应该是:

Displaying 9 stars
*********
user$

但当前的输出是:

Displaying 9 stars
*********user$

看起来write_string new_line, 1 没有将new_line 视为字符常量 虽然在db 'Displaying 9 stars', new_line new_line 添加了一个换行符, 字符代码 10。我最初的猜测是 new_line 根本不是字符常量。 这可能是什么原因?这怎么可能解决?我在 Mac OS X 10.10 上。我有 核心 i5 m540 英特尔 CPU。我使用 NASM 版本 2.11.06。

【问题讨论】:

  • mac不使用CR(代码13)吗?
  • @Jester:直到 2000 年左右,它都在“经典”Mac OS 中使用。尽管 Mac OS X 本质上是 *nix,因此它使用 LF (0xa),正如自然所期望的那样。
  • 如果你使用0xd 作为new_line 你会得到*********user$ 的输出并且Displaying 9 stars 不会打印。这是因为 CR 将光标返回到第一个位置(左侧)因此删除了Displaying 9 stars,不像 LF 馈送一行,将光标向下移动一行。
  • %define std_in 0x2 应该是 0。 FD 2 是标准错误。

标签: macos assembly nasm x86-64


【解决方案1】:

write_string 需要字符串的地址,但new_line 只是字符值0xa。试图将其解释为字符串的地址会产生不可预知的结果。如果您想要一个只包含一个可以打印的0xa 的字符串,那么您需要执行以下操作:

    section .data

message:    db 'Displaying 9 stars', new_line       ; a message
.length:    equ current_address - message           ; length of the message
asterisks:  times 9 db '*'                          ; a string of 9 asterisks
newline:    db new_line                             ; <<< a single LF

global start

    section .text

start:                                      ; linker entry point

    write_string message, message.length    ; print a message to standard output
    write_string asterisks, 9               ; print 9 asterisks
    write_string newline, 1                 ; <<< print LF

【讨论】:

  • 实际上,它是可以预测的—— 0xa 不会是一个有效的地址,所以系统调用将失败并显示 EFAULT 并且不打印任何内容。由于他不检查系统调用的错误,所以它什么也不做。
  • 没错,所以我应该传递一个字符串的地址,而不是一个字符值。 (+1)我现在意识到我的错误。但是如果我使用newline: db new_line ; &lt;&lt;&lt; a single LF 这将违背我最初的目的,即定义一个可以在整个代码中使用的有意义的常量字符串。为了消除看起来多余的newline: db new_line 的需要,我可以制作new_line: db 0xa 并改用它吗?那么如何使用它来制作message?因为message: db 'Displaying 9 stars', new_linenew_line: db 0xa 之后将无法工作
  • 通常您只使用new_line 作为您定义的任何字符串的一部分(就像您为message 所做的那样)。对于一个单独的 new_line 虽然使用上面的字符串 newline 没有任何问题 - 当然,您只需要定义一次,然后您就可以在任何地方使用它。或者考虑实现一个write_char 宏,它只输出一个字符值,然后你可以只做write_char new_line
  • 我喜欢编写一个write_char 宏来输出单个字符值的想法。它会是什么样子?
  • 可能最简单的实现就是调用putchar()