0

Let's assume we have the following code in a language that looks a lot like 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]);
    }

I want to define the output of this program. I haven't understood well the difference between the call-by-name and the call-by-macro-expansion method.

So, in the call-by-name method, k is initialized to 0 and then f() function is called. x becomes equal to "k" and y becomes equal to "A[k]". The first command in the called function is x++ which increases the value of "k" by 1. So k becomes equal to 1. Then A[1] is increased, so A[1] becomes 7+1=8. None of the x,y are affected. Finally, we have the command y++ which increases the value of "A[k]" by 1, so it increases the value of A[1] (since now k=1) by 1, so A[1] becomes now 8+1=9.

Then f() prints: 1,9,4,9

And then we return to the main() fuction which prints: 1,4,9

So, the output of the program is 1,9,4,9,1,4,9 if I am not mistaken.

But how does call-by-macro-expansion differs from this method? What does it change?

MJ13
  • 195
  • 6
  • What do you mean by "looks a lot like java"? This isn't supposed to be java code? Are you looking for a solution in c/c++ where you have macros? What is the actual programming language you are using? Please [edit] your question to change the tags to the language you are using. Currently this code doesn't make sense because java does not have "macro expansions". – Progman Jun 23 '19 at 11:55
  • @Progman Ok I edited my question. I just wanted to understand how this method works in constrast with passing parameters by name. Didn't know Java doesn't support parameter-passing by macro-expansion. So I had just written down a small programm in a hypothetical language (like PseudoCode). Do you know how macro-expansion differs from passing parameters by result? – MJ13 Jun 23 '19 at 12:36
  • Possible duplicate of [Call by name vs call by macro expansion](https://stackoverflow.com/questions/44490619/call-by-name-vs-call-by-macro-expansion) – Progman Jun 23 '19 at 12:37
  • @Progman I think that comparing the macro-expansion method with my example on calling by result method will make it more clear to me. – MJ13 Jun 23 '19 at 12:39
  • Your question is very unclear. Both C and Java are strict evaluation languages, where call-by-name is not possible. C has macros but they are primitive (textual substitution only); normally when people talk about "call-by-macro-expansion" they're doing so in the context of a much more powerful macro mechanism. It's unclear exactly what kind of answer you're looking for here, or how well you understand the concept of evaluation strategies in the first place. What do you already understand, and what are you trying to understand? – Daniel Pryden Jun 23 '19 at 13:26
  • To start off with, you probably should make sure you understand the [difference between call-by-value and call-by-reference](https://stackoverflow.com/a/34971934/128397) first. – Daniel Pryden Jun 23 '19 at 13:35
  • @DanielPryden I have understood how passing parameters by value-result works but I haven't understood how passing parameters to a fucntion by macro-expansion works. So, in this case i would like to know what are the differences in the example above (assuming that the language supports both mechanisms) – MJ13 Jun 23 '19 at 13:37
  • Call by name and call by macro expansion are essentially the same thing (a lazy evaluation strategy). The only difference is in how precise the captured "name" is: if it is an expression with free variables, do they get captured or not? Basically call-by-macro-evaluation is "poor man's call-by-name". – Daniel Pryden Jun 23 '19 at 13:41
  • @DanielPryden So in my pseudo code what are the differences? – MJ13 Jun 23 '19 at 13:55
  • I think you would need to construct an example in a different language to see a meaningful difference. In this specific case I think the result would be the same, since textual substitution doesn't affect the meaning of any identifiers. Or I guess you could say that macro substitution here would simply be a compile-time error, since `k` has no meaning inside `f`. – Daniel Pryden Jun 23 '19 at 14:13

2 Answers2

1

But how does call-by-macro-expansion differs from this method? What does it change?

For C, "call-by-macro-expansion" doesn't exist. Instead, for macros the preprocessor does a glorified "cut&paste" operation on raw text.

For example, if you have this:

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]);
}

Then the preprocessor will cut&paste the text from the macro to where the macro is used and then replace x and y with the arguments you provided, so that (after preprocessing) the source code looks like this:

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]);
}

