Разница между вызовом по имени и расширением вызова по макросу - PullRequest
0 голосов
/ 22 июня 2019

Предположим, у нас есть следующий код на языке, который очень похож на C.

 int A[2];

 A[0]=4;
 A[1]=7;

    void f(int x, int y) {
     x++; A[1]++; y++;
     printf(x, y, A[0], A[1]);
    }

    void main() {
     int k = 0;
     f(k, A[k]);
     print(k, A[0], A[1]);
    }

Я хочу определить вывод этой программы.Я не очень хорошо понял разницу между вызовом по имени и методом расширения по вызову.

Итак, в методе call-by-name k инициализируется значением 0, а затем вызывается функция f().x становится равным «k», а y становится равным «A [k]».Первая команда в вызываемой функции - x++, которая увеличивает значение «k» на 1. Таким образом, k становится равным 1. Затем A [1] увеличивается, поэтому A [1] становится 7 + 1 = 8.Ни один из x, y не затронут.Наконец, у нас есть команда y++, которая увеличивает значение «A [k]» на 1, поэтому оно увеличивает значение A [1] (поскольку теперь k = 1) на 1, поэтому A [1] становится теперь8 + 1 = 9.

Затем f() печатает: 1,9,4,9

И затем мы возвращаемся к функции main(), которая печатает: 1,4,9

Итакесли я не ошибаюсь, вывод программы - 1,9,4,9,1,4,9.

Но чем отличается расширение по вызову от макроса Этот метод?Что это меняет?

Ответы [ 2 ]

1 голос
/ 23 июня 2019

Нет ничего похожего на "вызов по макросу" в языке Си. Есть только макросы, которые принимают параметры. Макросы просто текстуально заменяются предварительно обработанными токенами.

Макросы IMO следует использовать только в том случае, если они действительно необходимы, в большинстве случаев лучше использовать встроенные функции. Макросы трудно использовать (так как компилятор компилирует предварительно обработанный файл .c) и подвержены ошибкам.

Пример

#define SUB(a,b) a-b

и использование

printf("%d", SUB(3-2,4-5));

результат будет не только 2 -8

0 голосов
/ 23 июня 2019

Но чем отличается расширение по вызову от макроса от этого метода?Что это меняет?

Для C "расширение через вызов" не существует.Вместо этого для макросов препроцессор выполняет прославленную операцию «вырезать и вставить» для необработанного текста.

Например, если у вас есть это:

int A[2];

A[0]=4;
A[1]=7;

#define MACRO(x, y) {         \
    x++; A[1]++; y++;         \
    printf(x, y, A[0], A[1]); \
}

void main() {
    int k = 0;
    MACRO(k, A[k]);
    print(k, A[0], A[1]);
}

Затем препроцессор будет вырезать и вставлять текст из макросагде используется макрос, а затем замените x и y аргументами, которые вы указали, чтобы (после предварительной обработки) исходный код выглядел следующим образом:

int A[2];

A[0]=4;
A[1]=7;

void main() {
    int k = 0;
{         \
    k++; A[1]++; A[k]++;         \
    printf(k, A[k], A[0], A[1]); \
}
    print(k, A[0], A[1]);
}

Конечно, макросы неНе нужно содержать корректный код, а в качестве источника вообще не нужно указывать C (например, вы можете использовать препроцессор для предварительной обработки исходного кода на языке ассемблера, сказав компилятору «не компилируйте, просто выводите предварительно обработанный текст»);и нет никакой реальной причины, по которой вы не можете использовать совершенно другой препроцессор (с совершенно другими функциями и / или синтаксисом макросов) и передавать полученный текст в компилятор C (говоря компилятору «не препроцессируйте, просто компилируйте»).

На практике;основные отличия для макросов и функций:

  • для макросов, нет проверки типов для параметров, поэтому ошибки в конечном итоге становятся более раздражающими, если вы найдете
  • для макросов, отладчикбудет сообщать только номер строки, в которой был развернут макрос, и не будет сообщать, откуда на самом деле появился код, поэтому ошибки в итоге оказываются более раздражающими при поиске
  • для макросов, поскольку они не обязательно должны быть действительнымиC вы можете сделать несколько странных махинаций (например, #define forever while(1) {, чтобы вы могли использовать forever i++; } в качестве бесконечного цикла), и может быть мощным для запутывания кода (намеренно затрудняя чтение кода).
  • дляфункции, компилятор может решить не включать функцию, чтобы уменьшить размер кода
  • для функций, у вас может быть рекурсия (с макросами вы не можете - это будет бесконечное количество текста)
  • для функций вы можете иметь указатели функций и / или иметь внешние функции (где компоновщик выясняет, где находится функция, либо со статической связью, либо с динамической связью).ing).
...