Вызов функции с большим количеством параметров, чем ожидалось - PullRequest
0 голосов
/ 22 ноября 2018

Я просматривал некоторый код и обнаружил нечто похожее на это.

Файл foo.c:

int bar(int param1)
{
    return param1*param1;
}

Файл main.c:

#include <stdio.h>

int bar(int param1, int unusedParam);

int main (void)
{
    int param = 2, unused = 0;
    printf("%d\n", bar(param, unused));
}

Запуск gcc main.c foo.c -Wall --pedantic -O0, он компилирует, связывает и работает правильно, не выдавая ни одного предупреждения в процессе.Почему это так?

Спасибо!

Ответы [ 2 ]

0 голосов
/ 22 ноября 2018

Обычно у вас будет такая структура файла:

foo.c

#include "foo.h"

int bar(int param1)
{
    return param1*param1;
}

foo.h

int bar(int param1);

main.c

#include <stdio.h>
#include "foo.h"

int main (void)
{
    int param = 2, unused = 0;
    printf("%d\n", bar(param, unused));
}

Теперь вы получите ошибку компиляции, как только вы используете bar с несоответствующими параметрами.

0 голосов
/ 22 ноября 2018

Это действительно зависит от соглашения о вызовах и архитектуры.Например, при cdecl на x86, когда аргументы перемещаются справа налево и вызывающий восстанавливает стек, наличие дополнительного параметра прозрачно для функции bar:

push    11
push    10
call    _bar
add     esp, 8

bar только «увидит» 10 и будет работать с этим параметром, как ожидается, возвращая 100.После этого стек восстанавливается, поэтому в main также нет смещения;если бы вы только что передали 10, вместо этого добавили бы 4 к esp.

Это также относится к соглашениям о вызовах x64 для MSVC в Windows и Система V ABI , где первые несколько 1 целочисленных аргументов передаются в регистрах;второй аргумент будет заполнен в назначенном регистре вызовом в main, но даже не будет рассмотрен bar.

Если, однако, вы попытались использовать альтернативное соглашение о вызовах, когда вызываемый абонентОтветственный за очистку стека, вы столкнетесь с проблемами либо на этапе сборки, либо (что еще хуже) во время выполнения.stdcall, например, украшает имя функции числом байтов, используемых списком аргументов, поэтому я даже не могу связать конечный исполняемый файл, изменив bar на использование stdcallвместо этого:

error LNK2019: unresolved external symbol _bar@8 referenced in function _main

Это потому, что bar теперь имеет сигнатуру _bar@4 в своем объектном файле, как и должно быть.

Это становится интересным, если вы используете устаревшее соглашение о вызовах pascal, где параметры перемещаются слева направо:

push 10
push 11
call _bar

Теперь bar возвращает 121, а не 100, как вы ожидали.То есть, если функция успешно вернется, чего не произойдет, поскольку вызываемый объект должен был очистить стек, но потерпел неудачу из-за дополнительного параметра, уничтожив адрес возврата.

1: 4 для MSVC onОкна;6 в системе V ABI

...