Code
How Does a Computer Know Where a String Ends?
How fixed lengths, terminators, length prefixes, and HTTP chunks define data boundaries
Solving Encode and Decode Strings made me wonder where the idea of putting a length before a string came from. Combining a list of strings into one value and restoring it later requires a rule for preserving each boundary. The same problem appears in language-level string representations and network protocol framing.
Terminators, explicit lengths, and HTTP chunks all tell a receiver where data ends, but they solve the problem at different levels.
Three ways to mark the end of data
There are three common ways to define boundaries in a continuous sequence of data.
The first is a fixed length. If every value is exactly 10 bytes, the receiver can split the input every 10 bytes. The rule is simple, but short values waste space, and longer values cannot fit.
The second is a terminator or delimiter.
hello\0world\0
The receiver reads until it reaches the agreed marker. This avoids storing a separate length, but the marker must either be forbidden inside the payload or handled through an escaping rule.
The third is a length prefix.
5:hello5:world
The decoder finds the delimiter, parses the preceding number, and then reads exactly that many bytes or characters. The colon marks the end of the length field, not the end of the payload. A colon or null character inside the payload therefore does not break the boundary.
None of these methods replaces the others in every case. The choice depends on whether values have a fixed size, which values the payload may contain, and whether the total length is known before transmission begins.
C strings look for a terminator
C does not define strings as a separate built-in data type. It represents them as character arrays terminated by a null character. Dennis Ritchie’s account of C’s development also describes C strings as arrays combined with an end marker.
['h', 'e', 'l', 'l', 'o', '\0']
This representation keeps the language and compiler rules simple because strings use the existing array model. Without a separately stored length, however, finding the string length requires scanning until the null character appears. A null character inside the data is treated as the end by C string functions.
Ritchie also noted the cost of that decision. Some operations must scan for the end, and more storage-management responsibility remains with the program. Null termination is a trade-off that preserves a simple array model.
TCP requires applications to define message boundaries
Storing one string in memory and sending multiple messages over a network are different problems, but the receiver still needs to know where one value ends.
TCP provides a byte stream and does not preserve application message boundaries. Two writes from the sender are not guaranteed to arrive as the same two chunks, so the application protocol must define its own framing.
RFC 6587, which describes syslog transport over TCP, shows two approaches side by side. Octet counting sends the message length first, while non-transparent framing appends a terminator such as LF or null.
23 <23>message payload...
A length-prefixed receiver can allow any character inside the payload. A terminator-based receiver can split one message into several by mistake when an unescaped terminator appears in the content. This is the same delimiter collision that appears in Encode and Decode Strings.
HTTP separates total length from chunk length
RFC 1945 for HTTP/1.0 explains that Content-Length carries the full body size when it is known. Otherwise, a response can use connection closure to indicate the end.
Content-Length: 3495
Closing the connection makes it harder to reuse that connection after one response. A dynamically generated response might also begin transmission before its final size is known.
HTTP/1.1 introduced chunked transfer coding for that situation. RFC 2068, the first HTTP/1.1 specification, was published in January 1997 and described dynamically generated content as a sequence of chunks, each preceded by its size. RFC 2616 revised the HTTP/1.1 specification in 1999 rather than introducing chunking for the first time.
In simplified form, the current RFC 9112 structure looks like this:
[hexadecimal chunk size]\r\n
[chunk data]\r\n
...
0\r\n
\r\n
Each chunk carries its own size, and a zero-sized chunk marks the end of the transfer. The sender can transmit completed pieces without knowing the total body size in advance and without immediately closing the connection.
The length in Encode and Decode Strings marks the end of one string. An HTTP chunk size instead describes one transport piece of a single response body. If a 1,000-byte HTML document is sent as a 400-byte chunk followed by a 600-byte chunk, the receiver joins them and uses the original 1,000-byte document. Where those chunks were divided has no meaning inside the HTML itself.
I started looking into this out of curiosity, but it clarified why similar-looking length fields can serve different purposes. I want to keep following questions like this when an algorithm problem overlaps with a system I use in practice.