Characters and C-Style Strings

📚 Gaddis (Ch. 10)

ASCII Encoding

Internally, all characters must be encoded into a numeric representation.

There is more than one way to do it… But C++ (by default) type char uses the ASCII encoding scheme.

Testing Characters

The following functions require the <cctype> header.

Character testing functions.

Case Conversion

Also require the <cctype> header.

Functions:

  • toupper(char c): if c is a lowercase letter, return uppercase equivalent; otherwise, return c unchanged.
  • tolower(char c): if c is an uppercase letter, return lowercase equivalent; otherwise, return c unchanged.

Case Conversion Example

char ch1 = 'H';
char ch2 = 'e';
char ch3 = '!';

cout << static_cast<char>(toupper(ch1)); //->'H’
cout << static_cast<char>(toupper(ch2)); //->'E’
cout << static_cast<char>(toupper(ch3)); //->'!'

cout << static_cast<char>(tolower(ch1)); //->'h’
cout << static_cast<char>(tolower(ch2)); //->'e’
cout << static_cast<char>(tolower(ch3)); //->'!'

C-Style Strings

C++ provides support for a low-level representation of strings of characters as they were defined in the C language: as null-terminated arrays of type char. We refer to strings of this type as C-Style strings, or simply C-Strings.

  • String literals such as "SMITH" are internally represented as c-strings by default.
  • Internally, they look like the following:

“SMITH” as seen in memory.

C-String Variables

Variables that store c-strings are just arrays of type char:

char name[6] = "SMITH";

Produces:


“SMITH” as seen in a C-style string.


  • The trailing '\0' is added automatically during the initialization.

IMPORTANT: The assignment operator only works with c-strings in an initialization statement! You cannot assign c-strings using =!

C-String Input

  • You can enter c-strings using >> (stream extraction).
    • Input is whitespace-terminated.
    • No bounds-checking is performed!
  • For input containing whitespace, and to control the amount of input, use cin.getline():

Syntax:
std::cin.getline(c_str_name,max_size)

const int SIZE = 64;
char      name[SIZE];
std::cout << "Enter your name: ";
std::cin.getline(name, SIZE);

What C-Strings Cannot Do

  • You cannot directly assign c-strings.
    • Why? They are arrays!
  • You cannot directly compare c-strings.
    • Why? They are arrays!

C-String Functions

The <cstring> header file contains functions that are useful for dealing with c-style strings:

    Function                    Description 
    ------------------------------------------------------------
    strlen(str)                 Returns length of `str`

    strcmp(str1, str2)          Compares `str1` to `str2` 
                                "alphabetically"

    strcpy(dst, src)            Copies `src` into `dst` 
                                (no bounds check) 

    strncpy(dst, src, count)    Copies up to `count` 
                                characters from `src` into 
                                `dst` (no null-terminator added)

    strcat(dst, src)            Concatenates `src` to the end 
                                of `dst` (no bounds check)  

    strncat(dst, src, count)    Concatenates up to `count` 
                                characters from `src` to end 
                                of `dst`, plus the 
                                null-terminator  

Example: strlen()

char dessert1[] = "Cheesecake";
char dessert2[] = "Baked Alaska";

// `strlen()` returns the _logical_ size of the 
// c-string (not counting the terminating '\0').
std::cout << strlen(dessert1) << "\n"; // prints "10"

// Make a column wide enough for the dessert with
// the longest name, plus 2 spaces for padding:
int width = strlen(dessert1);
if(strlen(dessert2) > width){
    width = strlen(dessert2);
}
width += 2;

// print dessert menu:
std::cout << std::setw(width) << dessert1 << "$ 9.99\n";
std::cout << std::setw(width) << dessert2 << "$12.50\n";

Example: strcmp()

char dessert1[] = "Cheesecake";
char dessert2[] = "Baked Alaska";

// Print the two desserts out in alphabetical order:
if( strcmp(dessert1, dessert2) < 0 ){ // dessert1 first?
    std::cout << dessert1 << "\n" << dessert2 << "\n";
}
else{ // dessert2 is first alphabetically...
    std::cout << dessert2 << "\n" << dessert1 << "\n";
}

Example 2: strcmp()

const int PWDMAX = 512;
char  password[PWDMAX];
char  password_confirm[PWDMAX];

// Get new password (twice, to confirm):
std::cout << "Enter new password: ";
std::cin.getline(password, PWDMAX);
std::cout << "Enter password again: ";
std::cin.getline(password_confirm, PWDMAX);

// Make sure passwords match:
if( strcmp(password, password_confirm) != 0 ){
    std::cout << "Error: Passwords did not match.\n";
    exit(1); // If they didn't exit with an error code.
}
else{ // Passwords matched!  OK...
    std::cout << "Password changed.\n";
}

Example: strcpy()

// this code was left intentionally blank





// because you should (almost) never use
// `strcpy()`...




// use `strncpy()` instead

Example: strncpy()

char longStr[]  = "Mighty Muddy Mississippi";
char shortStr[] = "Yo";

const int DESTMAX = 16;
char  destination[DESTMAX];

// DON'T do this:
strcpy(destination, longStr);      // BUFFER OVERFLOW!!!

// Do this instead:
strncpy(destination, longStr, DESTMAX);
destination[DESTMAX-1] = '\0';     // must terminate

std::cout << destination << "\n";  // what prints?

strncpy(destination, shortStr, DESTMAX);
destination[DESTMAX-1] = '\0';

std::cout << destination << "\n";  // what prints?

Example: strcat()

// this code was left intentionally blank





// because you should (almost) never use
// `strcat()`...




// use `strncat()` instead

Example: strncat()

const int DESTMAX   = 16;
char destination[DESTMAX];
char adj1[]         = "Mighty ";
char adj2[]         = "Muddy ";
char river[]        = "Mississippi";

// Start the full description by copying over the first
// adjective:
destination[DESTMAX-1] = '\0'; // alt. method: null first!
strncpy(destination, adj1, DESTMAX-1); // notice -1 here
// Now concatenate together the rest of the river description...
// But be careful not to overflow!
// Calculate the remaining space:
int remaining = DESTMAX - 1 - strlen(destination); // why -1?
strncat(destination, adj2, remaining);
remaining -= strlen(adj2); // update remaining space
strncat(destination, river, remaining);

std::cout << destination << "\n";  // What prints?

Conversion Functions

Require <cstdlib>

    Function                   Description 
    ------------------------------------------------------------
    atoi(str)                   Converts c-string to an int
    atol(str)                   Converts c-string to a long
    atof(str)                   Converts c-string to a double

Example: atoi(), atof()

const  int BUFMAX = 256;
char   buffer[BUFMAX];
int    quantity;
double price;

std::cout << "Enter quantity sold: ";
std::cin.getline(buffer, BUFMAX);
// Extract an integer value from buffer into quantity:
quantity = atoi(buffer);

std::cout << "Enter price of item: ";
std::cin.getline(buffer, BUFMAX);
// Extract the floating-point (double) price:
price = atof(buffer);

std::cout << "Sold "    << quantity << " items at $"
          << std::fixed << std::setprecision(2)
          << price      << " each.\n";

Characters and C-Style Strings