Не работает функция scanf

Когда я запускаю этот код на FASM, то сначала выводится "What is your name?". Потом я ввожу имя, нажимаю enter, программа зависает на несколько секунд и вылетает когда выполняется scanf.

format pe console
include 'C:\Program Files\FASM\INCLUDE\WIN32A.INC'
entry start

section '.code' code readable executable
        start:
                push question
                call [printf]
                push name
                push formatName
                call [scanf]
                push name
                push answer
                call [printf]
                call [getch]
                push NULL
                call[ExitProcess]

section '.data' data readable

        question db 'What is your name? ', 0
        answer db 'Hello, %s! ', 0
        formatName db '%s', 0
        NULL = 0
        name rd 2

section '.idata' import data readable writeable

        library kernel, 'kernel32.dll',\
                msvcrt, 'msvcrt.dll'

        import kernel,\
               ExitProcess, 'ExitProcess'

        import msvcrt,\
               printf, 'printf',\
               getch, '_getch',\
               scanf, 'scanf_s'

Я попробовал использовать scanf в c++ в этом коде:

#include <iostream>

int main()
{
    std::string Name;
    std::cout << "What is your name?\n";
    scanf_s("%s", &Name);
    std::cout << "Hello, " << Name << "!";
}

Результат тот же самый. Я думаю, что дело всё в msvcrt.dll. Кто-нибудь знает, как исправить scanf?


Ответы (2 шт):

Автор решения: Zugr
char s[10];
scanf_s("%9s", s, (unsigned)_countof(s)); // buffer size is 10, width specification is 9

И на ассемблере тоже надо уточнять сколько памяти вы выделили под name?

→ Ссылка
Автор решения: PRODIGY

Ошибка возникает из-за того, что функции msvcrt.dll используют соглашение о вызовах "cdecl", при котором за очистку аргументов в стеке отвечаешь ты сам. Например положили мы в стек 2-аргумента для printf(), то после вызова этого printf() нужно добавить [add esp,2*4]. Если положили 3-аргумента, то соответственно [add esp,3*4]. Иначе стек остаётся не сбалансированным и затирается адрес-возврата. Вообще-то, именно для отлова таких багов придумали отладчик.

У системных-же библиотек типа Kernel32.dll соглашение "stdcall", и их функции сами очищают стек от своих аргументов, при помощи пролога на входе [mov ebp,esp], и эпилога на выходе [mov esp,ebp]. Есть ещё тип вызовов "fastcall", который используют 64-битные приложения - здесь аргументы передаются не через стек, а через регистры.

Чтобы соблюдать подобные типы соглашений, FASM имеет для каждого из них свои макросы - для вызовов "cdecl" (c-declaration) предусмотрен макрос "cinvoke", а fn. "stdcall" вызываются при помощи обычного "invoke" (без приставки "с"). Более того имеется и инклуд "win32ax.inc", который позволяет использовать укороченные директивы типа .code, вместо портянки "section .code code readable executable" и прочих. И спецификаторы с текстом можно помещать сразу в аргументы (как это делается в си), обрамляя их в угловые скобки - вот пример:

format pe console
include 'win32ax.inc'
entry start
;//------------
.data
buff      db  0   ;//<--- буфер в 4К до конца секции-данных
;//------------
.code
start:  cinvoke  printf,<10,' What is your name? ', 0>
        cinvoke  scanf,<'%s',0>,buff
        cinvoke  printf,<10,' Hello, %s!',0>,buff

        cinvoke  getch            ;//<--- вызов "CDECL"   = cinvoke
         invoke  ExitProcess,0    ;//<--- вызов "StdCall" =  invoke
;//------------
section '.idata' import data readable
library  kernel32,'kernel32.dll',msvcrt,'msvcrt.dll'
import   msvcrt,printf,'printf',getch,'_getch',scanf,'scanf'
include  'api\kernel32.inc'
→ Ссылка