I often use the execv() function in C++, but if some of the arguments are in C++ strings, it annoys me that I cannot do this:
const char *args[4];
args[0] = "/usr/bin/whatever";
args[1] = filename.c_str();
args[2] = someparameter.c_str();
args[3] = 0;
execv(args[0], args);
This doesn't compile because execv() takes char *const argv[] which is not compatible with const char *, so I have to copy my std::strings to character arrays using strdup(), which is a pain.
Does anyone know the reason for this?
The Open Group Base Specifications explains why this is: for compatibility with existing C code. Neither the pointers nor the string contents themselves are intended to be changed, though. Thus, in this case, you can get away with const_cast-ing the result of c_str().
Quote:
The statement about argv[] and envp[] being constants is included to make explicit to future writers of language bindings that these objects are completely constant. Due to a limitation of the ISO C standard, it is not possible to state that idea in standard C. Specifying two levels of const- qualification for the argv[] and envp[] parameters for the exec functions may seem to be the natural choice, given that these functions do not modify either the array of pointers or the characters to which the function points, but this would disallow existing correct code. Instead, only the array of pointers is noted as constant.
The table and text after that is even more insightful. However, Stack Overflow doesn't allow tables to be inserted, so the quote above should be enough context for you to search for the right place in the linked document.
const is a C++ thing - execv has taken char * arguments since before C++ existed.
You can use const_cast instead of copying, because execv doesn't actually modify its arguments. You might consider writing a wrapper to save yourself the typing.
Actually, a bigger problem with your code is that you declared an array of characters instead of an array of strings.
Try:
const char* args[4];
This is just a situation where C / C++ style const doesn't work very well. In reality, the kernel is not going to modify the arguments passed to exec(). It's just going to copy them when it creates a new process. But the type system is not expressive enough to really deal with this well.
A lot of people on this page are proposing making exec take "char**" or "const char * const[]". But neither of those actually works for your original example. "char**" means that everything is mutable (certainly not not true for the string constant "/usr/bin/whatever"). "const char *const[]" means that nothing is mutable. But then you cannot assign any values to the elements of the array, since the array itself is then const.
The best you could do is have a compile-time C constant like this:
const char * const args[] = {
"/usr/bin/whatever",
filename.c_str(),
someparameter.c_str(),
0};
This will actually work with the proposed type signature of "const char *const[]". But what if you need a variable number of arguments? Then you can't have a compile-time constant, but you need a mutable array. So you're back to fudging things. That is the real reason why the type signature of exec takes "const char **" for arguments.
The issues are the same in C++, by the way. You can't pass a std::vector < std::string > to a function that needs a std::vector < const std::string >. You have to typecast or copy the entire std::vector.
I have usually hacked this with:
#define execve xexecve
#include <...>
#include <...>
#include <...>
#undef execve
// in case of c++
extern "C" {
int execve(const char * filename, char ** argvs, char * const * envp);
}
;/
Related
I have two third party modules and I have to combine them.
First I get data from a class. I will submit this data to a function.
bool loadLibrary(const char *strPlugName){
HPLUGIN temp = _BASS_PluginLoad(strPlugName,0);
return false;
}
The const char * strPlugName is a value that I got from another library. I cannot change this value type myself.
Inside the function I try to call a BASS Library function.
HPLUGIN temp = _BASS_PluginLoad(strPlugName,0);
Definition: typedef HPLUGIN (*BASS_PluginLoad_Type)(char *file,DWORD flags);
Here Xcode tell me:
Cannot initialize a parameter of type 'char *' with an rvalue of type 'const char *'
My question is how I can convert or cast this const char * to char *?
If and only if the function called via _BASS_PluginLoad doesn't alter the memory pointed at by file, you can use a const_cast:
HPLUGIN temp = _BASS_PluginLoad(const_cast<char*>(strPlugName),0);
Some old c API's are not const correct on account of the const keyword being a fairly late addition to the C language. But they still don't mutate their arguments, so a const_cast is the easiest way to make use of them in const correct C++ wrappers. It's a perfectly legitimate reason (maybe even the reason) for that cast.
The easy and safe way is to copy the argument into a local buffer, and then pass a pointer to that. As you are using C++, you can automate the memory management.
bool loadLibrary(const char *strPlugName){
std::string local(strPlugName);
local.push_back('\0'); // Ensure null terminated, if not using C++11 or greater
HPLUGIN temp = _BASS_PluginLoad(&local[0],0);
return false;
}
If using C++17, you can just call local.data() instead of &local[0].
Language lawyer caveat:
Strictly speaking, &local[0] was not defined to work in C++98 - in practice it always did (and later versions of the standard defined it to work).
I do have a "C" function
find_register(char *name)
which is called from a "C++" routine. The first tip to call it was
find_register("%ebx")
It results in a warning
deprecated conversion from string constant to 'char*'
OK, I corrected it to
find_register(string("%ebx").c_str())
which results in the error message
"invalid conversion from 'const char*' to 'char*'
Finally,
find_register((char*)string("%ebx").c_str())
is accepted without error message and warnings.
My questions:
1./ The probable reason of the error message is that the possibility of changing a 'const char*' is left open. It is OK, but in the first case the less sophisticated version allows the same, and converting a string constant to 'char *' is a legal, but deprecated conversion. Is there any deeper reason behind?
2./ Is there any simple method to do what I want? (the perfect (for the compiler) code is hardly readable for the programmer)
The reasoning lies in the original roots of C++ language, back in the time when certain amount of backward compatibility with C was considered important.
In C language string literals, despite being non-modifiable, have type char [N]. For this reason they are implicitly convertible to type char * in C. In C++ string literals have type const char[N]. Formally, const char[N] is not implicitly convertible to char *. But for aforementioned C compatibility reasons the original C++ specification (C++98) allowed implicit conversion of string literals to char *. This exception was made for immediate string literals only, as a form of special treatment provided to string literals.
But eventually the matter of C compatibility became unimportant and this special treatment was deprecated in C++03. So, this is what the compiler is telling you. In your find_register("%ebx") call it agrees to convert the immediate string literal to char *, but warns you that this conversion is deprecated. In C++11 this implicit conversion is outlawed entirely.
In all other contexts (not an immediate string literal), implicit conversion of const char [N] or const char * to char * is prohibited and has always been prohibited. This is why your
find_register(string("%ebx").c_str())`
variant has no chance of compiling.
As for working around this restriction... If you are sure that find_register(char *name) does not attempt to change the data pointed by name (and you cannot just change the find_register's parameter type to const char *name), then
find_register(const_cast<char *>("%ebx"))
is a fairly acceptable solution (with an appropriate accompanying comment). Of course, if find_register is a modifying function, then you'll have to do something like
char reg[] = "%ebx";
find_register(reg);
The prototype for the function find_register(char*) indicates that it may change the parameter since it is just a pointer that is passed. You do not mention if you have the source code of that function so I assume you don't otherwise it would be better to change that function to accept a char const * provided it doesn't change the contents.
Otherwise just pass a modifiable array:
char arg[] = "%ebx";
find_register(arg);
to write:
find_register((char*)string("%ebx").c_str())
is dangerous, what if the function does indeed modify the string e.g. strtok? But if you insist at least try to use the C++ style of casting instead of C casting (in this case find_register(const_cast<char*>("%ebx"))).
Side note: personally I find it easier to read to avoid using the C-way of putting const when possible. So instead of writing const char * write char const * and char * const when the pointer is constant i.e. const always goes to the left - it is more consistent.
If you are passing string literals to find_register(char *name), then the function prototype should be find_register(const char *name), as string literals are not modifiable.
With this conversion, C++ will happily let you shoot yourself in the foot and try to modify name, which gives me a segmentation fault, as in the following code:
void modifyIllegally(char* name) {
name[0]='d';
}
int main() {
modifyIllegally("abc");
}
Edit: If you don't control the API of the C program and are sure it won't change the char*, you can disable this warning with compiler flags. For gcc the flag is -Wno-write-strings.
The standard function strstr is used to find the location of a sub-string in a string. Both the arguments of the function are of const char * type, but the return type is char *.
I would like to know how a standard function is implemented violating the const-correctness.
All the const char * is telling you is that strstr is not going to modify the string you pass into it.
Whether you modify the returned string or not is up to you as it is your string!
In C++ this has been changed by overloading the method and having two versions, the const input version has a const output.
In C it doesn't have quite that level of safety built in for you and assumes you know yourself whether you should be modifying the returned string.
C allows pointing to memory with const or non-const pointers, regardless if the object was defined with the const qualifier or not.
6.5 Expressions
An object shall have its stored value accessed only by an lvalue expression that has one of
the following types:
— a qualified version of a type compatible with the effective type of the object,
The prototype of strstr in C is:
char *strstr(const char *s1, const char *s2);
The returned pointer, if valid, points to string s1. This can be achieved with a cast:
const char safe = 's' ;
char* careful = ( char* )&safe ;
The problem is modifying that memory.
6.7.3 Type qualifiers
If an attempt is made to modify an object defined with a const-qualified type through use
of an lvalue with non-const-qualified type, the behavior is undefined.
Since you created the string, you should know whether you can modify it or not, therefore you can accept the return value with a pointer to const, to avoid any problems:
const char* find = strstr( ... ) ;
According to ISO C++ 21.8(7) strstr returns a const char* or a char* depending on if it gets a const char* or a char*.
const char* strstr(const char* s1, const char* s2);
char* strstr( char* s1, const char* s2);
It's done by specifying the signature, and leaving the implementation up to the compiler builders.
Note that returning a char* which points to a const char[] string is just dangerous, but not yet a violation of any rule. However, any attempt to write to that memory is still Undefined Behavior.
The strstr function dates back to an era before there was such a thing as a const pointer. In cases were it would be legal for code to write to memory identified by the first pointer passed to strstr, it would be legal for code to write to memory identified by the returned pointer, and in cases where the returned value would only be used in ways that were legal with a pointer to read-only memory (e.g. a string literal), one could legally pass such a pointer to strstr.
If a functionality similar to strstr were being defined today, it might be implemented using two methods--one of which could accept any pointer and return a pointer which could not be written by the recipient, and one of which would only accept writable pointers but would return a pointer that the recipient could use as it saw fit. Because some code which used strstr would need to pass read-only pointers, however, and because some code which used strstr needed to be able to write to the pointers that it would yield when given writable pointers, it was necessary to have one set of pointer qualifications work both ways. The consequence is a set of pointer qualifications which are not really "safe" [since it may return a writable pointer to a read-only area of memory] and which will let code compile in some cases where it really "shouldn't", but which will allow code written before the days of const pointers to continue working as intended.
C++ provides two versions one that takes const arguments and ones that takes non-const.
const char* strstr( const char* str, const char* target );
char* strstr( char* str, const char* target );
Since in C we can not overload, we are left with two unpleasant choices:
Either we take the arguments as non-const but if our sources are indeed const then we need to perform an unpleasant cast to non-const.
The second option is the one we have which is that we take the arguments as const but we return a non-const. We could return a const char* but then we could never modify the result.
The return value is not the variable that you have passed to the function as parameter. This function returns a pointer to the first occurrence in haystack of any of the entire sequence of characters specified in needle, or a null pointer if the sequence is not present in haystack.
This is not my program so don't start berating me :-). Some random program I got. A globally declared buffer is being returned by MyFunc(). I use VS2008 and it does not complain
static char buffer[1024];
std::string MyFunc() {
....
....
return buffer;
}
However when I add this line of code
char * ret;
ret = MyFunc()
It complains: "error: no suitable conversion function from "std::string" to "char *" exists"
My question is why is the compiler complaining now? Why this inconstancy in syntax checking? Again I dont have the freedom to change MyFunc(). In my program if I can make
std::string ret;
ret = MyFunc();
and get rid of the syntax error but would really like to understand this strange behavior.
string() has a constructor that accepts a char*, so you get an automatic conversion. There is no automatic conversion from a string to a char*. You have to call string::c_str() to get the char*.
Edit
Although you asked only for an explanation of the behavior, others in this forum seem to think I have short-changed you by not mentioning that string::c_str returns a const char*, not a simple char*. But the explanation remains: there is no implicit/automatic conversion from string to char* or const char*. Feel free to read about c_str here if it's important to you.
It is not the syntax, it is the structure of the std::string that makes the compiler behave differently.
When you are returning a char* from a function returning std::string, the compiler notices that there is a constructor of std::string that takes char*, calls that constructor, and quietly returns the result.
When you are trying to return a std::string from a char* - returning function, the compiler tries to see if there is a conversion operator to make char* from a std::string, finds that there is no such operator, and reports an error.
If you want to convert a string to char*, you need to make a copy of the string's buffer, like this:
char* ret_ch = new char[ret.size()+1];
memcpy(ret_ch, ret.c_str(), ret.size()+1);
return ret_ch;
You could think that it is OK to return c_str() by itself, but it is not a good idea: the buffer that "backs up" this C string belongs to std::string object, so once the string gets deallocated, accessing the buffer starts producing undefined behavior. That is why you need to make an explicit copy when you access the buffer of a string. Of course you are also responsible for calling delete[] on the copied result.
std::string is designed as implicitly constructable from char const* because this supports using string literals and typical C style code strings as initializer values.
If this was not supported then one would just have to use some intermediate function, which would add nothing but verbosity and inefficiency.
In the other direction, however, std::string is intentionally designed to not convert implicitly to char const*. Part of the rationale is probably that with std::string being logically mutable, the returned raw pointer is only valid as long as no operations are performed that might cause a buffer replacement or string destruction. For example,
char const* s = foo().c_str();
where foo produces a std::string, makes s point to a buffer that no longer exists, a dangling pointer that is invalid.
The c_str() member function call makes the conversion stand out.
Consider how more common that problem could be if one could write just
char const* s = foo();
and have that compile.
Regarding that strike-through (deleted) text, I realized that it's completely irrelevant whether the string is logically mutable or immutable. Sorry. Need more coffee!
I have just done what appears to be a common newbie mistake:
First we read one of many tutorials that goes like this:
#include <fstream>
int main() {
using namespace std;
ifstream inf("file.txt");
// (...)
}
Secondly, we try to use something similar in our code, which goes something like this:
#include <fstream>
int main() {
using namespace std;
std::string file = "file.txt"; // Or get the name of the file
// from a function that returns std::string.
ifstream inf(file);
// (...)
}
Thirdly, the newbie developer is perplexed by some cryptic compiler error message.
The problem is that ifstream takes const * char as a constructor argument.
The solution is to convert std::string to const * char.
Now, the real problem is that, for a newbie, "file.txt" or similar examples given in almost all the tutorials very much looks like a std::string.
So, is "my text" a std::string, a c-string or a *char, or does it depend on the context?
Can you provide examples on how "my text" would be interpreted differently according to context?
[Edit: I thought the example above would have made it obvious, but I should have been more explicit nonetheless: what I mean is the type of any string enclosed within double quotes, i.e. "myfilename.txt", not the meaning of the word 'string'.]
Thanks.
So, is "string" a std::string, a c-string or a *char, or does it depend on the context?
Neither C nor C++ have a built-in string data type, so any double-quoted strings in your code are essentially const char * (or const char [] to be exact). "C string" usually refers to this, specifically a character array with a null terminator.
In C++, std::string is a convenience class that wraps a raw string into an object. By using this, you can avoid having to do (messy) pointer arithmetic and memory reallocations by yourself.
Most standard library functions still take only char * (or const char *) parameters.
You can implicitly convert a char * into std::string because the latter has a constructor to do that.
You must explicitly convert a std::string into a const char * by using the c_str() method.
Thanks to Clark Gaebel for pointing out constness, and jalf and GMan for mentioning that it is actually an array.
"myString" is a string literal, and has the type const char[9], an array of 9 constant char. Note that it has enough space for the null terminator. So "Hi" is a const char[3], and so forth.
This is pretty much always true, with no ambiguity. However, whenever necessary, a const char[9] will decay into a const char* that points to its first element. And std::string has an implicit constructor that accepts a const char*. So while it always starts as an array of char, it can become the other types if you need it to.
Note that string literals have the unique property that const char[N] can also decay into char*, but this behavior is deprecated. If you try to modify the underlying string this way, you end up with undefined behavior. Its just not a good idea.
std::string file = "file.txt";
The right hand side of the = contains a (raw) string literal (i.a. a null-terminated byte string). Its effective type is array of const char.
The = is a tricky pony here: No assignment happens. The std::string class has a constructor that takes a pointer to char as an argument and this is called to create a temporary std::string and this is used to copy-construct (using the copy ctor of std::string) the object file of type std::string.
The compiler is free to elide the copy ctor and directly instantiate file though.
However, note that std:string is not the same thing as a C-style null-terminated string. It is not even required to be null-terminated.
ifstream inf("file.txt");
The std::ifstream class has a ctor that takes a const char * and the string literal passed to it decays to a pointer to the first element of the string.
The thing to remember is this: std::string provides (almost seamless) conversion from C-style strings. You have to look up the signature of the function to see if you are passing in a const char * or a std::string (the latter because of implicit conversions).
So, is "string" a std::string, a c-string or a char*, or does it depend on the context?
It depends entirely on the context. :-) Welcome to C++.
A C string is a null-terminated string, which is almost always the same thing as a char*.
Depending on the platforms and frameworks you are using, there might be even more meanings of the word "string" (for example, it is also used to refer to QString in Qt or CString in MFC).
The C++ standard library provides a std::string class to manage and represent character sequences. It encapsulates the memory management and is most of the time implemented as a C-string; but that is an implementation detail. It also provides manipulation routines for common tasks.
The std::string type will always be that (it doesn't have a conversion operator to char* for example, that's why you have the c_str() method), but it can be initialized or assigned to by a C-string (char*).
On the other hand, if you have a function that takes a std::string or a const std::string& as a parameter, you can pass a c-string (char*) to that function and the compiler will construct a std::string in-place for you. That would be a differing interpretation according to context as you put it.
Neither C nor C++ have a built-in string data type.
When the compiler finds, during the compilation, a double-quoted strings is implicitly referred (see the code below), the string itself is stored in program code/text and generates code to create even character array:
The array is created in static storage because it must persist to be referred later.
The array is made to constant because it must always contain the original data (Hello).
So at last, what you have is const char * to this constant static character array.
const char* v()
{
char* text = “Hello”;
return text;
// Above code can be reduced to:
// return “Hello”;
}
During the program run, when the control finds opening bracket, it creates “text”, the char* pointer, in the stack and constant array of 6 elements (including the null terminator ‘\0’ at the end) in static memory area. When control finds next line (char* text = “Hello”;), the starting address of the 6 element array is assigned to “text”. In next line (return text;), it returns “text”. With the closing bracket “text” will disappear from the stack, but array is still in the static memory area.
You need not to make return type const. But if you try to change the value in static array using non constant char* it will still give you an error during the run time because the array is constant. So, it’s always good to make return constant to make sure, it cannot be referred by non constant pointer.
But if the compiler finds a double-quoted strings is explicitly referred as an array, the compiler assumes that the programmer is going to (smartly) handle it. See the following wrong example:
const char* v()
{
char text[] = “Hello”;
return text;
}
During the compilation, compiler checks, quoted text and save it as it is in the code to fill the generated array during the runt time. Also, it calculate the array size, in this case again as 6.
During the program run, with the open bracket, the array “text[]” with 6 elements is created in stack. But no initialization. When the code finds (char text[] = “Hello”;), the array is initialized (with the text in compiled code). So array is now on the stack. When the compiler finds (return text;), it returns the starting address of the array “text”. When the compiler find the closing bracket, the array disappears from the stack. So no way to refer it by the return pointer.
Most standard library functions still take only char * (or const char *) parameters.
The Standard C++ library has a powerful class called string for manipulating text. The internal data structure for string is character arrays. The Standard C++ string class is designed to take care of (and hide) all the low-level manipulations of character arrays that were previously required of the C programmer. Note that std::string is a class:
You can implicitly convert a char * into std::string because the
latter has a constructor to do that.
You can explicitly convert a std::string into a const char * by using the c_str() method.
As often as possible it should mean std::string (or an alternative such as wxString, QString, etc., if you're using a framework that supplies such. Sometimes you have no real choice but to use a NUL-terminated byte sequence, but you generally want to avoid it when possible.
Ultimately, there simply is no clear, unambiguous terminology. Such is life.
To use the proper wording (as found in the C++ language standard) string is one of the varieties of std::basic_string (including std::string) from chapter 21.3 "String classes" (as in C++0x N3092), while the argument of ifstream's constructor is NTBS (Null-terminated byte sequence)
To quote, C++0x N3092 27.9.1.4/2.
basic_filebuf* open(const char* s, ios_base::openmode mode);
...
opens a file, if possible, whose name is the NTBS s