Of course the macros don't need to contain valid code, and the source doesn't even need to be C at all (e.g. you could use the preprocessor to preprocess assembly language source code by telling the compiler "don't compile, just output the preprocessed text"); and there's no real reason why you can't use a completely different preprocessor (with completely different features and/or macro syntax) and feed the resulting text into a C compiler (telling the compiler "don't preprocesses, just compile").

In practice; the main differences for macros and functions are:

  • for macros, there's no type-checking on the parameters, so bugs end up being more annoying to find
  • for macros, a debugger will only say the line number where the macro was expanded and won't say where the code actually came from, so bugs end up being more annoying to find
  • for macros, because they don't have to be valid C you can do some bizarre shenanigans (e.g. #define forever while(1) { so you can use forever i++; } as an infinite loop), and can be powerful for code obfuscation (deliberately making it hard to read the code).
  • for functions, the compiler can decide not to inline the function to reduce code size
  • for functions, you can have recursion (with macros you can't - it'd end up being an infinite amount of text)
  • for functions, you can have function pointers and/or have external functions (where the linker figures out where the function is, either with static linking or dynamic linking).

For a simpler example of (a) difference, consider this code:

#define f(x) { \
    x++;       \
}

void g(int x) {
    x++;
}

void main() {
    int a = 1;
    int b = 1;

    f(a);
    printf("%d\n", a);

    g(b);
    printf("%d\n", b);
}

These look the same, but are not. After expanding the macro and inlining the function, it becomes more like this:

void main() {
    int a = 1;
    int b = 1;

    a++;
    printf("%d\n", a);  // Will print "2' because the original `a` was changed

    int x = b;
    x++;
    printf("%d\n", b);  // Will print "1' because the original `b` was not changed
}

Note that this is exactly the same problem with the example above (for the macro, the x++; modifies the original k and not a copy of the original k; and for the function the x++; modifies a copy and not the original).

