2

I know I can do this by str.c_str(), but I do not want a character constant. I want a char so that I can make some modifications.

char* removeDup(char *s)
{


        int len = strlen(s);
        int p,q, idx = -1;
    for(p = 0; p< len; p++)
   {
    char temp = s[p];
    bool flag = true;
    for(q=0;q<p;q++)
    {
        if(s[q] == temp)
        {
            flag = false;
            break;
        }
    }
    if(flag == true)
    {
        s[++idx] = temp;
    }
}
    s[++idx] = '\0';
    return s;
}

if I call this function as below, I get errors;

string s = "abcde";
removeDuplicate(s.c_str());

I need to convert this s into char and not const char.

1
  • 10
    My suggestion would be to pass in a string instead and skip char* completely Commented Jan 9, 2012 at 13:25

6 Answers 6

5

To get the underlying data from a std::string You can use:

string::data() or string::c_str(), both return a const char *.

In either case the returned data is const char * because the memory for it is allocated in some read only implementation defined region which an user program is not allowed to modify. Any attempt to modify the returned const char * would result in Undefined Behavior.

So you cannot and should not(through const_cast) modify the returned character string.

The only correct way to achieve this by creating a new char*, allocate it, and copy in the contents from the const char*:

std::string myString = "blabla";
char* myPtr = new char[myString.size() + 1];
myString.copy(myPtr, myString.size());
myPtr[myString.size()] = '\0';
Sign up to request clarification or add additional context in comments.

14 Comments

@Roger well, that's impossible. A C-style cast to char* will break the program, as it will result in undefined behavior. Why not use std:string's functions to achieve what you want?
@Roger You need to use an extra buffer - the one returned by c_str is owned by the original string - so you can't modify it.
@Cicada: casting away the constness would be an UB.
@WTP: Indeed, but it needs be std::unique_ptr<char[]>, so that the array is correctly deleted.
@Dennis std::vector<char> would be an even better choice, since it takes care of both the allocation and the deallocation. (Really, the only valid choice is to rewrite the function to use iterators---and to hopefully make it readable at the same time.)
|
2

I would suggest making a copy of the buffer, calling your function, and then setting the original string to the new char buffer. Something like:

std::string str("some string");
char tmp[str.length() + 1];
memset(tmp, 0, sizeof(tmp));
copy(str.begin(), str.end(), tmp);
str = removeDup(tmp);

Using the const buffer returned by c_str() directly and modifying it is asking for trouble. The buffer is owned by the string object and you should think of modifying that as breaking encapsulation and at minimum implementation dependent.

2 Comments

More than breaking the encapsulation(which probably one could get away with) casting away the const is a guaranteed Undefined Behavior.
Note that variable-length arrays are a non-standard extension to C++. If you want portability, use std::vector<char>.
1

Just copy it.

string str = "Hello";
char * cStr = new char[str.size()];
memcpy(cStr, str.c_str(), str.size());

1 Comment

And don't forget to delete the dynamic array when you're finished (or better yet use std::vector<char>). Also, it looks like the evil function in the question requires a zero-terminated string, so use str.size()+1 to include the terminator.
1

The only way to legally make modifications to a std::string is through it's member functions (including the access paths they indirectly provide). Thus, you should rewrite your removeDup to take std::string::iterator as arguments, something like:

std::string
removeDup( std::string& original )
{
    std::string::iterator current = original.begin();
    std::string::iterator end = original.end();
    while ( current != end ) {
        end = std::remove( current + 1, end, *current );
        ++ current;
    }
    original.erase( end, original.end() );
    return original;
}

(I think this does what your original code does. I can't be sure, because I couldn't really figure out your original code.)

From a design point of view, this is ugly; you should probably pass a std::string const&, and return a new std::string:

std::string
removeDup( std::string const& original )
{
    std::string results;
    std::bitset<UCHAR_MAX + 1> alreadySeen;
    for ( std::string::const_iterator current = original.begin();
            current != original.end();
            ++ current ) {
        if (! alreadySeen.test( static_cast<unsigned char>( *current ) ) ) {
            results += *current;
            alreadySeen.set( static_cast<unsigned char>( *current ));
        }
    }
    return results;
}

The only time you want to get a char* from a std::string is to pass it to legacy code (or C). In such cases, std::string::c_str() is the approved method; if the function you are calling requires a char*, then:

  • if the function doesn't actually modify the string (it's not const correct, but this is the case for many C functions), then use const_cast on the return value of std::string::c_str(), otherwise

  • you must allocate a local buffer, and pass it:

    std::vector localBuffer( s.begin(), s.end() ); localBuffer.push_back( '\0' ); legacyFunction( &localBuffer[0], localBuffer.size() );

Comments

0

I think you want this method from string:

Copy sequence of characters from string: http://www.cplusplus.com/reference/string/string/copy/

eg. If you had a string 'str', you could do this:

char buf[str.len()+1]
str.copy(buf, str.len())

//terminate with newline
buf[str.len() = '\n']

Hope that helps.

Comments

0

In the realm of 'possible' as opposed to 'sensible' you could do:

std::string str("foo");
str = removeDup( std::auto_ptr<char>(strdup(str.c_str()).get() );

But rewriting in terms of std::string would be better.

1 Comment

Although this does assume that malloc and new are synonymous for chars.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.