简体   繁体   中英

concatenate const char * strings

I'm confused about char * and const char * . In my example I'm not sure how to put them together. I have several const char * strings I would like to concatenate to a final const char * string.

struct MyException : public std::exception
{
  const char *source;
  int number;
  const char *cause;

  MyException(const char *s, int n)
      : source(s), number(n) {}
  MyException(const char *s, const char *c)
      : source(s), number(0), cause(c) {}

  const char *what() const throw()
  {
    if (number != 0) {
      char buffer[1024];
      // why does this not work?
      cause = strerror_r(number, buffer, 1024);
    }

    // how to concatenate the strings?
    return source + ": " + cause;
  }
};

You can store a std::string and still return a const char * from your what function.

struct MyException : public std::exception
{
private:
  std::string message;  

public:    
  MyException(const char *s, int n) {
    char buffer[1024];
    strerror_r(n, buffer, 1024);
    message.reserve(strlen(s) + 2 + strlen(buffer));
    message = s;
    message += ": ";
    message += buffer;
  }

  MyException(const char *s, const char *c) {
    message.reserve(strlen(s) + 2 + strlen(c));
    message = s;
    message += ": ";
    message += c;
  }

  const char *what() const throw()
  {
    return message.c_str();
  }
};

Just use strcat() and strcpy() function from string.h .

http://www.cplusplus.com/reference/clibrary/cstring/strcat/ http://www.cplusplus.com/reference/clibrary/cstring/strcpy/

Also, since you don't have to modify original strings, the difference between const char* and char* doesn't matter.

Also don't forget to malloc() (reserve the space for) the required size of destination string.

This is how I'd implement this:

struct MyException : public std::exception
{
public:
  const char *source;
  int number;
  const char *cause;
private:
  char buffer[1024]; // #1
  std::string message; // #2

  std::string build_message() {
    if (number != 0) {
      cause = strerror_r(number, buffer, 1024); // use the member buffer
    }
    std::string s; // #3
    s.reserve(strlen(source) + 2 + strlen(cause));
    return s + source + ": " + cause;
  }

public:
  MyException(const char *s, int n)
      : source(s), number(n), cause(), message(build_message()) {}
  MyException(const char *s, const char *c)
      : source(s), number(0), cause(c), message(build_message()) {}

  const char *what() const throw()
  {
    return message.c_str(); // #4
  }
};

Things to note:

  1. The original code was using a local variable for a buffer. That is a bad idea, as the pointer stored in cause would be invalid the moment the scope ends .

  2. For the concatenated message, dynamic allocation would be required. And that also means that cleanup of that storage would be required. I grabbed an existing tool that does that and provides string-like operations: std::string .

  3. With std::string concatenation can be done with the + operator. Note how I asked it to reserve memory for the expected size. This is memory an optimization, and is not required: the string would allocate enough memory either way.

  4. what cannot throw an exception, otherwise a call std::unexpected would arise. So the string cannot be allocated here.

If you must work with char* pointers, you will want to use strcat . strcat takes two arguments a char* and a const char* and appends the string pointed to by the const char* onto the char* . This means you first need to copy your first string over.

You'll want to do something like this:

char* Concatenate(const char* first, const char* second)
{
  char* mixed = new char[strlen(first) + strlen(second) + 2 /* for the ': ' */ + 1 /* for the NULL */];
  strcpy(mixed, first);
  strcat(mixed, ": ");
  strcat(mixed, second);

  return mixed;
}

Isn't that just ugly? And, remember, because you've dynamically allocated the char* returned by that function the caller must remember to delete[] it. This ugliness and the need to ensure the caller cleans up in the right way is why you're better off using a string implementation such as std::string .

Allocate a buffer of size strlen(source) + strlen(cause) + 3 and use sprintf to create your message. Actually you can move this code to constructor so that what becomes simple getter.

If you really must use c-strings, you should look at strcat() to concatenate them together. However, since you are creating a custom exception, it would be reasonable to consider using std::string instead because it is more friendly to use in C++.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM