-1

I want to change the characters in a string passed by user, converted into a C-style string and passed as an argument to a function with a char * argument:

#include <string>
#include <cstring>
#include <iostream>
#include <stdlib.h>

void functoupper(char *myString)
{
    int i=0;
    char z;
    do {
        z= myString[i];
        myString[i]=toupper(z);
        ++i;
    } while(myString[i]!=0);
}

int main() {

    std::string name;

    std::cout << "Please, enter your full name in small caps: ";
    std::getline (std::cin,name);
    const char *myString = name.c_str();
    std::cout << "Hello, " << functoupper(myString) << "!\n";

    return 0;   
}

I get error error: invalid conversion from 'const char*' to 'char*' [-fpermissive] when calling function functoupper(myString) in main().

Max Langhof
  • 23,383
  • 5
  • 39
  • 72
M Garcia
  • 331
  • 3
  • 14
  • 1
    Why not simply: `void functoupper(std::string& myString)`. The condition here should be `while(i < myString.size());` then. – πάντα ῥεῖ Jun 05 '19 at 16:22
  • 6
    It's pretty clear: If it's const you cant alter it. You need to make a copy. As this is C++, just use `std::string` and forget about C strings. – tadman Jun 05 '19 at 16:22
  • 6
    `std::transform(begin(name), end(name), begin(name), std::toupper)` – Some programmer dude Jun 05 '19 at 16:24
  • 3
    `std::cout << "Hello, " << functoupper(myString) << "!\n";` is nonsense regardless. The function returns `void`. – WhozCraig Jun 05 '19 at 16:31
  • 1
    Since you are using C++, I suggest using the C++ header file `#include `. – Eljay Jun 05 '19 at 16:33
  • @Someprogrammerdude: little nitpicking - `transform` works most of the time, [but not always](https://stackoverflow.com/questions/735204/convert-a-string-in-c-to-upper-case). – andreee Jun 05 '19 at 17:00

2 Answers2

3

The std::string::c_str() method returns a pointer to const char data, but your function expects a pointer to non-const char data. That is why you are getting an error.

You could use const_cast to cast away the const (but that is not really advisable):

char *myString = const_cast<char*>(name.c_str());
functoupper(myString);
std::cout << "Hello, " << name << "!\n";

You could use the non-const std::string::operator[] to access the string's underlying character data (just be careful because prior to C++11, characters were not required to be stored contiguously in memory, but most std::string implementations did):

functoupper(&name[0]);
std::cout << "Hello, " << name << "!\n";

In C++17 and later, you can use the non-const std::string::data() method instead:

functoupper(name.data());
std::cout << "Hello, " << name << "!\n";

That being said, heed this warning when using toupper():

Like all other functions from <cctype>, the behavior of std::toupper is undefined if the argument's value is neither representable as unsigned char nor equal to EOF. To use these functions safely with plain chars (or signed chars), the argument should first be converted to unsigned char ... Similarly, they should not be directly used with standard algorithms when the iterator's value type is char or signed char. Instead, convert the value to unsigned char first

With that said, try something more like this:

#include <string>
#include <iostream>
#include <cctype>

void functoupper(char *myString)
{
    for (int i = 0; myString[i] != '\0'; ++i) {
        unsigned char z = static_cast<unsigned char>(myString[i]);
        myString[i] = static_cast<char>(std::toupper(z));
    }
}

int main() {

    std::string name;

    std::cout << "Please, enter your full name in small caps: ";
    std::getline(std::cin, name);
    functoupper(&name[0]); // or name.data()
    std::cout << "Hello, " << name << "!\n";

    return 0;   
}

That being said, you should just pass the entire std::string as-is into your function instead, and then you can manipulate it as needed, for instance with the std::transform() algorithm:

#include <string>
#include <iostream>
#include <algorithm>
#include <cctype>

void functoupper(std::string &myString)
{
    std::transform(myString.begin(), myString.end(), myString.begin(),
      [](unsigned char ch){ return std::toupper(ch); }
    );
}

int main() {

    std::string name;

    std::cout << "Please, enter your full name in small caps: ";
    std::getline(std::cin, name);
    functoupper(name);
    std::cout << "Hello, " << name << "!\n";

    return 0;   
}

Alternatively:

#include <string>
#include <iostream>
#include <algorithm>
#include <cctype>

std::string functoupper(std::string myString)
{
    std::transform(myString.begin(), myString.end(), myString.begin(),
      [](unsigned char ch){ return std::toupper(ch); }
    );
    return myString;
}

int main() {

    std::string name;

    std::cout << "Please, enter your full name in small caps: ";
    std::getline(std::cin, name);
    std::cout << "Hello, " << functoupper(name) << "!\n";

    return 0;   
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
2

As @Someprogrammerdude and @RemyLebeau comment, why not simply:

std::transform(std::begin(name), std::end(name), std::begin(name), 
    [](const unsigned char c)
    {
        return std::toupper(c);
    });

But if you must do it via a char*, then you'll need to copy the data over first, something like:

char myString* = new char[name.size() + 1];
strcpy(myString, name.c_str());

EDIT: Thanks to the helpful comments by @RemyLebeau

Better still avoid all the memory management issues with the above by simply coping your std::string into a std::vector:

std::vector<char> myVec(std::begin(name), std::end(name));
myVec.push_back(`\0`);

and then call your char* function with:

functoupper(myVec.data());
Paul Evans
  • 27,315
  • 3
  • 37
  • 54
  • 1
    Consider using `std::vector` instead of `new[]`. Also, when using `toupper()`, you need to cast `char` values to `unsigned char` or you can end up with bad results when `char` is signed, which means you should not use `toupper` as the actual `transform` predicate, use a lambda or functor that casts chars as needed. – Remy Lebeau Jun 05 '19 at 16:59
  • If you are going to use `string::size()` to allocate a `char[]` array, consider using `string::copy()` instead of `strcpy()` to copy the string's chars into the array – Remy Lebeau Jun 06 '19 at 16:05