Brendan
  • 35,656
  • 2
  • 39
  • 66
  • Thanks for the answer! Could you explain me in which spot will the calculations above change? It will make it more clear through my example. – MJ13 Jun 23 '19 at 13:32
  • @MJ13: Example changed to be a lot closer to your original. – Brendan Jun 23 '19 at 13:38
  • macros are not "called" only textually replaced. C does not know anything like call-by-macro. – 0___________ Jun 23 '19 at 13:40
  • @P__J__: That's exactly what I said in the first sentence?? – Brendan Jun 23 '19 at 13:43
  • @Brendan sorry did not notice. Please edit your question to let me ammend my vote – 0___________ Jun 23 '19 at 13:49
  • @Brendan So can you define the output values on this case? – MJ13 Jun 23 '19 at 13:53
  • @MJ13: For macros, there's no "output value" (like there is for a `return something` in a function). – Brendan Jun 23 '19 at 13:59
  • @Brendan What I am trying to say is: Which is the output of this program? What will the `print` commands display? – MJ13 Jun 23 '19 at 14:03
  • @MJ13: It probably won't compile - the `printf(k, A[k], A[0], A[1]);` should cause a `first argument has the wrong type (not a string)" compile time error. – Brendan Jun 23 '19 at 14:06
  • @MJ13: If it did compile, printed values would be `1 9 4 9` and `1 4 9`. – Brendan Jun 23 '19 at 14:10
  • @Brendan So, If it did compile somehow then the printed values will be exactly the same with the pass by value-result, right ? – MJ13 Jun 23 '19 at 14:12
  • @MJ13: No - for the original "pass by value" code the output would be `1 9 4 9` and `0 4 9`; because whatever happens to `k` inside the function happens to the copy of the value that was passed and doesn't effect the original value in `main()`. – Brendan Jun 23 '19 at 14:17
  • @Brendan I am sorry...I meant **call by name** method...Would that be the same with this one? I guess yes – MJ13 Jun 23 '19 at 14:23
  • @MJ13: Yes - your "call by name" (a normal function with parameters passed by value) would (if it compiled) print `1 9 4 9` and `0 4 9`. – Brendan Jun 23 '19 at 14:26
  • @Brendan I think you are missing something...Check my analysis above and the printed output. – MJ13 Jun 23 '19 at 14:38
  • @MJ13: Your analysis (for "normal function using pass by value") was wrong - main prints `0 4 9` because the value of `k` never changes The value that is in `k` gets copied to `x` and `x` changes but `k` does not. – Brendan Jun 23 '19 at 14:50
  • @Brendan I had made a mistake while typing the question...A[1] = 7...Just fixed that. So with this example both **call by name** and **call by macro-expansion** methods would give the same result right? – MJ13 Jun 23 '19 at 15:07
  • @Brendan your makro is still wrong. The macro you wrote has side effects. The function does not. – 0___________ Jun 23 '19 at 15:20
  • @P__J__: The macro in my example is deliberately "as identical as possible" to the OP's original function so that it's possible to show that the resulting behavior is not equivalent. If the behavior was the same (because there are no side-effect) the example would be pointless.The macro is correct, you are wrong. – Brendan Jun 23 '19 at 16:00
  • @MJ13: No, the results will always be different because the function is modifying a copy of `k` while the macro is modifying the original `k`. To make the macro behave the same as the function you'd have to create a new variable (e.g. `#define MACRO(x, y) int x2 = x; x2++; A[1]++; ...`). – Brendan Jun 23 '19 at 16:06
  • @Brendan It's been a long time since I posted this question. But I am trying again now to figure this out. Could you please type the output of your example with **macro-expansion** ? My code for the **call by name** method gives an output of `1 9 4 9 1 4 9` . I would like to spot the difference between them in more detail cause I think I get the same results in both cases. – MJ13 Sep 13 '19 at 18:51
  • @MJ13: Added a simpler example to show why the output should be different depending on whether you use a macro or a function. Don't forget that you can compile C code and execute it, and see what it actually does do yourself. – Brendan Sep 14 '19 at 09:44
  • @Brendan I get your point but you are missing my question. You are explaining the difference between the **passing by value** and the **passing by macro** . I get that a macro modifies the original value when a **call by value** doesn't affect it. But my question is : What's the difference between a **call by macro** and a **call by name** pass? In both of these 2 cases the original values are affected after a call and not their copies. So in both these examples I think that these 2 methods act the same. I repeat that I am reffering to **call by name** and not to **call by value** – MJ13 Sep 16 '19 at 15:45
  • @MJ13: Ok, then... "Call by macro" is factitious nonsense that does not exist (macros are expanded and not called), and "call by name" is factitious nonsense that doesn't really exist either (it's just calling a function by whatever address the compiler assigns to it); and (for C) when calling a function its parameters are always passed by value (and never passed by reference, even when it's the value of a pointer being passed), and for "passed by value" the original value won't/can't be changed by the called function. – Brendan Sep 16 '19 at 18:26
  • @Brendan I just wanted an example where using **macro expansion** and **call by name** methods give **different output results** or just a comparison for the outputs on my example. Something like this: https://stackoverflow.com/questions/44490619/call-by-name-vs-call-by-macro-expansion . Anyway thanks for your time! – MJ13 Sep 17 '19 at 07:22
  • @MJ13. Ah. For "call by name" (which is better known as "pass by reference"); if C supported it (it doesn't) it'd be like it is in C++ (which supports it); and the main differences will be due to type checking; because macro expansion doesn't know or care about types (in addition to not caring if the text is data and not executable, or even if the text is valid/syntactically correct), but function's parameters do care about types (even when they're passed by reference). In other words; for `f(x)` as a macro, it'll accept integers, floating point numbers, strings, structures and anything else. – Brendan Sep 17 '19 at 09:55
1

There is nothing like "call-by-macro" in C language. There are only macros which take parameters. Macros are just textually replaced by the preprocessed tokens.

IMO macros should be used only if they are really needed, in most cases it better to use inline functions. Macros are dificult to debus (as compiler compiles the preprocessed .c file) and error prone.

Example

#define SUB(a,b) a-b

and the usage

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

the result will not be 2 only -8

0___________
  • 60,014
  • 4
  • 34
  • 74
  • Could you explain me how the output of my program would change if it was possible to compile? – MJ13 Jun 23 '19 at 15:07
  • If you were to build a hypothetical compiler, you could make it anything you want. You want rainbow colored unicorns? Your compiler could do that. – Lasse V. Karlsen Jun 23 '19 at 16:10