Por mais que eu goste de C e C ++, não posso deixar de coçar a cabeça com a escolha de cadeias terminadas nulas:
- As cadeias de comprimento prefixadas (ie Pascal) existiam antes de C
- As seqüências de caracteres com prefixo de comprimento tornam vários algoritmos mais rápidos, permitindo uma pesquisa constante de duração.
- Seqüências de caracteres com prefixo de comprimento tornam mais difícil causar erros de saturação de buffer.
- Mesmo em uma máquina de 32 bits, se você permitir que a string tenha o tamanho da memória disponível, uma string prefixada de comprimento será apenas três bytes mais larga que uma string terminada nula. Em máquinas de 16 bits, esse é um byte único. Em máquinas de 64 bits, 4 GB é um limite razoável de tamanho de string, mas mesmo que você queira expandi-lo para o tamanho da palavra-máquina, as máquinas de 64 bits geralmente têm memória suficiente, tornando os sete bytes extras como um argumento nulo. Eu sei que o padrão C original foi escrito para máquinas insanamente pobres (em termos de memória), mas o argumento da eficiência não me vende aqui.
- Praticamente todas as outras linguagens (por exemplo, Perl, Pascal, Python, Java, C # etc.) usam seqüências de caracteres com prefixo de comprimento. Essas linguagens geralmente superam C em benchmarks de manipulação de strings porque são mais eficientes com strings.
- O C ++ corrigiu isso um pouco com o
std::basic_string
modelo, mas matrizes de caracteres simples que esperam seqüências terminadas nulas ainda são difundidas. Isso também é imperfeito, pois requer alocação de heap. - Seqüências terminadas nulas precisam reservar um caractere (nulo), que não pode existir na seqüência, enquanto que as seqüências prefixadas de comprimento podem conter nulos incorporados.
Várias dessas coisas vieram à tona mais recentemente que C, portanto, faria sentido que C não as conhecesse. No entanto, vários foram bem antes de C surgir. Por que seqüências terminadas nulas foram escolhidas em vez do prefixo obviamente de comprimento superior?
EDIT : Como alguns pediram fatos (e não gostaram dos que eu já forneci) no meu ponto de eficiência acima, eles resultam de algumas coisas:
- Concat usando cadeias terminadas nulas requer complexidade de tempo O (n + m). A prefixação de comprimento geralmente requer apenas O (m).
- O comprimento usando cadeias terminadas nulas requer complexidade de tempo O (n). A prefixação do comprimento é O (1).
- Length e concat são de longe as operações mais comuns de strings. Existem vários casos em que seqüências terminadas nulas podem ser mais eficientes, mas ocorrem com muito menos frequência.
Nas respostas abaixo, alguns casos em que seqüências terminadas nulas são mais eficientes:
- Quando você precisar interromper o início de uma string e passar para algum método. Você não pode realmente fazer isso em tempo constante com o prefixo do comprimento, mesmo que tenha permissão para destruir a cadeia original, porque o prefixo do comprimento provavelmente precisa seguir as regras de alinhamento.
- Em alguns casos em que você está repetindo a cadeia de caracteres caractere por caractere, poderá salvar um registro da CPU. Observe que isso funciona apenas no caso de você não ter alocado dinamicamente a string (porque você precisaria liberá-la, sendo necessário usar o registro da CPU que você salvou para manter o ponteiro que você recebeu originalmente de malloc e amigos).
Nenhuma das opções acima é quase tão comum quanto o comprimento e a concat.
Há mais uma afirmação nas respostas abaixo:
- Você precisa cortar o final da corda
mas este está incorreto - é a mesma quantidade de tempo para cadeias terminadas com comprimento nulo e com prefixo. (Seqüências terminadas nulas apenas mantêm um nulo onde você deseja que o novo final esteja, os prefixos de comprimento apenas subtraem do prefixo.)