I'm wondering why this code works in C and not in C++
void* dum;
dum = "dum";
I have the C++ error
In function 'int main()':
8:10: error: invalid conversion from 'const void*' to 'void*' [-fpermissive]
Any C++ equivalent?
I'm wondering why this code works in C and not in C++
It doesn't work in C++ because string literal is an array of const char.
Any C++ equivalent?
const char* dum = "dum";
void* dum;
dum = (void *)"dum";
const void* dum = "dum";
const char* dum = "dum";
const char* dum;
dum = "dum";
const void* dum;
dum = "dum";
You would have to cast it back to get the result you want.
String literals have type const char* so your pointer would need to be constalso. You cannot assign something that is const to a pointer to non const.
const void* dum = "dum";
cout << static_cast<const char*>(dum);
Try this coutwithout the cast to see what you get. It would be interpreted as a void pointer...
Related
As per the output of this code snippet, the type of &"hello" is const char(*)[6], so char* ptr = &"hello"; is inlegal for char* and const char(*)[6] are different types.
And since char* ptr1 = "hello"; compiles with C++11 and latter, the type of "hello" is char*?
If the type of "hello" is char*, then &"hello" shouldn't be a pointer which points to char*(i.e. char**)?
I used to write char* ptr1 = "hello"; many years, and when I know that "hello" is a prvalue. A question raises that I could acquire the address of the string literal.After I found the type of &"hello" is const char(*)[6], I am totally confued now.
Could anybody shed some light on this matter?
Here is the aforementioned code snippet:
#include<memory>
#include<thread>
#include<iostream>
#include<typeinfo>
int main()
{
char* ptr = &"hello";
char* ptr1 = "hello";
}
Here is what the compiler complains:
<source>: In function 'int main()':
<source>:8:17: error: cannot convert 'const char (*)[6]' to 'char*' in initialization
8 | char* ptr = &"hello";
| ^~~~~~~~
| |
| const char (*)[6]
<source>:9:18: warning: ISO C++ forbids converting a string constant to 'char*' [-Wwrite-strings]
9 | char* ptr1 = "hello";
| ^~~~~~~
UPDATE:
Thanks to ShadowRanger's clarification. I realise that type of string literal is const char[6].
"Hello" is a string literal of type const char [6] which decays to const char* due to type decay.
Now let's see what is happening for each of the statements in you program.
Case 1
Here we consider the statement:
char* ptr = &"hello";
As i said, "hello" is of type const char [6]. So, applying the address of operator & on it gives us a const char (*)[6] which is read as a pointer to an array of size 6 with elements of type const char.
This means that there is a mismatch in the type on the right hand side(which is const char (*)[6]) and the left hand side(which is char*). And since there is no implicit conversion from a const char (*)[6] to a char* the compiler gives the mentioned error saying:
cannot convert 'const char (*)[6]' to 'char*'
Case 2
Here we consider the statement:
char* ptr1 = "hello"; //invalid C++
Since "Hello" is of type const char[6] meaning that the char elements inside the array are immutable(or non-changable). As i said, the type const char[6] decays to const char*. Thus on the right hand side we have a const char*. So if we were allowed to write char* ptr1 = "Hello"; then that would mean that we're allowed to change the elements of the array since there is no low-level const on ptr1. Thus, allowing char* ptr1 = "Hello"; would allow changing const marked data, which should not happen(since the data was not supposed to change as it was marked const). This is why the mentioned warning said:
ISO C++ forbids converting a string constant to 'char*'
So to prevent this from happening we have to add a low-level const as shown below:
vvvvv---------------------------> note the low-level const
const char* ptr1 = "Hello"; //valid c++
so that the pointer ptr1 is not allowed to change the const marked data.
By adding the low-level const highlighted above, it is meant that we're not allowed to change the underlying characters of the array.
I am currently working with a C-API (OpenSSL) and there are a lot functions declared like the following:
// From bytes to TYPE
TYPE *d2i_TYPE(TYPE **a, unsigned char **ppin, long length);
// From TYPE to bytes
int i2d_TYPE(TYPE *a, unsigned char **ppout);
But sometimes, functions expect a pointer to an array of pointers to some const type:
// From bytes to TYPE
TYPE *d2i_TYPE(TYPE **a, const unsigned char **ppin, long length);
Notice the const unsigned char ** vs unsigned char ** and ignore the functions' first argument.
Now consider the following example, where some input data is given as non-const pointers. What is the correct way of casting the array of pointers to an array of pointers const? And also, since the function expects a pointer, the parameter must be an lvalue. I am guessing this is the reason there is no implicit cast..
int length = 42;
unsigned char * data = new unsigned char[length];
// Error C2664: cannot convert argument 2 from 'unsigned char **' to 'const unsigned char **'
TYPE * my_typeE = d2i_TYPE(nullptr, &data, length);
// My solution:
auto x = reinterpret_cast<unsigned char const *>(data);
auto y = const_cast<unsigned char const *>(data);
TYPE * my_typeX = d2i_TYPE(nullptr, &x, length);
TYPE * my_typeY = d2i_TYPE(nullptr, &y, length);
Are there other solutions that are possibly easier to read?
No need of cast:
const unsigned char* z = data;
[[maybe_unused]]TYPE * my_typeZ = d2i_TYPE(nullptr, &z, length);
Demo
What C++ casts is the C cast using in order to convert from a const void* to a unsigned char*?
auto ucharptr = (unsigned char*)const_void_ptr;
Try the C++ casting operator and you need two of them: one to remove the const and another one to cast to your pointer type:
auto ucharptr = reinterpret_cast<unsigned char*>(const_cast<void*>(const_void_ptr));
Try this:
const void* ptr = "test example";
auto ucharptr = static_cast<const unsigned char*>(ptr);
//to remove the const ness
unsigned char* test = const_cast<unsigned char*>(ucharptr);
The easiest way to do that would probably be the following:
unsigned char* ucharptr = reinterpret_cast<unsigned char*>(const_cast<void*>(const_void_ptr));
I receive this error:
array.cpp: In function ‘void strcat2(char*, const char*)’:
array.cpp:74: error: invalid conversion from ‘char’ to ‘char*’
array.cpp:74: error: initializing argument 1 of ‘void strcpy2(char*, const char*)’
array.cpp:74: error: invalid conversion from ‘char’ to ‘const char*’
array.cpp:74: error: initializing argument 2 of ‘void strcpy2(char*, const char*)’
When trying to run this code:
//Concatenates two arrays of characters using pointers
void strcat2(char* t, const char* s)
{
unsigned int i;
for (i = 0; *t; i++);
strcpy2(*(t + i), *s);
}
Here is the strcpy2 function it calls:
//Copies the information from one array of characters to another using pointers
void strcpy2(char* t, const char* s)
{
for ( ; *t++ = *s++;);
}
It says invalid conversion from char to char*, but where am I trying to convert from char to char*? It seems to me that everything in the code is char*. What am I missing?
I've looked over this code many times and can't seem to find what is wrong. I'm relatively new to pointers, go easy! Thank you very much for any help!
*(t + i)
t is of type char *.
so t + i means char * + i which means "add the value of i to the pointer to make a new pointer". *(t + i) then dereferences that new pointer, the type of that dereferenced expression will be char. So yes, the compiler is correct. You're trying to pass a char into a function that expects a pointer-to-char.
You simply want
strcpy2(t + i, s);
note: You were also dereferencing s, which would cause the same compile error.
The expression *(t + i) in strcpy2(*(t + i), *s); is a char type because the * dereferences the pointer.
Change these statements
for (i = 0; *t; i++);
strcpy2(*(t + i), *s);
to
for (i = 0; *(t + i ); i++);
strcpy2( t + i, s);
Also it would be better to declare the functions as having return type char *. For example
char * strcat2(char* t, const char* s);
The function char* strrchr(const char *str, int ch) returns a pointer (char*) within str (const char *) where the last occurrence of ch is located.
So we can write the following code without any cast:
#include <string.h>
int main()
{
const char CONSTSTR[] = "foo/bar/foobar.txt";
char *ptr = strrchr (CONSTSTR, '/');
*ptr++ = 'B';
*ptr++ = 'A';
*ptr++ = 'D';
}
What is the advantage to return char* instead of const char* ?
EDIT:
As a Shafik Yaghmour pointed out, there are very good answers to How does strchr implementation work?
As my code is in C++, I will use <cstring> instead of <string.h>. Thanks for your answers ;-)
However, the Mike Seymour's answer fits best the question. I have even added a more detailed answer below to clearly say as strrchr() is a C function (overload not permitted), the declaration fits both const and non-const strings. Because strrchr() can be called with a non-const string, the returned string should also be non-const.
You're looking at the legacy function from the C standard library (<string.h>). The C++ library (<cstring>) introduces appropriate const and non-const overloads, so you should use that wherever possible.
In C, the function must either be like this, or force the user to use dodgy casts in many situations:
If it took a non-const pointer, you couldn't search a const string;
If it returned a const pointer, you couldn't use it to modify a non-const string.
In C++, you should include <cstring> rather than the deprecated C-only header. That will give you two const-correct overloads, which couldn't be done in C:
const char* strchr(const char* s, int c);
char* strchr( char* s, int c);
const char *str means strrchr guarantees not to modify str.
Returning const char * means strrchr forbids you to modify the returned value.
strrchr() from <string.h> is a C function. As C does not permit function overloading, strrchr() has been designed to fit both const and non-const strings.
char* strrchr( const char *str, int ch );
strrchr() may be called with a non-const string, and therefore the returned string should also be non-const as explained in the following examples.
const context without compilation error:
#include <string.h>
int main()
{
const char CONSTSTR[] = "foo/bar/foobar.txt";
const char *basename = strrchr (CONSTSTR, '/');
// basename points to "foobar.txt"
}
non-const context without compilation error:
#include <string.h>
int main()
{
char nonconst[] = "foo/bar/foobar.txt";
char *basename = strrchr (nonconst, '/');
basename[0] = 'G';
basename[3] = 'D';
// basename points to "GooDar.txt"
}
Bad usage also without compilation error:
#include <string.h>
int main()
{
const char CONSTSTR[] = "foo/bar/foobar.txt";
char *nonconst = strrchr (CONSTSTR, '/');
*nonconst++ = 'B';
*nonconst++ = 'A'; // drawback of the unique declaration:
*nonconst++ = 'D'; // no compilation error
}
In C++, there are two overloaded functions:
const char* strrchr( const char* str, int ch ); //1st
char* strrchr( char* str, int ch ); //2nd
const context uses the 1st one:
#include <cstring>
int main()
{
const char CONSTSTR[] = "foo/bar/foobar.txt";
const char *basename = std::strrchr (CONSTSTR, '/');
// basename points to "foobar.txt"
}
non-const context uses the 2nd one:
#include <cstring>
int main()
{
char nonconst[] = "foo/bar/foobar.txt";
char *basename = std::strrchr (nonconst, '/');
basename[0] = 'G';
basename[3] = 'D';
// basename points to "GooDar.txt"
}
Bad usage should produce compilation error:
#include <cstring>
int main()
{
const char CONSTSTR[] = "foo/bar/foobar.txt";
char *nonconst = std::strrchr (CONSTSTR, '/');
// Visual C++ v10 (2010)
// error C2440: 'initializing' : cannot convert from 'const char *' to 'char *'
*nonconst++ = 'B';
*nonconst++ = 'A';
*nonconst++ = 'D';
}
But this last example does not produce any compilation error using g++ -Wall file.cpp. Tested using GCC versions 4.1.2 (RedHat) and 4.7.2 (MinGW).
Why would you forbid the code from modifying a returned variable? Mind that const char * is not char * const, you would be allowed to modify the character in any case, but you won't have control on the returned value itself, which doesn't make much sense since you could want to change it and edit the underlying string in a different position for your purpose.