Concepts / C-Style String Representation

C-Style String Representation

Choose the access style by intent. Use at() when a position may be invalid and a range check is wanted. Use iterators or the [ ] operator when speed matters and the program is already sure its positions are valid.

  • Programming

A Position That Does Not Exist

Suppose a program holds the three-character string cat and is asked for the character at position 3. What should happen? Before reading any further, decide what you think the answer is. The position is simply a number, and nothing about the number itself tells you whether a character lives there. This article is about that gap between a requested position and a real character, and about how the way you access a string decides what the program does when the request is invalid.

The source material states the core idea directly: positions in a string run over a fixed range, and a position outside that range does not refer to a character in the string. The rest of the article follows from that one sentence. Once you know the valid range, you can ask whether a given request falls inside it, and you can choose an access method that matches how much you need to be sure.

Valid Positions in a String

A string is an ordered collection of characters, and each character can be reached by its position. Positions are counted from zero, so a string with a given number of characters has valid positions running from the first character up to the last one. For a string of length n, the valid positions are 0 through n minus 1. Any position outside that run does not refer to a character in the string at all.

position 0cposition 1aposition 2tposition 3no characternextnextend of string
Which positions actually hold characters in this string, and which index values fall outside them?

Read the diagram from left to right. Positions 0, 1, and 2 each hold one character of cat. Position 3 is marked as a request that lands outside the string. The count of characters tells you where the valid run ends, so a position one past the last character is never a character. Notice that the string's length, not the largest number you happen to know, determines the boundary.

Definition

Valid position: a position at or after the first character and at or before the last character of the string. Any other position is outside the string and does not refer to a character in it.

Tracing an Index Past the End

Now trace the state of the same string step by step. At each valid position the program can read a character, and that is the normal case. Then ask what a request for position 3 would find. Position 3 is not one of the three character positions, so the request does not land on a character. Whether the program notices this depends on which access method it uses, and that is the decision the rest of this article turns on.

character 0ccharacter 1acharacter 2tindex 3not a character of thestringunchecked request
When an index past the end is read, what is reached, and what lies beyond the string's characters?

The unchecked request in the diagram reaches past the last character. The source does not describe what memory or value that position would produce, and a learner should not assume one. What the source does establish is the intent: a position outside the valid run does not refer to a character, so using it as if it did is a logic error even when the program keeps running.

Worked example

Deciding whether a requested position is valid

A string holds the word cat, which has 3 characters. A program requests the character at position 2, then the character at position 3. Which request is valid?

  1. 1
    Find the length

    The string has 3 characters, so its length is 3.

  2. 2
    Find the last valid position

    Positions start at 0, so the last valid position is length minus 1, which is 2.

  3. 3
    Check position 2

    2 lies within 0 through 2, so it refers to the character t. The request is valid.

  4. 4
    Check position 3

    3 is greater than 2, so it lies outside the valid run. It does not refer to a character in the string.

Result

The request for position 2 is valid and returns t. The request for position 3 is invalid, and it is the case that needs a range check.

Choosing Between at() and [ ]

The source identifies at() as the access method to use when the program wants range checking. Range checking means verifying that a position is valid before the element is used. The source also gives the opposite advice for speed: when the goal is to optimize speed, iterators and the [ ] operator are the recommended choices rather than at(). Put simply, at() trades some speed for the safety of a range check, while iterators and [ ] are the faster options when the program is already sure its positions are valid.

at()range checkedcheck before accessthe purpose of at()[ ] operatoruncheckedspeed firstused when validity isalready establishedguaranteesfavors
For the same out-of-range index, how do at() and [ ] differ in intent and in what they guarantee?

The source does not describe the exact error behavior when at() detects an invalid position, so do not memorize a specific error message as part of this topic. The lesson to take from it is the intent. at() is the choice when an out-of-range request must be caught rather than silently attempted. The [ ] operator does not promise that check, so it belongs in code where the program has already confirmed that the position is valid.

cpp
Output
a
a
t
Notes on the code
  • word.at(1) asks for the range-checked form of the access at a valid position.
  • word[1] reads the same character, because the position is valid.
  • The last valid position for cat is 2, which is length minus 1.

The code above shows the two access styles on the same string so the difference in intent is visible. Both lines read the same character because the position is valid. The difference matters only when a position is not valid, which is the situation the earlier trace examined. A program that might ask for position 3 should use the checked form, while a loop whose bounds were already proven against the length can use the faster form.

Mistakes With Unchecked String Access

The mistakes below all share one root cause: the program assumes a position is valid without establishing that it is. Each one is easiest to avoid when you ask the validity question before the access happens.

  • Common mistake

    Looping up to the length instead of the last valid position

    What it looks like
    for (size_t i = 0; i <= word.size(); ++i) reads word[i] for every i in the loop, including the final pass.
    Why it fails

    The valid positions run from 0 through length minus 1. The final pass asks for the position equal to the length, which lies outside the string.

    Do this instead

    Use a loop that stops before the length, such as i < word.size(), so every position read is one of the valid positions.

  • Common mistake

    Indexing at the length of the string

    What it looks like

    For cat, word[3] is requested as if a fourth character existed.

    Why it fails

    The string has three characters, so position 3 does not refer to a character in it.

    Do this instead

    Compute the valid run from the length first, or use at() when the position comes from outside the program's control.

  • Common mistake

    Assuming the speed-oriented form also checks the range

    What it looks like

    A program uses word[i] with an index read from user input and never compares it to the length.

    Why it fails

    The source identifies at() as the range-checking tool. The [ ] operator is the speed choice and is recommended when validity is already established.

    Do this instead

    Choose at() when the position is not guaranteed valid, and reserve [ ] for positions the program has already verified.

Practice Tracing Valid and Invalid Requests

EASY

A string holds the word dog. List every valid position, then say which of the requests for positions 0, 2, 3, and 5 would fall outside the string. For each invalid request, state whether at() or [ ] is the better choice if the program cannot guarantee the position is valid.

Hints
  • Count the characters in dog first, then the last valid position is that count minus 1.
  • Positions outside the range 0 through 2 do not refer to a character in the string.
  • The source ties at() to range checking and the [ ] operator to speed when validity is already established.
MEDIUM

A loop reads every character of a string named name using name[i] for i from 0 up to and including the length. Explain which index causes the problem, what that index refers to, and how you would change the loop so it stays within the valid positions.

Hints
  • Ask which index is the first one past the last valid character.
  • Changing the upper bound from less-than-or-equal to less-than removes that index.

Key Takeaways for Safe Access

Key point

Choose the access style by intent. Use at() when a position may be invalid and a range check is wanted. Use iterators or the [ ] operator when speed matters and the program is already sure its positions are valid.

At a glance

  • A string's valid positions run from 0 through its length minus 1, and any other position does not refer to a character in the string.
  • Requesting a position outside that run is a logic error, and the program must decide what to do about it rather than assume the position is valid.
  • at() is the access method the source associates with range checking, so it is the choice when an out-of-range request must be caught.
  • Iterators and the [ ] operator are the source's recommended choices when the goal is speed, and they fit positions the program has already verified.
  • Most unchecked-access mistakes come from looping to the length or assuming validity without checking, so ask the validity question before every access.

More concepts starting with C