I have char* which is of fixed (known) width but is not null terminated.
I want to pass it into LOG4CPLUS_ERROR("Bad string " << char_pointer); but as its not null terminated it will print it all.
Any suggestions of some light weight way of getting "(char[length])*char_pointer" without performing a copy?
No, you'll have to deep-copy and null-terminate it. That code expects a null-terminated string and it means a contiguous block of characters ending with a null terminator.
If your goal is to print such a string, you could:
Store the last byte.
Replace it with \0.
Print the string.
Print the stored byte.
Put the stored byte back into the last position of the string.
Wrap all this in a function.
Real iostreams
When you're writing to a real iostream, then you can just use ostream.write() which takes a char* and a size for how many bytes to write -- no null termination necessary. (In fact, any null characters in the string would be written to the ostream, and would not be used to determine the size.)
Logging libraries
In some logging libraries, the stream that you write to is not a real iostream. This is the case in Log4CPP.
However, in Log4CPlus which is what it appears matt is using, the object that you're writing to is a std::basic_ostringstream<tchar> (see loggingmacros.h and streams.h for the definition, since none of this is obvious from the documentation). There's just one problem: in the macro LOG4CPLUS_ERROR, the first << is already built into the macro, so he won't be able to call LOG4CPLUS_ERROR(.write(char_pointer,length)) or anything like that. Unfortunately, I don't see an easy way around this without deconstructing the LOG4CPLUS_ERROR error macro and getting into the internals of Log4CPlus
Solution
I'm not sure why you're trying to avoid copying the string at this point, since you can see that there's a lot of copying going on inside the logging library. Any attempt to avoid that extra string copy is probably unwarranted optimization.
I'm going to assume that it's an issue of code cleanliness, and maybe an issue of making sure the copy happens inside the LOG4CPLUS_ERROR macro, as opposed to outside it. In that case, just use:
LOG4CPLUS_ERROR("Bad string " << std::string(char_pointer, length));
We're getting hung up on the semantics of conversion between char* and char[]. Take a step back, what are you trying to do? If this is a simple case of on an error condition, streaming out the content of a structure to a stream, why not do it properly?
e.g.
struct foo
{
char a1[10];
char a2[10];
char a3[10];
char a4[10];
};
// free function to stream the above structure properly..
std::ostream operator<<(std::ostream& str, foo const& st)
{
str << "foo::a1[";
std::copy(st.a1, st.a1 + sizeof(st.a1), std::ostream_iterator<char>(str));
str << "]\n";
str << "foo::a2[";
std::copy(st.a2, st.a2 + sizeof(st.a2), std::ostream_iterator<char>(str));
str << "]\n";
:
return str;
}
Now you can simply stream out an instance of foo and don't have to worry about null terminated string etc.!
I keep a string reference class in my toolkit just for these type of situations. Here is a greatly abbreviated version of that class. I trimmed away anything that is not relevant to this particular problem:
#include <iostream>
class stringref {
public:
stringref(const char* ptr, unsigned len) : ptr(ptr), len(len) {}
unsigned length() { return len; }
const char* data() { return ptr; }
private:
const char* ptr;
unsigned len;
};
std::ostream& operator<< (std::ostream& os, stringref sr) {
const char* data = sr.data();
for (unsigned len = sr.length(); len--; )
os << *data++;
return os;
}
using namespace std;
int main (int argc, const char * argv[])
{
cout << "string: " << stringref("test", 4) << endl;
}
or, in your case:
LOG4CPLUS_ERROR("Bad string " << stringref(char_pointer, length));
should work.
The idea of a string reference class is to keep enough information about a string (a size and a pointer) to refer to any block of memory which represents a string. It relies on you to make sure that the underlying string data is valid throughout the lifetime of a stringref object. This way you can pass around and process string information with a minimum of overhead.
When you know it is of fixed length: Why not simply add one more charakter to the size of the array? Then you can easily fill this last char with \0 terminating character and all will be fine
No, you'll have to copy it. There is no proper conversion in the language that you can use to get the array type out of it.
It seems very odd that you want to do this, or that you have a non-terminated C-style string in the first place.
Why are you not using std::string?
Related
So recently I was playing with the concept of creating my own C++ classes that represent generic data (such as strings, numbers and arrays).
And so far my progress on this has been good (as seen here: https://github.com/LapysDev/LapysCPP).
Except one hitch. For the life of me, I can not figure out why the code below faults when it comes to creating a String class object with a variable amount of arguments.
#include <iostream>
#include <sstream>
#include <string.h>
// Make a new C-style string (or stringify a value).
char* stringify(char character) {
std::string stream = static_cast<std::ostringstream*>(&(std::ostringstream() << character)) -> str();
char* string = new char[stream.size() + 1];
strcpy(string, stream.c_str());
return string;
}
template <typename data> char* stringify(data string) { return strdup(std::string(string).c_str()); }
char* globalString = stringify("");
class String {
public:
char* value = stringify("");
String() {}
template <typename data>
String(data value) {
strcat(globalString, value);
this -> value = stringify(globalString);
globalString = stringify("");
}
template <typename data, typename... argumentsData>
String(data value, argumentsData... values) {
strcat(globalString, stringify(value));
String(values...);
}
};
int main(int argc, char* argv[]) {
std::cout << "String [1]: '" << String("Hello, World!").value << '\'' << std::endl;
// -> String [1]: 'Hello, World!'
std::cout << "String [3]: '" << String("Hello,", ' ', "World!").value << '\'';
// -> String [3]: ''
return 0;
}
I have tried everything I can with the code already (and yes, using an std::string for the text value is banned). If there's anyone out there that can explain why using multiple arguments faults when using char*'s, you're welcome to comment.
To summarize, I need to be able to create a String object with a proper value property using a variable amount of arguments.
// Works fine
String("Hello, World!").value // -> Hello, World!
// Needs fixing
String("Hello,", ' ', "World!").value // -> ...
I understand that this may not be platform to ask questions of this nature but a little help would go a long way. Thanks for reading through.
globalString is char* that points to the return value of stringify("");.
stringify("") returns strdup(std::string(string).c_str());. strdup returns a dynamically allocated string that has the length of its parameter ( and the same contents) .
Here, "" only contains \0 so the C-string returned from strdup will only have a length of 1.
You then try to call strcat(destination, source) with globalString as the destination, but globalString isn't big enough to fit the source.
strcat says:
The behavior is undefined if the destination array is not large
enough for the contents of both src and dest and the terminating null
character. The behavior is undefined if the strings overlap. The
behavior is undefined if either dest or src is not a pointer to a
null-terminated byte string.
So both of your test cases are UB. Even the first test that seems to work well.
std::string handles all of this for you. If you somehow aren't allowed to use it for whatever (stupid) reason a professor has given you, then make sure to allocate enough space for globalString before calling strcat on it, C-strings are tricky beasts.
I am learning pointers and i tried this following program
#include <iostream>
#include <cstdlib>
#include <cstdio>
using namespace std;
char* getword()
{
char*temp=(char*)malloc(sizeof(char)*10);
cin>>temp;
return temp;
}
int main()
{
char *a;
a=getword();
cout<<a;
return 0;
}
To my level of understanding, a is a pointer to a character, and in the function getword() I returned temp which I think the base &temp[0]. I thought that the output would be the first character of the string I enter, but I got the entire string in stdout. How does this work?
In the tradition of C, a char* represents a string. Indeed, any string literal in your program (e.g. "hello") will have a type of const char *.
Thus, cout::operator<<( const char * ) is implemented as a string-output. It will output characters beginning at the address it is given, until it encounters the string terminator (otherwise known as null-terminator, or '\0').
If you want to output a single character, you need to dereference the pointer into a char type. You can choose one of the following syntaxes:
cout << *a; // Dereference the pointer
cout << a[0]; // Use array index of zero to return the value at that address
It should be noted that the code you provided isn't very C++ish. For starters, we generally don't use malloc in C++. You then leak the memory by not calling free later. The memory is uninitialised and relies on cin succeeding (which might not be the case). Also, you can only handle input strings of up to 9 characters before you will get undefined behaviour.
Perhaps you should learn about the <string> library and start using it.
It's true that char* "points to a character". But, by convention, and because with pointers there is no other way to do so, we also use it to "point to more than one character".
Since use of char* almost always means you're using a pointer to a C-style string, the C++ streams library makes this assumption for you, printing the char that your pointer points to … and the next … and the next … and the next until NULL is found. That's just the way it's been designed to work.
You can print just that character if you like by dereferencing the pointer to obtain an actual char.
std::cout is an overloaded operator and when it receives a char * as an operand then it treats it as a pointer to c style string and it will print the entire string.
If you want to print the first character of the string then use
cout << *a;
or
cout << a[0];
In your code, std::cout is an ostream and providing a char* variable as input to operator<< invokes a particular operator function overload to write characters to the ostream.
std::ostream also has a operator overload for writing a single character to itself.
I'm assuming you now know how to dereference a char* variable, but you should be using std::string instead of an unsafe char* type.
Here is the correct code
#include <stdio.h>
#include <stdlib.h>
char* getword()
{
char*temp=(char*)malloc(sizeof(char)*10);
scanf("%s",temp);
return temp;
}
int main()
{
char *a;
a = getword();
int currChar = 1;
printf("%c",*(a + currChar)); //increment currChar to get next character
return 0;
}
I have been working with C++ strings and trying to load char * strings into std::string by using C functions such as strcpy(). Since strcpy() takes char * as a parameter, I have to cast it which goes something like this:
std::string destination;
unsigned char *source;
strcpy((char*)destination.c_str(), (char*)source);
The code works fine and when I run the program in a debugger, the value of *source is stored in destination, but for some odd reason it won't print out with the statement
std::cout << destination;
I noticed that if I use
std::cout << destination.c_str();
The value prints out correctly and all is well. Why does this happen? Is there a better method of copying an unsigned char* or char* into a std::string (stringstreams?) This seems to only happen when I specify the string as foo.c_str() in a copying operation.
Edit: To answer the question "why would you do this?", I am using strcpy() as a plain example. There are other times that it's more complex than assignment. For example, having to copy only X amount of string A into string B using strncpy() or passing a std::string to a function from a C library that takes a char * as a parameter for a buffer.
Here's what you want
std::string destination = source;
What you're doing is wrong on so many levels... you're writing over the inner representation of a std::string... I mean... not cool man... it's much more complex than that, arrays being resized, read-only memory... the works.
This is not a good idea at all for two reasons:
destination.c_str() is a const pointer and casting away it's const and writing to it is undefined behavior.
You haven't set the size of the string, meaning that it won't even necessealy have a large enough buffer to hold the string which is likely to cause an access violation.
std::string has a constructor which allows it to be constructed from a char* so simply write:
std::string destination = source
Well what you are doing is undefined behavior. Your c_str() returns a const char * and is not meant to be assigned to. Why not use the defined constructor or assignment operator.
std::string defines an implicit conversion from const char* to std::string... so use that.
You decided to cast away an error as c_str() returns a const char*, i.e., it does not allow for writing to its underlying buffer. You did everything you could to get around that and it didn't work (you shouldn't be surprised at this).
c_str() returns a const char* for good reason. You have no idea if this pointer points to the string's underlying buffer. You have no idea if this pointer points to a memory block large enough to hold your new string. The library is using its interface to tell you exactly how the return value of c_str() should be used and you're ignoring that completely.
Do not do what you are doing!!!
I repeat!
DO NOT DO WHAT YOU ARE DOING!!!
That it seems to sort of work when you do some weird things is a consequence of how the string class was implemented. You are almost certainly writing in memory you shouldn't be and a bunch of other bogus stuff.
When you need to interact with a C function that writes to a buffer there's two basic methods:
std::string read_from_sock(int sock) {
char buffer[1024] = "";
int recv = read(sock, buffer, 1024);
if (recv > 0) {
return std::string(buffer, buffer + recv);
}
return std::string();
}
Or you might try the peek method:
std::string read_from_sock(int sock) {
int recv = read(sock, 0, 0, MSG_PEEK);
if (recv > 0) {
std::vector<char> buf(recv);
recv = read(sock, &buf[0], recv, 0);
return std::string(buf.begin(), buf.end());
}
return std::string();
}
Of course, these are not very robust versions...but they illustrate the point.
First you should note that the value returned by c_str is a const char* and must not be modified. Actually it even does not have to point to the internal buffer of string.
In response to your edit:
having to copy only X amount of string A into string B using strncpy()
If string A is a char array, and string B is std::string, and strlen(A) >= X, then you can do this:
B.assign(A, A + X);
passing a std::string to a function from a C library that takes a char
* as a parameter for a buffer
If the parameter is actually const char *, you can use c_str() for that. But if it is just plain char *, and you are using a C++11 compliant compiler, then you can do the following:
c_function(&B[0]);
However, you need to ensure that there is room in the string for the data(same as if you were using a plain c-string), which you can do with a call to the resize() function. If the function writes an unspecified amount of characters to the string as a null-terminated c-string, then you will probably want to truncate the string afterward, like this:
B.resize(B.find('\0'));
The reason you can safely do this in a C++11 compiler and not a C++03 compiler is that in C++03, strings were not guaranteed by the standard to be contiguous, but in C++11, they are. If you want the guarantee in C++03, then you can use std::vector<char> instead.
Is there an exact equivalent to strncpy in the C++ Standard Library? I mean a function, that copies a string from one buffer to another until it hits the terminating 0? For instance when I have to parse strings from an unsafe source, such as TCP packets, so I'm able to perform checks in length while coping the data.
I already searched a lot regarding this topic and I also found some interesting topics, but all of those people were happy with std::string::assign, which is also able to take a size of characters to copy as a parameter. My problem with this function is, that it doesn't perform any checks if a terminating null was already hit - it takes the given size serious and copies the data just like memcpy would do it into the string's buffer. This way there is much more memory allocated and copied than it had to be done, if there were such a check while coping.
That's the way I'm working around this problem currently, but there is some overhead I'd wish to avoid:
// Get RVA of export name
const ExportDirectory_t *pED = (const ExportDirectory_t*)rva2ptr(exportRVA);
sSRA nameSra = rva2sra(pED->Name);
// Copy it into my buffer
char *szExportName = new char[nameSra.numBytesToSectionsEnd];
strncpy(szExportName,
nameSra.pSection->pRawData->constPtr<char>(nameSra.offset),
nameSra.numBytesToSectionsEnd);
szExportName[nameSra.numBytesToSectionsEnd - 1] = 0;
m_exportName = szExportName;
delete [] szExportName;
This piece of code is part of my parser for PE-binaries (of the routine parsing the export table, to be exact). rva2sra converts a relative virtual address into a PE-section relative address. The ExportDirectory_t structure contains the RVA to the export name of the binary, which should be a zero-terminated string. But that doesn't always have to be the case - if someone would like it, it would be able to omit the terminating zero which would make my program run into memory which doesn't belong to the section, where it would finally crash (in the best case...).
It wouldn't be a big problem to implement such a function by myself, but I'd prefer it if there were a solution for this implemented in the C++ Standard Library.
If you know that the buffer you want to make a string out of has at least one NUL in it then you can just pass it to the constructor:
const char[] buffer = "hello\0there";
std::string s(buffer);
// s contains "hello"
If you're not sure, then you just have to search the string for the first null, and tell the constructor of string to make a copy of that much data:
int len_of_buffer = something;
const char* buffer = somethingelse;
const char* copyupto = std::find(buffer, buffer + len_of_buffer, 0); // find the first NUL
std::string s(buffer, copyupto);
// s now contains all the characters up to the first NUL from buffer, or if there
// was no NUL, it contains the entire contents of buffer
You can wrap the second version (which always works, even if there isn't a NUL in the buffer) up into a tidy little function:
std::string string_ncopy(const char* buffer, std::size_t buffer_size) {
const char* copyupto = std::find(buffer, buffer + buffer_size, 0);
return std::string(buffer, copyupto);
}
But one thing to note: if you hand the single-argument constructor a const char* by itself, it will go until it finds a NUL. It is important that you know there is at least one NUL in the buffer if you use the single-argument constructor of std::string.
Unfortunately (or fortunately), there is no built in perfect equivalent of strncpy for std::string.
The std::string class in STL can contain null characters within the string ("xxx\0yyy" is a perfectly valid string of length 7). This means that it doesn't know anything about null termination (well almost, there are conversions from/to C strings). In other words, there's no alternative in the STL for strncpy.
There are a few ways to still accomplish your goal with a shorter code:
const char *ptr = nameSra.pSection->pRawData->constPtr<char>(nameSra.offset);
m_exportName.assign(ptr, strnlen(ptr, nameSra.numBytesToSectionsEnd));
or
const char *ptr = nameSra.pSection->pRawData->constPtr<char>(nameSra.offset);
m_exportName.reserve(nameSra.numBytesToSectionsEnd);
for (int i = 0; i < nameSra.numBytesToSectionsEnd && ptr[i]; i++)
m_exportName += ptr[i];
Is there an exact equivalent to strncpy in the C++ Standard Library?
I certainly hope not!
I mean a function, that copies a string from one buffer to another until it hits the terminating 0?
Ah, but that's not what strncpy() does -- or at least it's not all it does.
strncpy() lets you specify the size, n, of the destination buffer, and copies at most n characters. That's fine as far as it goes. If the length of the source string ("length" defined as the number of characters preceding the terminating '\0') exceeds n, the destination buffer is padded with additional \0's, something that's rarely useful. And if the length if the source string exceeds n, then the terminating '\0' is not copied.
The strncpy() function was designed for the way early Unix systems stored file names in directory entries: as a 14-byte fixed-size buffer that can hold up to a 14-character name. (EDIT: I'm not 100% sure that was the actual motivation for its design.) It's arguably not a string function, and it's not just a "safer" variant of strcpy().
You can achieve the equivalent of what one might assume strncpy() does (given the name) using strncat():
char dest[SOME_SIZE];
dest[0] = '\0';
strncat(dest, source_string, SOME_SIZE);
This will always '\0'-terminate the destination buffer, and it won't needlessly pad it with extra '\0' bytes.
Are you really looking for a std::string equivalent of that?
EDIT : After I wrote the above, I posted this rant on my blog.
There is no built-in equivalent. You have to roll your own strncpy.
#include <cstring>
#include <string>
std::string strncpy(const char* str, const size_t n)
{
if (str == NULL || n == 0)
{
return std::string();
}
return std::string(str, std::min(std::strlen(str), n));
}
The string's substring constructor can do what you want, although it's not an exact equivalent of strncpy (see my notes at the end):
std::string( const std::string& other,
size_type pos,
size_type count = std::string::npos,
const Allocator& alloc = Allocator() );
Constructs the string with a substring [pos, pos+count) of other. If count == npos or if the requested substring lasts past the end of the string, the resulting substring is [pos, size()).
Source: http://www.cplusplus.com/reference/string/string/string/
Example:
#include <iostream>
#include <string>
#include <cstring>
int main ()
{
std::string s0 ("Initial string");
std::string s1 (s0, 0, 40); // count is bigger than s0's length
std::string s2 (40, 'a'); // the 'a' characters will be overwritten
strncpy(&s2[0], s0.c_str(), s2.size());
std::cout << "s1: '" << s1 << "' (size=" << s1.size() << ")" << std::endl;
std::cout << "s2: '" << s2 << "' (size=" << s2.size() << ")" << std::endl;
return 0;
}
Output:
s1: 'Initial string' (size=14)
s2: 'Initial string' (size=40)
Differences with strncpy:
the string constructor always appends a null-terminating character to the result, strncpy does not;
the string constructor does not pad the result with 0s if a null-terminating character is reached before the requested count, strncpy does.
Use the class' constructor:
string::string str1("Hello world!");
string::string str2(str1);
This will yield an exact copy, as per this documentation: http://www.cplusplus.com/reference/string/string/string/
std::string has a constructor with next signature that can be used :
string ( const char * s, size_t n );
with next description:
Content is initialized to a copy of the string formed by the first n characters in the array of characters pointed by s.
For C, we use char[] to represent strings.
For C++, I see examples using both std::string and char arrays.
#include <iostream>
#include <string>
using namespace std;
int main () {
string name;
cout << "What's your name? ";
getline(cin, name);
cout << "Hello " << name << ".\n";
return 0;
}
#include <iostream>
using namespace std;
int main () {
char name[256];
cout << "What's your name? ";
cin.getline(name, 256);
cout << "Hello " << name << ".\n";
return 0;
}
(Both examples adapted from http://www.cplusplus.com.)
What is the difference between these two types in C++? (In terms of performance, API integration, pros/cons, ...)
A char array is just that - an array of characters:
If allocated on the stack (like in your example), it will always occupy eg. 256 bytes no matter how long the text it contains is
If allocated on the heap (using malloc() or new char[]) you're responsible for releasing the memory afterwards and you will always have the overhead of a heap allocation.
If you copy a text of more than 256 chars into the array, it might crash, produce ugly assertion messages or cause unexplainable (mis-)behavior somewhere else in your program.
To determine the text's length, the array has to be scanned, character by character, for a \0 character.
A string is a class that contains a char array, but automatically manages it for you. Most string implementations have a built-in array of 16 characters (so short strings don't fragment the heap) and use the heap for longer strings.
You can access a string's char array like this:
std::string myString = "Hello World";
const char *myStringChars = myString.c_str();
C++ strings can contain embedded \0 characters, know their length without counting, are faster than heap-allocated char arrays for short texts and protect you from buffer overruns. Plus they're more readable and easier to use.
However, C++ strings are not (very) suitable for usage across DLL boundaries, because this would require any user of such a DLL function to make sure he's using the exact same compiler and C++ runtime implementation, lest he risk his string class behaving differently.
Normally, a string class would also release its heap memory on the calling heap, so it will only be able to free memory again if you're using a shared (.dll or .so) version of the runtime.
In short: use C++ strings in all your internal functions and methods. If you ever write a .dll or .so, use C strings in your public (dll/so-exposed) functions.
Arkaitz is correct that string is a managed type. What this means for you is that you never have to worry about how long the string is, nor do you have to worry about freeing or reallocating the memory of the string.
On the other hand, the char[] notation in the case above has restricted the character buffer to exactly 256 characters. If you tried to write more than 256 characters into that buffer, at best you will overwrite other memory that your program "owns". At worst, you will try to overwrite memory that you do not own, and your OS will kill your program on the spot.
Bottom line? Strings are a lot more programmer friendly, char[]s are a lot more efficient for the computer.
Well, string type is a completely managed class for character strings, while char[] is still what it was in C, a byte array representing a character string for you.
In terms of API and standard library everything is implemented in terms of strings and not char[], but there are still lots of functions from the libc that receive char[] so you may need to use it for those, apart from that I would always use std::string.
In terms of efficiency of course a raw buffer of unmanaged memory will almost always be faster for lots of things, but take in account comparing strings for example, std::string has always the size to check it first, while with char[] you need to compare character by character.
I personally do not see any reason why one would like to use char* or char[] except for compatibility with old code. std::string's no slower than using a c-string, except that it will handle re-allocation for you. You can set it's size when you create it, and thus avoid re-allocation if you want. It's indexing operator ([]) provides constant time access (and is in every sense of the word the exact same thing as using a c-string indexer). Using the at method gives you bounds checked safety as well, something you don't get with c-strings, unless you write it. Your compiler will most often optimize out the indexer use in release mode. It is easy to mess around with c-strings; things such as delete vs delete[], exception safety, even how to reallocate a c-string.
And when you have to deal with advanced concepts like having COW strings, and non-COW for MT etc, you will need std::string.
If you are worried about copies, as long as you use references, and const references wherever you can, you will not have any overhead due to copies, and it's the same thing as you would be doing with the c-string.
One of the difference is Null termination (\0).
In C and C++, char* or char[] will take a pointer to a single char as a parameter and will track along the memory until a 0 memory value is reached (often called the null terminator).
C++ strings can contain embedded \0 characters, know their length without counting.
#include<stdio.h>
#include<string.h>
#include<iostream>
using namespace std;
void NullTerminatedString(string str){
int NUll_term = 3;
str[NUll_term] = '\0'; // specific character is kept as NULL in string
cout << str << endl <<endl <<endl;
}
void NullTerminatedChar(char *str){
int NUll_term = 3;
str[NUll_term] = 0; // from specific, all the character are removed
cout << str << endl;
}
int main(){
string str = "Feels Happy";
printf("string = %s\n", str.c_str());
printf("strlen = %d\n", strlen(str.c_str()));
printf("size = %d\n", str.size());
printf("sizeof = %d\n", sizeof(str)); // sizeof std::string class and compiler dependent
NullTerminatedString(str);
char str1[12] = "Feels Happy";
printf("char[] = %s\n", str1);
printf("strlen = %d\n", strlen(str1));
printf("sizeof = %d\n", sizeof(str1)); // sizeof char array
NullTerminatedChar(str1);
return 0;
}
Output:
strlen = 11
size = 11
sizeof = 32
Fee s Happy
strlen = 11
sizeof = 12
Fee
Think of (char *) as string.begin(). The essential difference is that (char *) is an iterator and std::string is a container. If you stick to basic strings a (char *) will give you what std::string::iterator does. You could use (char *) when you want the benefit of an iterator and also compatibility with C, but that's the exception and not the rule. As always, be careful of iterator invalidation. When people say (char *) isn't safe this is what they mean. It's as safe as any other C++ iterator.
Strings have helper functions and manage char arrays automatically. You can concatenate strings, for a char array you would need to copy it to a new array, strings can change their length at runtime. A char array is harder to manage than a string and certain functions may only accept a string as input, requiring you to convert the array to a string. It's better to use strings, they were made so that you don't have to use arrays. If arrays were objectively better we wouldn't have strings.