gpt4 book ai didi

windows - 在调用 asm 函数之前在 C 中调用 printf 或不调用的神秘副作用?

转载 作者:可可西里 更新时间:2023-11-01 11:24:01 26 4
gpt4 key购买 nike

此程序必须根据用户提供的精度计算圆周率。calculate_pi()函数是用NASM写的。有人可以向我解释为什么如果评论此行:

//printf("accuracy: %.15f\n", precision);       //<- This line

程序无法正常运行。向 calcuta_pi() 函数发送奇怪的数字?如果注释掉这一行,就会向函数发送一个非常小的值,程序将无限运行。

但如果不是注释程序,则它可以正常工作。

#include <stdio.h>
#include <math.h>

extern double calculate_pi(double precision); /* external function declaration */

double calculate_pi(double precision); /* function prototype */

int main()
{
double precision = 1;

printf("A program that calculates pi, with accuracy provided by the user\n");
printf("Give me accuracy\n");

while(1)
{
if (scanf("%lf", &precision) != 1)
{
printf("reading error\n");

fseek(stdin,0,SEEK_END);

continue;
}
if(precision<0)
precision = fabs(precision);

//printf("accuracy: %.15f\n", precision); //<- This line
printf("pi: %.15f\n", calculate_pi(precision));
}

return 0;
}

这是我的汇编代码:

;   arctg(1)=a
; tg(arctg(1))=tg(a)
; atan(x) = x - x^3/3 + x^5/5 - x^7/7 + x^9/9..
; PI/4 = atan(1) = 1 - 1/3 + 1/5 - 1/7 + 1/9...
; PI = (4/1) - (4/3) + (4/5) - (4/7) + (4/9) - (4/11) + (4/13) - (4/15) ...


section .text use32

global _calculate_pi

_calculate_pi:

%idefine a [ebp+12]


;ramka stosu
push ebp
mov ebp, esp

;ustawianie zmiennych
fld qword [const_wynik]
fstp qword [wynik]

fld qword [const_licznik]
fstp qword [licznik]

fld qword [const_mianownik]
fstp qword [mianownik]

.loop:
finit ; inicjalizacja stosu FPU

fld qword [licznik] ;licznik na stos
fld qword [mianownik] ;mianownik na stos
fdiv ;wynik dzielenia st1/st0
fadd qword [wynik] ;st0 = wynik dzielenia + [wynik]

fstp qword [wynik] ;wywalamy z st0 do [wynik]

;zmieniamy mianownik + 2
fld qword [mianownik] ;mianownik na stos
fadd qword [zwiekszmian] ;st0 = mianownik + 2
fstp qword [mianownik] ;wywalamy z st0 do [mianownik]

;zmieniamy licznik *(-1)
fld qword [licznik] ;licznik na stos
fchs ;st0 = -st0 = -licznik
fstp qword [licznik] ;wywalamy z st0 do [licznik]

;sprawdzanie dokladnosci
fld qword[wynik] ;wynik na stos
fldpi ;pi na stos
fsub ;st0 = wynik-pi = st1 - st0
fabs ;st0 = |wynik-pi|

fld qword a ;st0 = zadana dokladnosc

;(Unordered Compare ST(0) to ST(i) and set CPU flags and Pop ST(0))
;Przyrostek p oznacza obniżenie stosu rejestrów koprocesora, przyrostek i oznacza zapisywanie wyników bezpośrednio do flag procesora a nie flag koprocesora
fucomip st0, st1 ;porownanie z dokladnoscia if(zadana dokladnosc > uzyskana)


jb .loop ;only the C0 bit (CF flag) would be set if no error


fld qword [wynik]

;zwraca to co w st0
leave ; LEAVE = mov esp, ebp / pop ebp
ret


section .data:

wynik dq 4.0
licznik dq -4.0
mianownik dq 3.0
zwiekszmian dq 2.0

const_wynik dq 4.0
const_licznik dq -4.0
const_mianownik dq 3.0

示例输出:

enter image description here

我正在使用:

  • 2014 年 10 月 20 日编译的 NASM 版本 2.11.06
  • gcc (MinGW.org GCC-6.3.0-1) 6.3.0

编译和汇编命令:

nasm -o pi.o -f coff pi.asm
gcc pi.o pi_interface.c -o projekt.exe -Wall -Wextra

最佳答案

我认为您错误地访问了函数 arg,偏移了 4 个字节。当你制作堆栈框架时,第一个 arg 位于 [ebp+8],但你正在从 [ebp+12] 加载. (这适用于在堆栈上传递参数的所有调用约定。我认为 32 位 mingw 默认情况下为 double 执行此操作。)

这意味着您用作 precisiondouble 值的高 4 字节来自调用者碰巧留在 8 字节 arg 槽上方的堆栈中的任何内容.这解释了为什么调用者的变化会影响你的函数的行为,以及为什么你会得到一个无限循环:如果你加载的字节恰好代表一个非常小的 double,你的循环永远不会退出。

低 4 个字节(尾数的 32 个最低有效位)来自调用者传递的前 4 个字节。

通过查看寄存器并注意到您加载的值不是调用者传递的值,您可以使用调试器轻松找到它。此外,@Ped7g 的建议只是尝试在一个普通的 asm 函数中返回 precision 也会发现问题。

关于windows - 在调用 asm 函数之前在 C 中调用 printf 或不调用的神秘副作用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47845728/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com