Differences using char* and std::string

Hello,

I've been programming for a couple years now, and through out my article readings and code snippet browsing, i have often seen things like:
1
2
3
4
5
6
char* someVar;

or

std::string anotherVar;


I was just wondering what the difference is between them (aside from the types), and why you'd want to use a char* over std::string or vice versa.

Thanks
A char* is basically a pointer to a character. What C does is frequently makes this pointer point to the first character in an array.

An std::string is a class that is much like a vector. Internally, it handles the storage of an array of characters, and gives the user several member functions to manipulate said stored array as well as several overloaded operators.

Reasons to use a char* over an std::string:
C backwards-compatibility.
Performance (potentially).
char*s have lower-level access.

Reasons to use an std::string over a char*:
Much more intuitive to use.
Better searching, replacement, and manipulation functions.
Reduced risk of segmentation faults.
char*s have lower-level access.

That's my take on it, anyway.

-Albatross
Last edited on
char* is a pointer. That is, it points to data that exists somewhere else, but doesn't actually contain the string.

std::string actually contains the string. It holds an internal buffer, manages the memory, etc, etc.


char* must be used in conjuction with either a char array, or with a dynamically allocated char array. After all, a pointer is worthless unless it actually points to something. This is mainly used in C programs:

1
2
3
4
5
6
7
8
char somebuffer[100] = "a string";
char* ptr = somebuffer;  // ptr now points to somebuffer

cout << ptr; // prints "a string"

somebuffer[0] = 'b';  // change somebuffer

cout << ptr;  // prints "b string" 


notice that when you change 'somebuffer', 'ptr' also changes. This is because somebuffer is the actual string in this case. ptr just points/refers to it.

With std::string it's less weird:

1
2
3
4
5
6
7
8
std::string a = "a string";
std::string b = a;

cout << b;  // prints "a string"

a[0] = 'b';  // change 'a'

cout << b;  // prints "a string" (not "b string") 


Here, you can see that changing 'a' does not affect 'b', because 'b' is the actual string.




But really, the major difference is that with char arrays, you are responsible for managing the memory, whereas std::string does it for you. In C++, there are very few reasons to use char arrays over strings. 99 times out of 100 you're better off with a string.

Until you fully understand memory management and pointers, just save yourself some headaches and use std::string.
Ahh, thanks guys for the replies.

Although, im still wondering if a bit of code I have written for a console-based game is prone to be faulty at some point. Its a (very) basic error messaging system to be used with try/catch blocks. Heres the code, as well as an example of its usage:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
// errorTranslations.h

#pragma once
#define NAME_TOO_LONG "The name you choose is too long. " // 1
#define NAME_CONTAINS_SPACE "Spaces are not permitted in names. " // 2
#define SET_ERROR "An invalid parameter was passed with 'set'. " // 3
#define SET_COLOR_ERROR "No valid color specified. " // 4
#define HELP_CAT_ERROR "No catagory found matching your request. " // 5

// Handle the Undefined/Unexpected errors
#define UNKNOWN_ERROR "An unexpected error occured during runtime. " // 404
#define FATAL_ERROR "Fatal Error occured:  " // 405 : for use in an exception& catch block
#define VECTOR_RANGE_ERROR "Error attempting to set a variable: " // 406

void error(char* error, HANDLE winConsole)
	{ 
	    SetConsoleTextAttribute(winConsole,0xCF);
		cout << "    " <<  error << "    " << endl;
            SetConsoleTextAttribute(winConsole,0x0A);
	}
char* getError(int erno)
	{
	switch(erno)
		{
		case 1:
			return NAME_TOO_LONG;
			break;
		case 2:
			return NAME_CONTAINS_SPACE;
			break;
		case 3:
			return SET_ERROR;
			break;
		case 4:
			return SET_COLOR_ERROR;
			break;
		case 5:
			return HELP_CAT_ERROR;
			break;
		
		default:
			return UNKNOWN_ERROR ;
			break;
		}
	return UNKNOWN_ERROR;
	}
