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;
}
'''
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]
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.
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.
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.
Related
#include <iostream>
using namespace std;
int func(int ar[5]){
//the code is written here with returning whatever the requirement is
}
int main(){
int ar[5];
func(ar);
return 0;
}
In this kind of situation where we are passing array through a function why the address of the array is used in actual parameter whereas the array in formal parameter?
This is because C handled arrays weirdly.
In C, arrays convert to a pointer to their first element at the drop of a hat, cannot be passed as arguments to a function, cannot be returned from functions, and cannot be copied by assignment.
C++, originally based off C, carries these screwed up design decisions as a legacy issue. Fixing them would break insane amounts of code.
If you want an array that behaves more reasonable, use std::array.
void foo(int[5]);
this is actually taking an int* argument.
int arr[7];
foo(arr);
this converts the 7 element arr to a pointer and passes it to foo. Which seems to take int[5] but that 5 does nothing.
Yes this is utterly crazy.
Now,
void bar(int(&)[5])
because C does not have references, the above is an actual reference to an array if 5 elements, so
bar(arr)
won't compile.
There is nothing weird, nor screwed up about how arrays are passed in C. C is expressed pretty simply, really. C /does/ have references; they are explicit, so, easy to see when they're in use. We call them pointers.
One simply needs to understand that there is not dedicated storage for array types, that arrays are not first-class types, so, won't behave like first-class types. Can't be assigned into (directly), nor passed into and out of functions directly.
In C/C++ we use pointers to refer to a chunk of memory where the contents of the array are stored.
So, all that's left is that we understand that when we declare an array, the name of the array is really a pointer to the beginning of the array.
int a[12] ;
/* a's type is this: */
int *a ;
So, when we "pass an array" to a function, we are copying the pointer into the function. The semantics are straightforward, and entirely consistent (unlike Java).
The array, itself, is never passed directly.
Finally, realise that there is no difference between a pointer to a single integer, and a pointer to a contiguous chunk of integers in memory.
Remember, a[i] is equivalent to *(a+i). So, in this example:
int i = 12 ;
int *p = &i ;
, *p is the same as *(p+0); the p can be thought of as an array of length 1.
With a slight change in perspective you should be able to see the simplicity, the elegance, that is C.
So, I always knew that the array "objects" that are passed around in C/C++ just contained the address of the first object in the array.
How can the pointer to the array "object" and it's contained value be the same?
Could someone point me towards more information maybe about how all that works in assembly, maybe.
Short answer: A pointer to an array is defined to have the same value as a pointer to the first element of the array. That's how arrays in C and C++ work.
Pedantic answer:
C and C++ have rvalue and lvalue expressions. An lvalue is something to which the & operator may be applied. They also have implicit conversions. An object may be converted to another type before being used. (For example, if you call sqrt( 9 ) then 9 is converted to double because sqrt( int ) is not defined.)
An lvalue of array type implicitly converts to a pointer. The implicit conversion changes array to &array[0]. This may also be written out explicitly as static_cast< int * >( array ), in C++.
Doing that is OK. Casting to void* is another story. void* is a bit ugly. And casting with the parentheses as (void*)array is also ugly. So please, avoid (void*) a in actual code.
You are mixing two unrelated (and, actually, mutually exclusive) things, which creates more confusion.
Firstly, you are correctly stating that "array objects that are passed around in C/C++ just contained the address of the first object in the array". The key words here are "passed around". In reality arrays cannot be passed around as array objects. Arrays are not copyable. Whenever you are using an array-style declaration in function parameter list it is actually interpreted as pointer declaration, i.e. it is a pointer that you are "passing around", not the array. However, in such situations your equality does not hold
void foo(int a[]) {
assert((void *) &a == (void *) a); // FAIL!!!
}
The above assertion is guaranteed to fail - the equality does not hold. So, within the context of this question you have to forget about arrays that you "pass around" (at least for the syntax used in the above example). Your equality does not hold for arrays that have been replaced by pointer objects.
Secondly, actual array objects are not pointers. And there's no need to take the term object into quotation markes. Arrays are full-fledged objects, albeit with some peculiar properties. The equality in question does indeed hold for the actual arrays that have not lost their "arrayness", i.e. array object that have not been replaced by pointer objects. For example
int a[10];
assert((void *) &a == (void *) a); // Never fails
What it means is that numerically the address of the entire array is the same as the address of its first element. Nothing unusual here. In fact, the very same (in nature) equality can be observed with struct types in C/C++
struct S { int x; } a;
assert((void *) &a == (void *) &a.x); // Never fails
I.e. the address of the entire struct object is the same as the address of its first field.
How can the pointer to the array "object" and it's contained value be the same?
An array is a contiguous block of memory which stores several elements.
Obviously, the first element in the array is located at some address.
There's no data "in between" the first element and the beginning of the actual array.
Therefore, the first element has the same address as the array.
Please read the following thread
http://www.cplusplus.com/forum/beginner/29595/
It basically explains that (&a != a) due to the type difference (since &a returns the pointer to the array and a to the first element) even though they both point to the same address.
Since you are casting them both to (void*) only the address value is compared and found to be equal, meaning that ((void*) a == (void*)&a) as you've stated. This makes sense since the array's address has to be the same as the first elements.
Let's look at these two declarations:
int a[4];
int * b;
Both a and b have a type compatible with int * and can, for example, be passed as an argument to a function expecting int *:
void f(int * p);
f(a); // OK
f(b); // OK
In case of a, the compiler allocates space for 4 int values. When you use the name a, such as when calling f(a), the compiler just substitutes the address of where it allocated the first of those int values, since it knows.
In case of b, the compiler allocates space for one pointer. When you use the name b, such as when calling f(b), the compiler generates code for retrieveing the pointer value from the allocated storage.
When it comes to &, that's when the difference between a and b becomes apparent. & always means the address of the storage the compiler has allocated for your variable: &a is the address of those four int values (therefore coinciding with just a), while &b is the address of the pointer value. They have different types, too.
&a is not exactly the same as a, though, even though they compare as equal. They have a different type: &a is a pointer and a is an array. You can notice the difference, for example, if you apply the sizeof operator to these expressions: sizeof(a) will evaluate to the size of four int values, while sizeof(&a) is the size of a pointer.
Ok, So what I thought happened is that when you created an array, you allocated space for the array somewhere and you created a pointer to its first object somewhere else, and what you passed around in your code was the pointer.
This is actually the behavior of what happens when you create an array with new in C++ or with malloc in C/C++. As such,
int * a = new a[SIZE];
assert((void*)&a==(void*)a); // Always fails
What I learned is that for arrays declared in the style of int a[SIZE];, a pointer to the first element is created when you try to pass the array to a function (this is called array-pointer decay). It's interesting to note that, indeed, as AndreyT writes,
void foo(int a[]) {
assert((void *) &a == (void *) a); // Always fails
}
This shows that it's only when you try to pass arrays around that a pointer is created for arrays in the style of int a[SIZE];.
I am a little bit confused about pass an array in C/C++. I saw some cases in which the signature is like this
void f(int arr[])
some is like this
void f(int arr[], int size)
Could anybody elaborate what's the difference and when and how to use it?
First, an array passed to a function actually passes a pointer to the first element of the array, e.g., if you have
int a[] = { 1, 2, 3 };
f(a);
Then, f() gets &a[0] passed to it. So, when writing your function prototypes, the following are equivalent:
void f(int arr[]);
void f(int *arr);
This means that the size of the array is lost, and f(), in general, can't determine the size. (This is the reason I prefer void f(int *arr) form over void f(int arr[]).)
There are two cases where f() doesn't need the information, and in those two cases, it is OK to not have an extra parameter to it.
First, there is some special, agreed value in arr that both the caller and f() take to mean "the end". For example, one can agree that a value 0 means "Done".
Then one could write:
int a[] = { 1, 2, 3, 0 }; /* make sure there is a 0 at the end */
int result = f(a);
and define f() something like:
int f(int *a)
{
size_t i;
int result = 0;
for (i=0; a[i]; ++i) /* loop until we see a 0 */
result += a[i];
return result;
}
Obviously, the above scheme works only if both the caller and the callee agree to a convention, and follow it. An example is strlen() function in the C library. It calculates the length of a string by finding a 0. If you pass it something that doesn't have a 0 at the end, all bets are off, and you are in the undefined behavior territory.
The second case is when you don't really have an array. In this case, f() takes a pointer to an object (int in your example). So:
int change_me = 10;
f(&change_me);
printf("%d\n", change_me);
with
void f(int *a)
{
*a = 42;
}
is fine: f() is not operating on an array anyway.
WHen an array is passed in C or C++ only its address is passed. That is why the second case is quite common, where the second parameter is the number of elements in the array. The function has no idea, only by looking at the address of the array, how many elements it is supposed to contain.
you can write
void f( int *arr, int size )
as well, having latter (size) allows to not step outside the array boundaries while reading/writing to it
C and C++ are not the same thing. They have some common subset, though. What you observed here is that the "first" array dimension when passed to a function always results just in a pointer being passed. The "signature" (C doesn't use this term) of a function declared as
void toto(double A[23]);
is always just
void toto(double *A);
That is that the 23 above is somewhat redundant and not used by the compiler. Modern C (aka C99) has an extension here that lets you declare that A always has 23 elements:
void toto(double A[static 23]);
or that the pointer is const qualified
void toto(double A[const 23]);
If you add other dimension the picture changes, then the array size is used:
void toto(double A[23][7]);
in both C and C++ is
void toto(double (*A)[7]);
that is a pointer to an array of 7 elements. In C++ these array bounds must be an integer constant. In C it can be dynamic.
void toto(size_t n, size_t m, double A[n][m]);
They only thing that you have to watch here is that here n and m come before A in the parameter list. So better you always declare functions with the parameters in that order.
The first signature just passes the array with no way to tell how big the array is and can lead to problems with out-of-bounds errors and/or security flaws.\
The second signature is a more secure version because it allows the function to check against the size of the array to prevent the first versions shortcomings.
Unless this is homework, raw arrays are a bit out-dated. Use std::vector instead. It allows passing the vector around without having to manually pass the size as it does this for you.
The size of an array is not passed with the array itself. Therefore, if the other function needs the size, it will have it as a parameter.
The thing is, some functions implicitly understand the array to be of a certain size. So they won't need to have it specified explicitly. For example, if your function operates on an array of 3 floats, you don't need the user to tell you that it is an array of 3 floats. Just take an array.
And then there are those functions (let's call them "terrible" because they are) that will fill an array in with arbitrary data up to a point defined by that data. sprintf is probably the "best" example. It will keep putting characters in that array until it is finished writing them. That's very bad, because there's no explicit or implicit agreement between the user and the function as to how big this array could be. sprintf will write some number of characters, but there's no way for the user to know exactly how many get written (in the general case).
Which is why you should never use sprintf; use snprintf or _snprintf, depending on your compiler.
Anytime you need to know the size of the array, it needs to be provided. There is nothing special about the two forms of passing the array itself; the first parameter is the same either way. The second method simply provides the information needed to know the size of the array while the first does not.
Sometimes the array itself holds the information about its size, though. In your first example, for instance, perhaps arr[0] is set to the size of the array, and the actual data begins at arr[1]. Or consider the case of c-strings... you provide just a char[], and the array is assumed to end at the first element equal to \0. In your example, a negative value may act as a similar sentinel. Or perhaps the function simply doesn't care about the array's size, and will simply assume it is large enough.
Such methods are inherently unsafe, though... it is easy to forget to set arr[0] or to accidently overwrite the null terminator. Then, f suddenly has no way of knowing how much space it has available to it. Always prefer to provide the size explicitly, either via a size parameter like you show, or with a second pointer to the end of the array. The latter is the method generally taken by the standard library functions in C++. You still have the issue of providing an incorrect size, though, which is why in C++ it isn't recommended you ever use such an array in the first place... use an actual container that will keep track of that information for you.
The difference is that the second one includes a parameter that indicates the array size. The logical conclusion is that if you don't use such a parameter, the function doesn't know what the array size is. And this indeed turns out to be the case. In fact, it doesn't know you have an array. In fact, you don't have to have an array to call the function.
The array syntax here, without a specified size inside the square brackets, is a fake-out. The parameter is actually a pointer. For more information, please see http://c-faq.com/aryptr/index.html , especially section 4.
Hey there,
I wonder if it's worth passing primitive single values like int, float, double or char by pointer? Probably it's not worth!? But if you would simply pass everything by pointer, is this making the program slower?
Should you always just pass arrays as pointer?
Thanks!
I wonder if it's worth passing primitive single values like int, float, double or char by pointer?
What are you trying to accomplish? Do you want to be able to write to the passed in value? Or do you just need to use it? If you want to write to it, the idiomatic way is to pass by reference. If you don't need to write to it, you're best avoiding any risk that you'll write to it accidentally and pass by value. Pass by value will make a copy of the variable for local use. (as an aside, if you don't want to make a copy AND want some level of safety, you can pass by const reference)
But if you would simply pass everything by pointer, is this making the program slower?
Difficult to say. Depends on a lot of things. In both pass by value and pass by reference (or pointer) your making a new primitive type. In pass by value, you're making a copy. In pass by reference/pointer you're passing an address to the original. In the latter case, however, you're requiring an extra fetch of memory that may or may not be cached. Its very difficult to say 100% without measuring it.
That all being said, I doubt the difference is even noticeable. The compiler may be able to optimize out the copy in many pass-by-value cases, as indicated in this article. (thanks Space C0wb0y).
Should you always just pass arrays as pointer?
From this.
In C++ it is not possible to pass a complete block of memory by value as a parameter to a function, but we are allowed to pass its address.
To pass an array:
int foo(int bar[], unsigned int length)
{
// do stuff with bar but don't go past length
}
I'd recommended avoiding arrays and using std::vector which has more easily understood copy semantics.
It's probably not worth passing primitive values by pointer if your concern is speed -- you then have the overhead of the "indirection" to access the value.
However, pointers often are the "width of the bus", meaning the processor can send the whole value at once, and not "shift" values to send-down-the-bus. So, it is possible pointers are transferred on the bus faster than smaller types (like char). That's why the old Cray computers used to make their char values 32 bits (the width of the bus at that time).
When dealing with large objects (such as classes or arrays) passing pointer is faster than copying the whole object onto the stack. This applies to OOP for example
Look in your favorite C++ textbook for a discussion of "output parameters".
Some advantages of using a pointer for output parameters instead of a reference are:
No surprising behavior, no action at a distance, the semantics are clear at the call site as well as the caller.
Compatibility with C (which your question title suggests is important)
Usable by other languages, functions exported from a shared library or DLL should not use C++-only features such as references.
You should rarely have to pass anything by pointer. If you need to modify the value of the parameter, or want to prevent a copy, pass by reference, otherwise pass by value.
Note that preventing a copy can also be done by copy-elision, so you have to be very careful not to fall into the trap of premature optimization. This can actually make your code slower.
There's is no real answer to your question except few rules that I tend to bare in mind:
char is 8 bytes and a pointer is 4 bytes so never pass a single char as a pointer.
after things like int and float are the same size as a pointer but a pointer has to be referenced so that technically takes more time
if we go to the pentium i386 assembler:
loading the value in a register of a parameter "a" in C which is an int:
movl 8(%ebp),%eax
the same thing but passed as a pointer:
movl 8(%ebp),%eax
movl (%eax),%eax
Having to dereference the pointer takes another memory operation so theorically (not sure it is in real life) passing pointers is longer...
After there's the memory issue. If you want to code effectively everything composed type (class,structure,arrays...) has to be passed by pointer.
Just imagine doing a recursive function with a type of 16bytes that is passed by copy for 1000 calls that makes 16000 bytes in the stack (you don't really want that do you ? :) )
So to make it short and clear: Look at the size of your type if it's bigger than a pointer pass it by pointer else pass it by copy...
Pass primitive types by value and objects as const references. Avoid pointers as much as you can. Dereferencing pointers have some overhead and it clutters code. Compare the two versions of the factorial function below:
// which version of factorial is shorter and easy to use?
int factorial_1 (int* number)
{
if ((*number) <= 1)
return 1;
int tmp = (*number) - 1;
return (*number) * factorial_1 (&tmp);
}
// Usage:
int r = 10;
factorial_1 (&r); // => 3628800
int factorial_2 (int number)
{
return (number <= 1) ? 1 : (number * factorial_2 (number - 1));
}
// Usage:
// No need for the temporary variable to hold the argument.
factorial_1 (10); // => 3628800
Debugging becomes hard, as you cannot say when and where the value of an object could change:
int a = 10;
// f cound modify a, you cannot guarantee g that a is still 10.
f (&a);
g (&a);
Prefer the vector class over arrays. It can grow and shrink as needed and keeps track of its size. The way vector elements are accessed is compatible with arrays:
int add_all (const std::vector<int>& vec)
{
size_t sz = vec.size ();
int sum = 0;
for (size_t i = 0; i < sz; ++i)
sum += vec[i];
}
NO, the only time you'd pass a non-const reference is if the function requires an output parameter.
I have a homework assignment with a number of questions. One is asking why the strcpy() function doesn't need the call by reference operator for CStrings. I've looked through the book numerous times and I can't, for the life of me, find the answer. Can anyone help explain this to me?
It is an array of sorts so I would think you would need the call by reference.
strcpy() takes a pointer to char.
Thus you don't pass the "string" as a parameter but only the address of its first character.
So basically you have something like this:
void func(const char* str);
const char* str = "abc";
func(str); // Here str isn't passed by reference but since str is a pointer to the first character ('a' here), you don't need a reference.
Passing a pointer is fast. On a 32 bits architecture, a pointer takes 32 bits, whatever the length of the pointed string.
If you mean class CString, then in other words the question asks you:
Why does this compile?
CString sExample;
char buffer[LARGE_ENOUGH];
strcpy(buffer, sExample);
The answer is, because class CString defines an operator const char* and therefore can be converted to the type of strcpy's second argument.
I 'm not sure if this is what you mean though.
This a problem of terminology, mostly.
An "object" (I use the term as designing "a chunk of RAM") is passed by value when the called function gets a copy of the chunk. It is passed by reference when the called function gets a way to access the one and only chunk.
Consider this:
void f(int x)
{
x = 42;
}
void g()
{
int y = 54;
f(y);
// here "y" still has value 54
}
Here, the function f() modifies x, but that is its own x, a variable which contains a copy of the contents of the y variable of g(). What f() does with its x does not impact what the y of g() contains. The variable is then passed by value.
C++ has a notion of reference which goes like this:
void f(int& x)
{
x = 42;
}
void g()
{
int y = 54;
f(y);
// here "y" now has value 42
}
Here, the special construction (with the "&") instructs the C++ compiler to play some hidden tricks so that the x known by f() is actually a kind of alias on the y variable used by g(). There is only one variable: the x and the y designate the same chunk of RAM. The variable is here passed by reference.
Now, consider a "C string". In C (and C++), a string is just a bunch of char values, the last of which having value 0 (this is the conventional string terminator). The code does not handle strings directly; it uses pointers. A pointer is a value which actually designates an emplacement in RAM. The pointer is not the string; the pointer is a kind of number (usually on 32 or 64 bits, it depends on the processor type and the operating system) which tells where in RAM is the first char of the string. So when you call strcpy() you actually give it pointers (one for the destination buffer, one for the source string). Those pointers are unmodified: the source string and the destination buffers are not moved in the process; only the contents of the string are copied into the destination buffer.
Hence, strcpy() needs not have access to the variables which contain the pointers in the caller code. strcpy() only needs to know where the destination buffer and the source strings are in RAM. Giving a copy of the pointer values to strcpy() is enough. Hence, those pointers are passed by value.
Note that in the presence of pointers, there are two objects to consider: the variable which contains the pointer, and the chunk of RAM which is pointed to. The pointer itself is passed by value (strcpy() receives its own copy of the variable which contains the pointer to the destination buffer). We can say that the pointed-to chunk of RAM (the destination buffer) is "passed by reference" since it is not duplicated in the process and the called function (strcpy()) can modify it. The point here is that the term "reference" has two distinct meanings:
The C++ syntax meaning: "reference" designates the special construction with the "&" that I have described above.
The language theory formal meaning: "reference" designates a way by which a value is indirectly designated, so that caller and callee may access the same chunk of RAM under distinct names. With that meaning, passing by value a pointer to a called function is equivalent to passing by reference the chunk of RAM to which the pointer points.
A C++ "reference" (first meaning) is a syntaxic way to pass "by reference" (second meaning) a variable.
Well in the case you mean c-strings (char*) you don't need a call by reference because the string itself is a pointer. So string copy knows where to/from where to copy the string.
Because strcpy works with char* which are pointers. The pointer is passed by value, and strcpy uses that pointer to access the indiviual characters in the target string and change them. Compare that to passing an integer by value - the function can't change the original integer.
Understanding how char* strings are not like integers is vital to you not going crazy during your C++ course. Well done for your prof making you face it.
Because in C when calling functions, arrays are passed as the address of the first element, which is equivalent of calling by reference.
See Peter Van Der Linden Expert C programming, Deep secrets book.
automatic type conversion, is the answer I guess they're looking for. Looking that turn up might give you some help.