Why do certain C/C++ functions use pointers as parameters?
I'm coming from C# and I'm learning C++ (with this tutori开发者_StackOverflow社区al) so I have a relatively insubstantial amount of knowledge on memory, but the only use I see in pointers is "saving space" and iterating through arrays. So why do functions like the scanf
function take pointers as parameters? For instance:
printf("What's your name?: ");
and then:
scanf("%s",&userName)
. Wouldn't it make more sense to just pass the variable itself as an argument? The C book I was looking at said that using the variable itself would produce unexpected results, but I don't see why. Could anyone please enlighten me in a C++ fashion? I switched from learning C because I realized how much I love OOP.
There are 3 different ways of passing a variable to a function in C++, pass by copy, pass by reference and pass by pointer.
#include <iostream>
void passByCopy(int a)
{
a += 1;
}
void passByReference(int &a)
{
a += 1;
}
void passByPointer(int *a)
{
(*a) += 1; // De-reference then increment.
}
int main()
{
int a = 0;
// Passing by copy, creates a copy of the 'a' object, then sends it to the function.
passByCopy(a);
std::cout << a << std::endl; // Outputs 0
// Passing by reference, causes the 'a' object in the function to reference the 'a'
// object at this scope. The value of 'a' will change.
passByReference(a);
std::cout << a << std::endl; // Outputs 1
// Passing by pointer, does almost the same thing as a pass by reference, except a
// pointer value can by NULL, while a reference can't.
passByPointer(&a);
std::cout << a << std::endl; // Outputs 2
}
With scanf, the purpose of the function is to pass values to variables in the current scope, so it can't use pass by copy. It doesn't use pass by reference for two reasons, one is that it is an old C function, so pass by reference didn't exist when it was written. The second is that it is a variadic function, which means that internally the function receives a list of pointers rather than a series of arguments.
C and C++ pass variables by value; the formal parameter is a different object in memory from the actual parameter. Thus, if the formal parameter is modified in the function, the value of the actual parameter isn't changed.
By passing a pointer, the function can modify the contents of the actual parameter:
void swap(int *a, int *b)
{
int t = *a;
*a = *b;
*b = t;
}
...
swap(&x, &y);
Thus, writing to *a
in swap
is equivalent to writing to x
in the caller.
Pointers in C serve three main purposes:
- Faking pass-by-reference semantics, as above (as Karl notes in the comments, C++ has a mechanism that supports true pass-by-reference);
- Tracking dynamically-allocated memory (the memory allocation functions
malloc
,calloc
, andrealloc
and the C++new
operator return pointer values); - Building dynamic data structures (trees, lists, queues, stacks, etc.).
All parameters are passed by value in C. If you want to pass by reference, you need to explicitly pass the address of the variable in question. Passing by reference is important when you want the function to modify the variable passed in.
C++ has references, but since C++ code often calls C library functions, you still see the same syntax used in C++ sometimes.
In your specific example, scanf is part of the C standard library. C does not have a string class, so strings are represented as arrays of characters.
In C, the address of the first character is usually passed when strings are required. This can either be &str[0]
or str
. I'm not sure it's correct to have &username
if it's expecting a char *
, I guess it depends on username.
C has no concept of passing by reference and can return only a single value. The traditional purpose of passing by pointer is therefore to allow the called function to write to a variable so that the caller can read what has been written.
C++ has the ability to pass by reference, but scanf
and printf
are C functions that are also available when building as C++ so that isn't available to them.
EDIT: to explain further, even putting beside the variable arguments issue, in the following code:
void calledFunction(int var1, int var2)
{
var1 = 23;
var2 = 19;
}
void callingFunction(void)
{
int v1 = 9, v2 = 18;
calledFunction(v1, v2);
printf("%d %d", v1, v2);
}
The output is "9 18". calledFunction
gets local copies of v1
and v2
because C passes by value, meaning that the values of v1
and v2
were passed in, but no other link was kept to the originals. So the fact that it modifies its copies has no effect on the originals.
In C there are no references, all the arguments are passed by value.
If scanf
would be in C# then its parameters would be preceded by out
or ref
keyword and no pointers would be needed. Passing pointers allow the scanf
function to write some values to whatever place the pointers point to.
In C++ however there are references, though different from what you know from C#. Nonetheless if you'd use iostream
library and not stdio
which was inherited to C++ from C, then you wouldn't have to use pointers in this way. You'd just write:
String username;
cin >> username;
One other use of pointers that I don't see mentioned in the other anwers here is that it's one of only two ways that a C function can return multiple values. A C function is defined to return a single object, but it can receive many arguments. If your multi-valued function always returns the same set of values, it's a question of convenience whether to wrap the values in a struct and have the function return the struct or have the function receive pointers through which it can write the values.
The pointer-parameter option becomes much more attractive if the function also needs to return a status or error code (because it's silly to write that through a pointer, forcing the caller to allocate another variable and obstructing standard idioms like if(f()==ERROR)...
). And if the set of values is not predictable at compile time (like scanf, which must interpret the format string at runtime), then pointer-parameters become the only (sensible) option.
Edit: In your example, scanf("%s",&username);
assuming username is an array type or a pointer to storage suitable for holding a character string, you don't need to pass the address. If it's an array, it will be passed as a pointer implicitly. And if it already is a pointer, taking the address yields a pointer-to-pointer, which is not suitable to hold a string. So drop the &
here.
scanf
is a C function. C doesn't have references, so it has to use pointers in order to write to arguments. Also, scanf
is a varargs function, so it takes variable numbers of arguments. And varargs functions can only take built-in types: pointers (to anything), integers, doubles.
精彩评论