// example useage
// header code here
int main()
{
	std::string name;
	cout << "Enter a name for your player: ";
	getline(cin,name);
	if(name.length() > 20)
	{
		error(getError(1),output_handle);
	}
}


Is using char* 's in the above code sample a good way to do that? Or is using std::strings' safer / smarter / more efficient?

T3ch
Last edited on
using char* shouldn't work. You would need const char*. As a general rule of const correctness, when passing to/from functions, if you're not changing the string data, use const char*. char* should only be if you're modifying it.

And actually, this is one of the better uses of char pointers. If you're talking about strict constant strings and not strings that will be modified at runtime, then you're better off with const char* due to lower overhead.

So you're doing it almost right.

I would recommend the following changes:

1) get rid of the macros. They're evil
2) since the strings are only used in getError, put them in get error. Don't make them global.
3) don't use magic numbers. If you want an identifier for an error code, use that instead
4) no need for a switch for something like this, use a lookup table instead. (possibly -- this point is debatable)


Here's what I'd change that to:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// if you don't already have an arraysize function:
template <typename T,unsigned S> inline unsigned arraysize(const T (&v)[S]) { return S; }

namespace Error  // to avoid name conflicts and to avoid having to say 'Error' a whole bunch
{
  enum
  {
    NameTooLong    = 1,     // = 1 if you want error codes to start at 1
    NameHasSpace,
    Set,
    SetColor,
    HelpCategory
  };

  const char* getString(int errcode)
  {
    static const char* errstrings[] = {
      "The name you chose is too long.",
      "Spaces are not permitted in names.",
      "An invalid parameter was passed with 'set'.",
      "No valid color specified.",
      "No category found matching your request."
    }

    if(errcode >= 1 && errcode <= arraysize(errstrings))
        return errstrings[errcode - 1];

    return "Unknown error.";
  }

  void print(const char* error, HANDLE winConsole)
  { 
    SetConsoleTextAttribute(winConsole,0xCF);
    cout << "    " <<  error << "    " << endl;
    SetConsoleTextAttribute(winConsole,0x0A);
  }

  inline void print(int errcode,HANDLE winConsole)  // a handy overload to avoid an extra step
  {
      print(getString(errcode,winConsole);
  }

}


// usage

int main()
{
  std::string name;
  cout << "Enter a name for your player: ";
  getline(cin,name);
  if(name.length() > 20)
  {
    Error::print(Error::NameTooLong,output_handle);
  }
}



I put the error stuff in an Error namespace. This might seem like more work because you have to prefix everything with Error::, but it really isn't any more work because this way you can omit the 'Error' from all your identifiers.

For example instead of SetColorError you can just have SetColor because it will be Error::SetColor. Don't have to have printError because it will be Error::print, etc. So it's the same idea.
Last edited on
Ah, very nice example. I'm still a bit confused as to the template part, but im new to templates and have a lot of reading to do on them. Otherwise its a much more long term solution then the one I had. And in someways simpler. Thank you all for the replies so far, very helpful.

~T3ch

EDIT:

I've experimented with enumerated lists before, but never could get them to work exactly how i needed them to. And I never knew you could use them like that within a namespace. I always thought they had to be named. Learning something new all the time.
Last edited on
Don't worry about understanding that template thing. It's just some magic to return the size of a fixed-size array.
Magic indeed. Have to keep it in mind when ever I use arrays. I've been using vectors more then arrays as of late. I've found that that at() is invaluable when you don't know the size of a vector at the start of execution and want to prevent assertion failures or something from crashing your program. But you don't need a vector all the time so having a function to return the size of an array is rather nice as well.
Really I only use arrays for lookup tables / constants. I tend to use vectors for variable storage.

With the rare exception of having something that is truely fixed-length, in which case I would use an array.
Topic archived. No new replies allowed.