3

It is very common to declare and pass a basic data-type variable during a function invocation, can we achieve something similar with the structures ? Below code explains my question better.

struct s 
{
    int i;
    char c;
};

void f(int i)
{
    return;
}

void g(struct s s1)
{
    return;
}

int main()
{
    int i = 5;  // possible
    struct s s1 = {1, 'c'}; // possible

    f(i);   // possible
    g(s1);  // possible

    f(5);   // possible
    g({1, 'c'});    // not possible, is there any alternative way ?

    return 0;
}
m0hithreddy
  • 1,752
  • 1
  • 10
  • 17

1 Answers1

2

First of all, as a rule of thumb you should avoid passing structs by value, because that's slow and takes up lots of memory. A better interface would be:

void g (struct s* s1)
...
g(&s1);

To answer the question, you may use a compound literal:

g( (struct s){1, 'c'} );
Lundin
  • 195,001
  • 40
  • 254
  • 396
  • as per your suggestion, about passing structs by value, in that case it will be: `g(&((struct s){1, 'c'}) );` – Frankie_C May 14 '20 at 08:46
  • Ok, in general, but does it really takes a lot of memory in OP's case (a small struct with size comparable to a pointer)? – Bob__ May 14 '20 at 08:50
  • Yes, I am aware of more function stack getting consumed when passed by value, for the purpose question I declared so. But another thing, after declaring a constant can we access its address with `&` ? In case of integer constants (`&1`) we would get `lvalue` error, is it fine with structures ? – m0hithreddy May 14 '20 at 08:51
  • @Frankie_C Indeed! Though you don't actually need the outer parenthesis, since compound literals count as postfix operators and have higher operator precedence than than unary &. – Lundin May 14 '20 at 08:52
  • @Bob__ Depends on ABI and calling convention. Or a 8 bit CPU, the struct would take up 24 bits but a pointer would only take up 16 bits, but on a 32 bit PC there won't be any notable difference, since the struct is less than 32 bits. Hence "rule of thumb". You can get away with passing small structs by value on some systems. – Lundin May 14 '20 at 08:55
  • 1
    @MohithReddy A compound literal is not a constant, it's an unnamed initialized object. That means its address can be taken, it can be read from or written to, and pointers to it can be passed to other functions (as long as it is still in scope of course) – Felix G May 14 '20 at 08:57
  • 1
    @MohithReddy An integer constant `1` has no memory location (it is not a "lvalue"), but a compound literal struct works just as a local variable, except it doesn't have a name. It has an address and a local scope, in this case it is valid within main(). It works just like your explicitly declared variable `s1` in this regard. – Lundin May 14 '20 at 08:57
  • I don't agree with your rule of thumb at all. `struct` parameters can be a very efficient tool because they avoid aliasing. And combined with inlining any copy overhead usually disappears. For using variants with pointers (if your are arguing about efficiency) that should at least be `const` qualified for the target type and `restrict` qualified for the pointer. – Jens Gustedt May 14 '20 at 08:58
  • @Frankie_C Just for the nitpicking record: You meant "passing structs by *reference*", not "passing structs by *value*". – RobertS supports Monica Cellio May 14 '20 at 09:03
  • You should probably add a note that this only works in C99 or above. – DarkAtom May 14 '20 at 09:06
  • @Lundin yes, it's my habit to make more readable the code (and also because some IDE's annoys suggesting use of parenthesis). – Frankie_C May 14 '20 at 09:07
  • @RobertSsupportsMonicaCellio yes I the meaning is 'pass structs by reference'. Sorry for the typo. – Frankie_C May 14 '20 at 09:08
  • @JensGustedt Those concerns are quite advanced and not something beginners should consider though (newbies stop reading here). To address your concerns: first of all, most functions taking struct parameters are likely reside in a different translation unit than the caller, so inlining might not be feasible. And looking at most ABIs out there, structs passed by value are most often making things complicated. ABI documentation for structs is not trivial, if at all documented. -> – Lundin May 14 '20 at 09:13
  • Structs also introduce an alignment requirement that might not have existed for the individual members (if they are uint8_t etc), causing padding overhead. Furthermore, structs enforce an allocation order which may in turn even prevent certain optimizations, if we are unlucky. -> – Lundin May 14 '20 at 09:14
  • Regarding aliasing, optimization of plain types passed through pointers can be done with `restrict` indeed, if needed. No aliasing is far more likely to be a burden than an asset though. Consider a function taking `struct s { int x; int y; }` and the caller got an `int[2]` array. Now even if the struct allocates the two integers adjacently with no padding, you still can't call the function passing on the array, because you wouldn't have compatible types and it would lead to aliasing violations. – Lundin May 14 '20 at 09:14
  • @Lundin, those concerns are not more advanced than the ones you raised in your answer. Conceptually passing in a `struct` is much simpler, and as long as you are not too concerned by performance, this should be preferred. But *if* you are concerned by performance, then there is much more to it than just passing in a pointer. So overall, as short as you put it in your answer, this is just bad advice. – Jens Gustedt May 14 '20 at 12:19
  • @JensGustedt If we restrict the discussion to existing real-world computers, then passing a struct by value only performs well in case doing so is sensible for the given ABI. Again, it is most often not [see this for x86](https://stackoverflow.com/questions/42411819/c-on-x86-64-when-are-structs-classes-passed-and-returned-in-registers) and that's for a high end CPU. If you think understanding that ABI for structs is trivial, you are probably quite alone. -> – Lundin May 14 '20 at 13:01
  • Don't get me started on low-end microcontrollers. I remember a rookie who managed to kill his entire PIC16 stack with a single pass-struct-by-value function call. On the low end ISAs it's often far better to provide a struct pointer than a parameter list even. Something like `void func (int a, int b, int c, int d)` could generate quite slow, memory-consuming code on 8 bit MCUs, but `void func (const struct s* obj)` only uses a single index register and vastly outperforms the parameter list. – Lundin May 14 '20 at 13:01
  • @lundin, did you read what I just said? It was you who pushed all of this towards performance in your answer, with your "rule of thumb". All I am saying is that this is not a good generic rule and that it should not be used to sacrifice security. In many cases it will just be premature optimization. – Jens Gustedt May 14 '20 at 13:57
  • @JensGustedt Needless stack usage is to sacrifice security, since passing structs by value significantly increases the potential for stack overflows. – Lundin May 14 '20 at 14:01