I'm reading C++ Primer and in section 6.2 it says:
"Parameter initialization works the same way as variable
initialization."
Yet when I do:
void foo(char* args[]) {return;}
int main() {
char* args[]={"asd","dsa"}; // ok.
foo({"asd","dsa"}); // error.
}
Why is that?
As #T.C. pointed out in the comments, the args in the function argument is converted to a char** because functions can't take arrays as an argument. Since you can't do
char **asd={"asd","dsa"};
the code is illegal. My confusion came from the fact that
char* args[]={"asd","dsa"};
char **asd=args;
is legal.
It is generally possible to take advantage of the new initialization syntax and semantics to use anonymous arrays as arguments, but you will have to jump through a few hoops. For example
typedef const char *CC2[2];
void foo(const CC2 &a) {}
int main() {
foo({ "asd", "dsa" });
}
However, in your case this technique will not help because you are requesting an array-to-pointer conversion on a temporary array. This is illegal in C++.
typedef int A[2];
const A &r = A{ 1, 2 }; // reference binding is OK
int *p = A{ 1, 2 }; // ERROR: taking address is not OK
So, if you really want to do something like this, you can do the following
template <size_t N> void foo(const char *const (&args)[N]) {}
int main() {
foo({ "asd", "dsa" });
}
but that is not exactly what you had in mind originally.
"Parameter initialization works the same way as variable initialization."
This is true as far as calling constructors, I think they mean. However, initializers are special, and different from ordinary value expressions you can assign to a variable or pass to a function call.
C/C++ doesn't have a way to write a literal anonymous array. You can only do it as an initializer as you declare a variable.
Why is that?
First of all in both cases, you need char const* because you are working with string literals.
Secondly, {...} could work if the parameter type was an array, but char*[] is adjusted to char** (due to decayment) which cannot be initialised with a braced-init-list.
Alternatively use std::string and std::vector, as you are already supposed to do:
void foo(std::vector<std::string> args) {return;}
and:
foo({"asd","dsa"});
will work just fine.
Related
I am using PRQA QA C++ as source code analyzer.
This is the first code I analyzed :
void test1(int * var);
void example1()
{
int var1[10];
test1(var1);
}
QA C++ told me
Array type is used as a pointer type argument in the function call.
So, I tried this following example (as it suggested) :
void test2(int (&var)[10]);
void example2()
{
int var2[10];
test2(var2);
}
This time, it told me :
Array type is used as a reference type argument in the function call.
Is there a better solution to use an array parameter ?
The original warning is fine, the second warning is also true.
This is due to arrays decaying to pointers, so var1, originally an array of integers can be used in an expression requiring a pointer.
If you really want to remove these, there are several options:
std::array<int, 10> var1;
test1(var1.data());
Of better:
void test2(std::array<int, 10>& var);
void example2()
{
std::array<int, 10> var2;
test2(var2);
}
Then the second option fixes the size of the array. If it needs to be variable but fixed at compile time, use a template, otherwise use a std::vector instead of a C-style array.
I know it will work. I am more interested of what conversion happens if I send it. Is it better for me to change the parameter to char* and then converted it myself using to_String? Or is this fine as it is, in the term of creating unexpected errors in the long run ?
std::string has a constructor which will take care of this case:
string (const char* s);
that makes the following work:
char *s = "Hello, World!";
std::string str(s);
As soon as s is not NULL not long run errors will take place.
docs here
since std::string has a constructor that accepts const char* the compiler is smart enough to use it and allow you to pass const char* as an std::string argument. see http://en.cppreference.com/w/cpp/language/implicit_conversion
In c++, when a type T has a constructor that takes a single parameter of type U (e.g. T::T(U)) then the compiler will allow the code to implicitly convert from type U to type T.
Consider the following code
struct U
{
//etc.
}
struct T
{
T(U obj);
//etc.
}
my_func(T param);
int main()
{
U u;
my_func(u);
}
When the compiler gets to "my_func(u)" and sees that my_func expects a parameter of type T, it looks at the constructors for T. When it sees the constructor "T(U obj);", it inserts it in-place in the function call like so:
//...
int main()
{
U u;
my_func(T(u));
}
It is important to note that this is done completely silently, which can lead to some difficult debugging if it is done on accident as opposed to intentionally.
So, I see that the practice for dynamic allocating of an array of pointers looks like this:
int **array = new int *[10];
And indeed, using syntax:
int *array[] = new int *[10];
results in error:
/Users/Malachi/Projects/playground/playground.gcc/src/pgccc-5/main.cpp:8: error: definition of variable with array type needs an explicit size or an initializer
const char* test_array[];
^
I'm always more comfortable using pure pointer syntax anyway. However, what bothers me is lines like this:
int main(int argc, char *argv[])
are valid. I'm accustom to empty array brackets [] more or less aliasing out to a pointer type. It seems to me char *argv[] is subject to almost exactly the same constraints as my int *array[], so why is the syntax permitted in one scenario but not the other?
EDIT: It appears the simpler case of int array[] = new int[10] exhibits the same behavior
This one:
int *array[] = new int *[10];
is not a valid syntax. The reason the left side has a type of an array of pointers to int, and the right side has a type of a pointer to a pointer to int. So the assignment is not legal due to the different types of left and right sides.
On the other hand, arrays decay into pointers. It means, that when you declare a function in the form of:
void foo(int* arr[])
the compiler sees it as:
void foo(int** arr)
The rule above applies only for functions, but not for assignments like in the first example.
It's because function parameter declaration is something different than variable declaration.
An array can decay into a pointer for the first dimension.
You can explicitly express that function expects an array rather than a pointer through the declaration using [] notation in e.g int main(int argc, char *argv[]). They type doesn't matter:
void f(int* i[]) {}
is legal as well. This says "I want an array of pointers to ints". This is more expressive than:
void f(int** i) {}
I'm accustom to empty array brackets [] more or less aliasing out to a pointer type.
That's valid only in the declaration of a function argument.
void foo(int a[]);
is the same as:
void foo(int* a);
However, when declaring or defining variables, they are not the same.
int a[] = {1, 2, 3}; // Valid. Array of 3 ints
is not the same as
int* a = {1, 2, 3}; // Invalid syntax.
Exception
You can use a string literal to intialize a char array or char const*.
char s1[] = "string 1";
char const* s2 = "string 2";
However, you can't use (not in C++ anyway):
char* s2 = "string 2";
Function overloading can happen between two member functions which have the same number of parameters, if one of them is declared as const.
But what if one function has a const argument, another has non-const argument of same type?
Will it work for references and pointers? If C++ provides it, why does it provide? Please share the reason with me if you know.
Below is the example that helps you in understanding the above scenario.
void fun(const int i)
{
cout << "fun(const int) called ";
}
void fun(int i)
{
cout << "fun(int ) called " ;
}
int main()
{
const int i = 10;
fun(i);
return 0;
}
Output: Compiler Error: redefinition of 'void fun(int)'
void fun(char *a)
{
cout<<"non-const fun() called";
}
void fun(const char *a)
{
cout<<"const fun() called";
}
int main()
{
const char *ptr = "GeeksforGeeks";
fun(ptr);
return 0;
}
Output: const fun() called
Why is the second one allowed in C++?
The first one's parameters are top-level const. This means that the function can't change the parameter's value, however, the caller doesn't care: The callee gets a copy of the argument, so if a parameter has top-level const, it's an implementation detail. Note that the following works:
void f(int); // forward declare
void g(){ f(42); }
void f(int const i){ /*...*/ } // define above declared function
For the second set of overloads, the const isn't top-level anymore. It describes whether or not the callee can change what the pointer points at. As a caller, you do care about that. It's not just an implementation detail anymore.
First, explain why the first code is not allowed while the second one is ok.
const int and int as parameter, you pass any related type, double, int or anything else can convert to int, both const int and int can accept the pass-in value, there's no difference practically. And if the complier allow to the define both, then which one to call? You don't know, neither the complier. So the first part of code is not allowed.
When it comes to second example, reference and pointer makes a difference. Because you can't pass a const int* to initialize int * and neither can use const int to initialize int&. So if you define two functions with same return type, one is "const version" pointer or reference parameter, and the other is not, that makes a difference. Another question comes up, what if I pass a int object(or called variable, same meaning) or int * pointer, then which one is matched (when parameters are pointer or reference)? The answer is the "non-const" one. if you want to match the "const version" with non-const object or non point to const pointer, you may need const_cast which I am trying to figure out.
So back to your question:
But what if one function has a const argument, another has non-const argument of same type? Will it work for references and pointers?
Yes, it to some extent only works for reference and pointers.
And
If C++ provides it, why does it provide?
Can't tell. I don't have much experience.
For further information, read the very related part sections of C++ Primer 5th.
Links of screenshots are listed as follows:
https://imgur.com/tnqrxVY
https://imgur.com/hF1MjUH
https://imgur.com/Fg2zeEw
By the way, though I am a newbie. But what is int const i from the first answer? And I don't understand what "it's an implementation detail" exactly mean. No offense, just can't understand that part of answer. :D
My question is: when i write a function prototype in C like this:
void foo(int *vector);
It's the same thing to do:
void foo(int vector[MAX_LENGTH]);
To the function, is passed always as a pointer? The code it's the same?
Thanks in advance.
This is subtle. Arrays in C are not pointers, but C does not allow arrays to be passed as function parameters. So when you have void foo(int vector[MAX_LENGTH]);, essentially all you're doing is telling other programmers (and your future self) that this function expects an array of MAX_LENGTH to be passed to it. The compiler won't help you. It will silently cast your array to a pointer.
This explains it pretty well.
Yes an array type is implicitly converted to a pointer type when passed to a function.
So
void foo(int *a) and void foo(int a[]) are identical.
You can easily check that using sizeof() operator inside the function definition
For example
void foo(int a[])
{
std::cout<<sizeof(a); //prints sizeof(int*)
}
int main()
{
int a[]={1,2,3,4};
foo(a);
}
EXTRA (Printing size of an array inside a function)
[C++ Only]
template<typename T,size_t n>
void size_of_array(T (&a)[n]) //Array passed by reference. Template argument deduction
{
std::cout<<sizeof(a); //prints sizeof(n*sizeof(int))
}
int main()
{
int a[]={1,2,3,4,5};
size_of_array(a);
}
This is one of the rough edges of the C language(s). Two declaration that look exactly the same (but for the names), one in the prototype and one as a stack variable, result in the declaration of two different types of variables.
void foo(int A[10]) {
int B[10];
}
Inside the scope of foo, A is pointer to int and B is array of ten elements of type int. As somebody else mentioned, even their sizes computed with sizeof are different.
C++ inherited the rule, so for your example code the prototypes of both functions should be the same.
C99 complicates this matter even further by introducing the new keyword static ;-)
void foo(int A[static 10]) {
int B[10];
}
this doesn't change the rules on how A and B are seen from the inside, but provides an information to the caller side of howmuch array elements are expected. For the moment gcc accepts this new syntax and simply ignores this information.
there's little else it could pass! the [] contraint lets the compiler do more checks though.