3

I am confused because I haven't written C in a while. In C++, we would pass them as references, in order not to copy the whole struct. Does this apply to C too? Should we pass them as pointers, even if we don't want to modify them, in order to avoid copying?

In other words, for a function that checks if two structs are equal, we better do

int equal(MyRecord* a, MyRecord* b);

and decrease a bit the readability (because of pointers)

or

int equal(MyRecord a, MyRecord b);

will have the same performance?

Shoe
  • 74,840
  • 36
  • 166
  • 272
gsamaras
  • 71,951
  • 46
  • 188
  • 305
  • 1
    I've removed the C++ tag because it appears to be a C related question. – Shoe Oct 11 '14 at 10:57
  • You can pass the address of the struct in the first case, or you can typedef a pointer type to MyRecord and use the second example, but keep in mind that if you want to pass as a reference, you need to pass an address as an arguments – Andrea Oct 11 '14 at 10:57
  • 3
    And use ``const`` to prevent accidential modification, if desired. – Jonas Schäfer Oct 11 '14 at 10:59
  • OK Sofffia. The point is which is faster. Secondly I care for readability in the scope of this question. – gsamaras Oct 11 '14 at 11:00
  • 2
    There is no strict rule, you will have to measure your code. – 2501 Oct 11 '14 at 11:02
  • The method of your second example, in C, will copy the whole structs to a work area (created by the compiler) and copy it again when the function returns. And may I suggest that you make pointers obvious, as that will greatly increase the readability of your code. for instance: int equal(const MyRecord* pA, const MyRecord* pB); – user3629249 Oct 12 '14 at 06:40

3 Answers3

6

Often, passing pointers is faster - and you'll call equal(&r1, &r2) where r1 and r2 are local struct variables. You might declare the formals to be const pointers to a const structure (this could help the optimizing compiler to generate more efficient code). You might also use the restrict keyword (if you are sure you'll never call your equal with two identical pointers, e.g. equal(&r1,&r1), i.e. without pointer aliasing).

However, some particular ABIs and calling conventions may mandate particular processing for some few particular structures. For example, the x86-64 ABI for Linux (and Unix SVR4) says that a struct with two pointers or integral values will be returned thru two registers. This is usually faster than modifying a memory zone with its pointer in a register. YMMV.

So to know what is faster, you really should benchmark. However, passing a large-enough struct (e.g. with at least 4 integral or pointer fields) by value is almost always slower than passing a pointer to it.

BTW, what really matters on current desktop and laptop processors is the CPU cache. Keeping frequently used data inside L1 or L2 cache will increase performance. See also this.

Community
  • 1
  • 1
Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
  • It's not a question of the function prototype whether the compiler can do optimizations, but of the object itself the address to which is passed. (But if the object is `const` the function needs a matching prototype to avoid an ugly cast, that comment isn't really wrong, but somehow misses the point IMO.) – mafso Oct 11 '14 at 11:43
  • I'm not sure. If you use pointer to `const` structure, and the compiler inlines `equal(&r1,&r1)`, I guess it might optimize better – Basile Starynkevitch Oct 11 '14 at 11:51
  • I played around with it with `clang` (couldn't get `gcc` to do such optimizations), and it's with me... The problem is: If you have a non-const structure, have its address taken and pass that to a function expecting a pointer to const, this function is allowed to cast const away and modify the object. The same is true the other way round: If you have a const object, take its address, cast const away, pass that to a function, the function must not modify the object pointed-to. The prototypes don't matter, the only thing what does is the declared type of the object itself. – mafso Oct 11 '14 at 12:19
3

What is faster massively depends on the size of the struct and it’s use inside the called function.

If your struct is not larger than a pointer, passing by value is the best choice (less or equal amount of data needs to be copied).

If your struct is larger than a pointer, it heavily depends on the kind of access taking place inside the called function (and appearantly also on ABI specifics). If many random accesses are made to the struct, it may be faster to pass by value, even though it’s larger than a pointer, because of the pointer indirection taking place inside the function.

All in all, you have to profile to figure out what’s faster, if your struct is larger than a pointer.

Jonas Schäfer
  • 20,140
  • 5
  • 55
  • 69
0

Passing pointers is faster, for the reasons you say yourself.

Actually, I find C more readable than C++ in this case: by passing a pointer in the call, you acknowledge that your paramters might get changed by the called function. With C++ references, you can't immediately say that by seeing only the call, you also have to check out the called function prototype to see if it uses references.

SukkoPera
  • 621
  • 4
  • 8
  • Well the point is not C VS C++ readability, but the first sentence you wrote. I will wait a bit and then maybe I will accept your answer. :) – gsamaras Oct 11 '14 at 11:02
  • You'll take my +1, but I will accept another answer for focusing more on the point. :) – gsamaras Oct 11 '14 at 12:48
  • 1
    Well, if you have more doubts just ask. You seemed to know enough from your initial question. – SukkoPera Oct 11 '14 at 13:24