I know this question asked many times and I'm not asking how to do it because i did that at compile-time. My question is how it works because that is what i don't understand.
When passing a char[] to a function it looses its information and become char* and because of that we can not get size of character array at compile time using template, so i tried and passed the string itself and it worked:
template <int N> inline int arr_len(const char (&)[N]) { return N - 1; }
#define STR "this is an example!"
const char *str_ptr = STR;
int main()
{
int array_size = arr_len(STR);
}
I tested this code with VC++ 2008, Intel C++ 12 and GCC 4.7 and it works.
By passing the string itself the compiler sees it as const char[] - at least that what i think - and he able to get the string size, How is that possible?
String literal type is an array, not a pointer. So, when you pass it directly to a function that takes array by reference, it doesn't decay to a pointer.
That is because STR is a macro that is replaced before the compiler starts.
It replaces it with "XXXX" which is a string literal (not an array).
To get this to work do:
char const str_ptr[] = "XXX YYY";
// ^^^^ Compiler works out size
int main()
{
int array_size = arr_len(str_ptr);
// ^^^^^^^ pass in an object that has a type.
};
In C++ when a function's parameter type is tentatively identified as an array, the parameter type is 'adjusted' to be a pointer to the array's element type.
So when you write: void foo(char c[]) the compiler effectively rewrites it to be void foo(char *c). Then when you pass an array:
char x[10];
foo(x);
C++ finds void foo(char *c) and sees that it can convert your char [] argument into a char * and call the function, and this is exactly what it does.
However, this adjustment to the function's type only occurs when the written parameter type is an array. A reference to an array is not an array, so no equivalent adjustment is performed when you declare a function void bar(char (&c)[10]).
The second bit needed to answer your question is that the type of a string literal is an array of const char. When you write the string literal directly in the function call, with a function that takes const char (&)[N] C++ sees that it can pass a reference to the array instead of doing the "array -> pointer to first element" conversion. So that's what it does and the template type deduction finds the right number for the size of the string.
Related
This question already has answers here:
Array changed in a void function, is still changed outside! why? (scope)
(4 answers)
Passing an array as a parameter in C
(3 answers)
Passing Arrays to Function in C++
(5 answers)
Passing an array as an argument to a function in C
(11 answers)
Closed 11 months ago.
I don't get it why you can alter the values inside the array, without using a reference or a pointer (&, *), I'm a freshmen student, and I don't know the reason behind, I hope someone can provide a logical answer, please refer to the code below, Thank You in Advance.
#include <iostream>
using namespace std;
void a(int x[]){
for(int i = 0; i < 5; i++){
x[i] += 2;
}
}
int main(){
int x[5] = {1,2,3,4,5};
a(x);
for(auto b : x){
cout << b << " ";
}
return 0;
}
A function parameter is never an array in C++. When you declare a function parameter as an array, it is automatically adjusted to be a pointer to element of such array. These declarations are effectively identical:
void a(int x[]); // looks like an array of int of unknown bound
void a(int* x); // declaration above is adjusted to this
void a(int x[1234]); // the size is ignored completely
An array implicitly converts to a pointer to the first element of the array (such conversion is called decay). Hence, you can call the function that doesn't accept an array parameter by passing an array argument:
int* ptr1 = x; // implicit conversion
int* ptr2 = &x[0]; // same conversion explicitly
a(x); // same conversion happens here
These two rules (function parameter adjustment and array to pointer decay) make it so that what syntactically looks like passing arrays by value, is actually done using indirection. Within the function, you modify the elements by indirecting through the pointer parameter that points to the array that exists outside of the function.
Important note: The adjustment of array to pointer in function parameter does not apply in other contexts. Arrays and pointers are distinct types with different properties.
Another note: The adjustment does not apply to parts of compound types. For example, a function parameter of type "pointer to array" will not be adjusted to be "pointer to pointer" and "reference to array" will not be adjusted to be "reference to pointer".
The parameter having the array type in this function declaration
void a(int x[]){
is adjusted by the compiler to pointer type to array elements type. That is the above declaration is equivalent to
void a(int *x){
In this call of the function
a(x);
the array designator is implicitly converted to pointer to its first element. That is the call is equivalent to
a( &x[0]);
So within the function you have a pointer to the first element of the array declared in main.
Using the pointer arithmetic you can access elements of the array. That is the elements of the array are passed to the function by reference in the C meaning indirectly through a pointer to them.
Within the function the variable x has the type int *. And this expression statement
x[i] += 2;
is equivalent to
*( x + i ) += 2;
Beacuse
void a(int x[]){
is the same as
void a(int *x){
and so you are using a pointer
Why?
Because an array like
int x[10];
'decays' to a pointer when passed to a function (and in other places). This can be very confusing but at the same time is very flexible
It mens that I can have a function like strlen that can accpet a 'real' array, or a pointer. These 'strings'
char *s1 = malloc(10);
strcpy(s1, "hello");
char s2[] = "hello";
char *s3 = "hello";
store their data in different ways but all can be handled by
size_t strlen(const char *s);
note that this is exactly the same as
size_t strlen(const char s[]);
they are 2 different ways of writing the same thing. Personal preference is for the second type if its really is an 'array' vs a pointer to maybe an array.
One issue with this 'decay' is that inside strlen (or any pointer/array accepting function) it is impossible to 'know' the length just from the parameter. The compiler knows that the size of s2 is 6 but this information is not carried forward to the function.
Regularly SO sees this
void my_func(int *arr){
int len = sizeof(arr)/sizeof(arr[0]);
....
}
int a[10];
my_func(a);
This will give len = 1 or 2 (depending on 32 or 64 bit machine), never 10
The flexibility costs a litle power
Assume a function with one parameter of const char* type. When I use a string when calling that function, nothing goes wrong! I would expect that you could only input characters included in the ASCII, for example c or dec 68, but it seem otherwise. Take a look at the code below...
void InitFunction(const char* initString)
{
std::cout << initString;
}
int main()
{
InitFunction("Hello!");
}
When you run the code, no problem, warnings, or errors appear. I did some more testing on this as you can see from the following...
void InitFunction(char initString)
{
std::cout << initString;
}
int main()
{
InitFunction("Hello!"); // compiler error on this line
}
This compiles with an error since Hello! cannot be converted into char. I tried another test, but instead used const char as the function parameter type. The code still compiles with an error. But when I add * to the function parameter type, then everything compiles fine! It appears to me that the const is also necessary.
To my understanding of pointers, the function is asking for a pointer, and the pointer is identified as being a character. But this raises three problems for me.
First, Hello! is not in the form of a pointer. I would expect at least to have to use the reference operator (&).
Second, Hello! is not a char type.
And third, why do we need to include the const?
Am I missing something here? Do pointers and characters work in ways that I don't know?
"Hello!" is a const char array of characters. Arrays are treated like a pointer, they decay to a pointer when used in a function call as an argument. The C++ standard specifies this in order to be compatible with the way that the C language standard specifies arrays are to be treated.
By the way this array decay to a pointer happens with other cases where an array is being used where a pointer or the address operator could be used such as an assignment statement. See also Arrays are Pointers?
So "Hello!" will be put into the argument list of InitFunction() as a pointer which points to where the compiler has stored the array of characters with the terminating zero character added. When the compiler generates the code for the function call, the pointer to the array of characters used as an argument to the function is const char *, a pointer to a char which is const and should not be changed.
When you have the function prototype as InitFunction(const char *), the compiler is fine with a function call such as InitFunction("Hello!");. What you are doing is providing a const char * that points to "Hello!".
However if you remove the const then since "Hello!" is a const the compiler complains. The compiler complains because a const variable is being used in a function call whose argument list is non-const indicating that the function may change what the pointer is pointing to. Since "Hello!" is not supposed to be changed, since it is const, the compiler issues an error.
If you remove the asterisk, change const char * to const char, then since "Hello!" is a char array which the compiler then decays into a pointer to the first element of the array, the compiler complains as you are trying to use a pointer for an argument that is not a pointer. In this alternative the problem is the actual data type, char versus char * is the problem.
The following lines of code would also be acceptable:
const char *p = "Hello!"; // create a pointer to an series of characters
char x1[] = "Hello!"; // create an array of char and initialize it.
char *p2 = x1; // create a pointer to char array and initialize it.
char *p3 = x1 + 2; // create a pointer to char array and initialize it with address of x1[2].
InitFunction (p); // p is a const char *
InitFunction (x1); // x1 decays to a char *
InitFunction (p2); // p2 is a char *
InitFunction (p3); // p3 is a char *
InitFunction (x1 + 3); // called with address of x1[3].
Note also C++ has an actual character string type, string, that results in a dynamic character text string whose underlying physical memory layout normally includes a pointer to an array of characters. The C style array of characters that is labeled a string is not the same thing as the C++ string type.
the function is asking for a pointer
Correct.
and the pointer is identified as being a character
No, a pointer is a not a character. A pointer is a pointer. This pointer points to one or more characters, i.e. an array of characters. And that's exactly what your string literal is.
First, Hello! is not in the form of a pointer.
Yes, it is. The string literal "Hello!" has type const char[7] and this decays to a pointer.
I would expect at lest to have to use the reference operator (&).
That's the address-of operator. You don't always need it to get a pointer. Example: this.
Second, Hello! is not a char type.
No, but each constituent character is.
And third, why do we need to include the const?
Because the string literal "Hello!" has type const char[7]. Dropping the const would violate const-correctness and is therefore not permitted.
This throws an error since Hello! cannot be converted into char.
That's right. A char is one byte. The string "Hello!" is not one byte. It is a string.
A C-string is typically/conventionally/usually provided in const char* form.
You should read the chapter in your book about this subject as it's a fundamental of the language. It has nothing to do with ASCII (text encodings are irrelevant to storing a sequence of bytes).
Trying to figure out string literal types in C/CPP
printf("%s\n", typeid("abc").name());
printf("%s\n", typeid(const char[]).name());
print
A4_c
A_c
Not familiar with C, is different length/capacity of array means different type in C? if yes, why could we pass char[n] as char[] when passing function parameters?
is different length/capacity of array means different type in C?
Yes.
if yes, why could we pass char[n] as char[] when passing function parameters?
It is actually not possible to accept an array value as a function argument. When a function argument is declared to be an array type, that declaration is adjusted by the language to mean a pointer to an element of that array type. I.e. array of char becomes pointer to char. Same applies to return types. Example:
void function(char argument[N]); // this
void function(char *argument); // actually means this
Similarly, when an array name is used as a value argument, that array name implicitly converts to a pointer to the first element of the array. This implicit conversion is called decaying. Example:
void function(char *argument);
char array[N];
function(array); // array decays to pointer to first element
Note that this adjustment is only applied to "toplevel" arrays. Pointers to arrays and references to arrays are not adjusted to be pointers or references to pointers to element of that array even in function argument declarations.
What's the difference between char[] and char[n] in C/CPP?
char[n] is an array type. It is an array of n elements. It is a complete type. It is possible to create arrays of this type.
char[] is an array of unknown bound. It is an incomplete type. It is not possible to create an array of this type. This type can only be used in contexts where it is adjusted to another type. In a function declaration, it is adjusted to a pointer to element.
In a declaration of a non-argument array, it is adjusted to the the actual array type of known bound that is deduced from an initialiser:
char arr[] = {'1', '\0'}; // type of arr is adjusted to char[2]
char arr[]; // ill-formed declaration
Since the typeid().name() output varies from compiler to compiler, piping output to c++filt is a better way to see the type's name.
This code:
#include <iostream>
int main(void)
{
std::cout << typeid("abc").name() << std::endl;
std::cout << typeid(const char[]).name() << std::endl;
return (0);
}
compiled and ran as ./a.out outputs:
A4_c
A_c
but when ran as ./a.out | c++filt
outputs the following:
char [4]
char []
For char[] vs char[4] its minimal, but for big nested types and auto in later standards of C++ it becomes a real handy tool for seeing whats happening under the hood.
(Sorry for not being a direct answer but oh how I cringe to see unformatted typeid names in any context, especially since they are compiler specific)
What is the meaning of the syntax const char (&x)[] in C++, is it something like pass pointer by reference to a function call?
Is it the same like const char x[], which defines x as const char*?
And if both are one and the same where should I use const char (&x)[] instead of const char x[]?
const char (&)[] is a reference to a const char array. The expression you showed is declaring x as a one of those.
Is it the same like const char x[]
it's a reference to one of those
... which defines x as const char*
Um, no, let's take a step back.
const char array[5]
declares array as a an array of 5 const chars. It doesn't declare a pointer. However, arrays easily decay to pointers in C++, so for example
void foo(const char *);
// ...
foo(array);
is legal. In fact, arrays decay to pointers so easily, it takes extra care to pass them somewhere without decay:
template <size_t N>
void bar(const char (&x)[N]);
bar(array);
will actually get a reference to the array, and as a bonus, allow bar to deduce the array size.
Note that the only useful difference between a pointer and an array is the number of elements - when I quoted you saying const char x[], I assume there will really be a number between the square brackets. If you omit that, it doesn't have any benefit over a pointer, unless you initialize it:
const char x[] = { 'h', 'e', 'l', 'l', 'o' };
will still allow that call to bar to deduce N=5, even though you never wrote a literal 5 in your code.
It can also be used when you want your function to accept a fixed-length array:
void fun(const char (&x)[50]);
It's also usable with multi-dimensional arrays (but prefer std::vector or std::array when possible).
What is the meaning of the syntax const char (&x)[] in C++
This syntax mean that you want to use a reference to an array of const char.
Is it the same like const char x[], which defines x as const char*?
No, it isn't the same, the main difference with const char* is that the size of the array became part of the type, so you can't pass an array with a different number of elements.
The main use of reference to array is in template where the number of elements is than deduced
This question already has answers here:
Functions with const arguments and Overloading
(3 answers)
Closed 9 years ago.
I am confused why the following code is not producing any error ,because the arguments passed to display are of same type i.e char.Does const really makes difference?
#include<iostream>
using namespace std;
void display(char *p)
{
cout<<p;
}
void display(const char *p)
{
cout<<p;
}
int main()
{
display("Hello");
display("World");
}
EDIT
As per answers,the first display is never called,which is correct and so is the output.
But suppose I do it like :
int main()
{
char *p="Hello";
display(p);//now first display is called.
display("World");
}
Compiler gives a warning: deprecated conversion from string constant to ‘char*’ [-Wwrite-strings] but then it calls first display.Does it mean that string is now no more taken as constant?
const char* and char * are actually not the same. The later allow for modifying the pointed char, while the first one will prevent that.
Also note that if those were class methods, void display() and void display() const would also be valid overloads. The later would imply that the method must not change the object's state.
Consider this code:
void display(char *s)
{
std::cout << "Display" << std::endl;
}
void display(const char *s)
{
std::cout << "Display with const" << std::endl;
}
int main()
{
char *str = strdup("boap");
const char *str2 = "toto";
/* It is a string literral "bound" as a char *.
Compiler will issue warning, but it still compiles.
Avoid to do that, it's just an exemple */
char *not_safe = "not_safe";
display("llama");
display(str2);
display(str);
display(not_safe);
}
This will print Display with const twice, and then twice Display. See there.
Now, let's see why:
"llama" is a string literal, and then is resolved as a const char *.
str2 is a pointer to a string literal. Since its type is const char*, this also revolves to the const overload.
not_safe is also a pointer to a string literal. However, its type is char *: this is not correct. The memory it points to is read-only, and trying to modifies it will result in a crash. However, the type of the variable is still char *, so this resolve to the non-const overload.
str is a char * pointer, and the string it points to is not read-only. Modifying its content is valid, and since its type is char *, it will resolve to the non-const overload.
The issue is that string literals such as "Hello" and "World" have type const char[6]. This can decay to const char*, but not to char*. So the overload taking const char*,
void display(const char *p);
is the better match. As #JamesKanze points out, it would be possible for a function taking char* to accept a string literal, but attempting to modify the data pointed at would result in undefined behaviour. For this reason, it is unsafe to pass string literals to such functions. With suitable warning settings, GCC produces the following:
warning: deprecated conversion from string constant to ‘char*’
In any case, in the presence of two overloads like the ones you have shown, the one taking const char* wins.
The arguments passed to the two functions are actually not the same.
The first takes a char*: A pointer to a char.
The second takes a const char*: A pointer to a const char.
So you see, the difference here is actually in whether the pointer points to an object which can be changed or not. This is definitely a property on which you want to be able to overload a function.
Whether you can modify an object or not definitely is a useful piece of information depending on which you may want to invoke different behavior! Consider this:
void foo(int * p) { ++(*p); }
void foo(int const * p) { std::cout << *p << '\n'; }
The sizes of the strings "Hello" and "World" are known at compile time and cannot be modified. They are constant, compile-time character arrays.
In C and C++, an array e.g. char a[6] can be referred to using a pointer, i.e. a is actually a char *. Since the arrays for "Hello" and "World" must not be modified at runtime, their type is essentially const char *.
The compiler recognizes this and performs the overload resolution for display correctly, since only one of the functions (display(const char* p)) takes a const char* as an argument. This is why there is no ambiguity in the two functions, and you don't get the error that you expected to get.
"because the arguments passed to display are of same type i.e char."
No here the argument are "const char*". the data type is the but the const qualifier indicate that the literal string you hard coded, is not something that can be change.
"Does const really makes difference?"
Yes the const qualifier make a difference.
in Display(char*) you can update the content of the null-terminate string you passed but not in Display(const char*). That fact allow more optimization by the compiler.
But read that http://www.possibility.com/Cpp/const.html it's a good source to start using const efficiently.