Sina Gz
Sina Gz

Reputation: 31

copying array vs copying int cost and performance in c

I am reading a book about c and the following paragraph is a bit unclear for me:

Surprisingly, passing the pointer is not efficient in the above example! That's because of the fact that the int type is 4 bytes and copying it is more efficient than copying 8 bytes of its pointer. But this is not the case regarding structures and arrays. Since copying structures and arrays is done byte-wise, and all of the bytes in them should be copied one by one, it is usually better to pass pointers instead.

as I know all the operations in CPU are limited to arithmetic(plus or minعس) or bit-wise kind of operation so

What does the writer mean about copying array and structure, isn't an int copying a bit shifting operation?

Second: are pointers array?

NOTE: the book name is Extreme C and published by packT and following example is what the author is referring to:

#include <stdio.h>
void func(int* a) {  
int b = 9;
*a = 5;  a = &b; 
}
int main(int argc, char** argv) {  
int x = 3;
int* xptr = &x;
printf("Value before call: %d\n", x);
printf("Pointer before function call: %p\n", (void*)xptr);  func(xptr);  
printf("Value after call: %d\n", x);
printf("Pointer after function call: %p\n", (void*)xptr);
return 0; 
}

'''

Upvotes: 2

Views: 1022

Answers (4)

Jonathon S.
Jonathon S.

Reputation: 1980

While the sample code has much to be desired and some bugs, I think that the gist of what the author is saying is that for a small data type it is more efficient to directly pass a parameter to a function by value (int) rather than by passing by pointer (int *). When a function is called, parameters are pushed onto the stack and and a type of int would require 2 bytes, but an int *parameter may require 4 or 8 bytes depending on the system.

When passing a struct as a parameter, the overall size of the struct will typically be greater than 4 or 8 bytes, so passing a pointer to thr struct may be more efficient, since only 4 or 8 bytes would need to be copied to the stack.

I am not sure why the author mentioned arrays, since an array cannot be passed to a function by value unless it is contained in a struct.

Upvotes: 1

Tony Delroy
Tony Delroy

Reputation: 106116

What does the writer mean about copying array and structure?

Let's compare two functions taking a large amount of data (e.g. a struct with lots of data members):

void f(const big_type_t* p_big_type);

void g(const big_type_t big_type);

Both can effectively read the values from the caller-specified big_type_t object, but in the former case f() need only be passed a pointer (which is typically 8 bytes on modern everyday hardware) to tell it where the caller has a big_type_t object for it to use. In the latter case g() pass-by-value argument asks the compiler to make a complete copy of the caller's big_type_t argument and copy it to a location on the stack where g() implicitly knows to find it. Every byte of the data in the struct must be copied (unless the compiler's smart enough to optimise under the as-if rule, but that's a bit of a distraction - it's generally best to write code so it's not unnecessarily inefficient if not optimised well).

For built-in arrays, the situation is different. C and C++ implicitly pass arrays by pointer, so...

void h(const int* my_array);
void i(const int my_array[]);

...are both called the same way, with the my_array argument actually being a pointer to the first int the caller specifies.

In C++ there are also std::array<>s, which are effectively struct/classes with a static-sized array data member (i.e. template <typename T, size_t N> struct array { T data_[N]; ... }). They can be passed by-value, the same as structs. So, for large std::array objects, access via a pointer or reference is more efficient than doing a full copy.

Sometimes a function really does want a copy though, as it may need to do something like sort it without affecting the caller-specified variable passed to that argument. In that case, there's not much point passing by pointer or reference.

isn't an int copying a bit shifting operation?

No... the term "bit shifting" has a very specific meaning in programming. Consider an 8-bit integer - say 0x10010110. If we shift this value one bit to the right, we get 0x01001011 - a 0 is introduced on the left, and a 0 is discarded on the right. If we shift the new value to the right again, we could get either 0x00100101 (add 0 at left; discard at right) or - what's called a circular shift or rotation - 0x100100101`, where the right-most bit is moved to become the left-most bit. Bit-shifting happens to CPU registers, and the shifted values can be stored back into the memory where a variable is located, or used in some calculation.

All that's quite unrelated to memory copying, which is where the bits in one value are (at least notionally, without optimisation) copied into "another" value. For large amounts of data, this usually does mean actually copying the bits in a value read from memory to another area of memory.

Second: are pointers array?

No they're not. But, when you have an array, it easily "decays" to a pointer to its first element. For example:

void f(const char* p);
f("hello");

In C++, "hello" is a string literal of type char[6] (as there's implicitly a null character at the end. When calling f, it decays from array form to a pointer to the first character - 'h'. That's usually desirable to give the called function access to the array data. In C++, you can also do this:

template <size_t N> void f(const char(&arr)[N]);
f("hello");

The call above does not involve decay from an array to a pointer - arr is bound to the string literal array and N is derived as 6.

Upvotes: 4

4386427
4386427

Reputation: 44274

The book is not clear and it's also wrong.

The assumption seem to be that an 8 byte pointer is "harder" to copy than a 4 byte integer. That's wrong for nearly all modern CPUs.

Further, the part about copying an array is just plain wrong. That is not what C does. Passing an array in C does not involve an copy. It's actually just like passing a pointer.

The part about structs is however correct... as long as the struct isn't just a simple integer or char but "something bigger".

What does the writer mean about copying array

Sounds like rubbish... as C doesn't pass array by doing a copy

What does the writer mean about copying ... structure,

Structs are copied by value. So passing a struct to a function involves copying every byte of the struct. That is rather expensive if the struct is large.

are pointers array?

No. Pointers are pointers. But... Under the correct circumstances a pointer can be used as an array because *(p + i) is the same as p[i]

Upvotes: 6

John Bode
John Bode

Reputation: 123468

What does the writer mean about copying array and structure, isn't an int copying a bit shifting operation?

When you pass an object of struct type as a parameter in a function, the contents of that structure are copied into the formal parameter:

struct foo { 
  ...
};

void do_something_with( struct foo arg )
{
  // do something with arg
}

int main( void )
{
  struct foo f = { 1, 2.0, "three" };
  ...
  do_something_with( f );
  ...
}

The objects main:f and do_something_with:arg are two separate instances of struct foo - when you pass f as an argument, its contents are copied into arg. Any changes you make to the contents of arg do not affect the contents of f.

The thing is, the author of the book is wrong about arrays - when you pass an array expression as an argument to a function, what you are actually passing is a pointer to the first element, not the whole array.

Second: are pointers array?

Arrays are not pointers - however, unless it is the operand of the sizeof or unary & operators, an expression of type "N-element array of T" will be converted, or "decay", to an expression of type "pointer to T" and the value will be the address of the first element of the array.

When you pass an array expression as an argument to a function, what the function actually receives is a pointer to the first element of the array - no copy of the array is made like it is for the struct above.

Finally - while runtime efficiency does matter, correctness, clarity, and maintainability matter more. If it makes sense to pass an argument as a pointer (such as you want the function to modify the argument), then by all means do so. But don't start passing everything as a pointer because it might speed things up. Start by making things clear and correct - then, measure the performance of your code and take action based on that. Most of your runtime performance gains come from using the right data structures and algorithms, not how you pass arguments.

Upvotes: 3

Related Questions