As strings C são sempre terminadas com nulo ou dependem da plataforma?

13

No momento, estou trabalhando com sistemas embarcados e descobrindo maneiras de implementar seqüências de caracteres em um microprocessador sem sistema operacional. Até agora, o que estou fazendo é apenas usar a idéia de ter ponteiros de caracteres terminados em NULL e tratá-los como cadeias de caracteres em que o NULL significa o fim. Eu sei que isso é bastante comum, mas você pode sempre contar com esse para ser o caso?

A razão pela qual pergunto é que estava pensando em usar um sistema operacional em tempo real em algum momento e gostaria de reutilizar o máximo possível meu código atual. Então, para as várias opções disponíveis, posso esperar que as strings funcionem da mesma forma?

Deixe-me ser mais específico, porém, para o meu caso. Estou implementando um sistema que aceita e processa comandos por uma porta serial. Posso manter meu código de processamento de comando igual e, em seguida, esperar que os objetos de seqüência de caracteres criados no RTOS (que contém os comandos) sejam todos terminados em NULL? Ou seria diferente com base no sistema operacional?

Atualizar

Depois de ser aconselhado a dar uma olhada nesta pergunta , concluí que ela não responde exatamente o que estou perguntando. A pergunta em si está perguntando se o comprimento de uma string deve sempre ser passado, o que é totalmente diferente do que estou perguntando, e embora algumas das respostas tenham informações úteis, elas não são exatamente o que estou procurando. As respostas pareciam dar razões pelas quais ou por que não terminar uma sequência com um caractere nulo. A diferença com o que estou perguntando é se posso mais ou menos esperar que as strings nativas de plataformas diferentes terminem suas próprias strings com nulo, sem ter que sair e tentar todas as plataformas existentes, se isso fizer sentido.

Snoop
fonte
3
Não uso C há muito tempo, mas não consigo pensar em um momento em que deparei com uma implementação que não usava seqüências terminadas em NULL. É parte do padrão C, se bem me lembro (como eu disse, ele tem sido um tempo ...)
MetalMikester
1
Eu não sou um especialista em C, mas, tanto quanto sei, todas as seqüências de caracteres em C são matrizes de caracteres com terminação nula. Você pode criar seu próprio tipo de string, mas precisará implementar todas as funções de manipulação de strings por conta própria.
Machado
1
@MetalMikester Você acha que essa informação pode ser encontrada na especificação C padrão?
Snoop
3
@ Snoopy Muito provavelmente sim. Mas na verdade, quando se fala de strings em C, eles são apenas uma matriz de caracteres que termina com NULL e é isso, a menos que você use algum tipo de biblioteca de strings não padrão, mas não é disso que estamos falando aqui. Duvido que você encontre uma plataforma que não respeite isso, especialmente com um dos pontos fortes de C: a portabilidade.
precisa saber é o seguinte

Respostas:

42

As coisas chamadas "strings C" serão terminadas em nulo em qualquer plataforma. É assim que as funções padrão da biblioteca C determinam o final de uma string.

Na linguagem C, não há nada que o impeça de ter uma matriz de caracteres que não termina em um nulo. No entanto, você terá que usar outro método para evitar o final de uma string.

Simon B
fonte
4
apenas para adicionar; geralmente você tem um lugar inteiro para acompanhar o comprimento da corda e depois você acaba com uma estrutura de dados personalizado para fazê-lo direito, algo como a classe QString em Qt
Rudolf Olah
8
Caso em questão: trabalho com um programa C que usa pelo menos cinco formatos de string diferentes: charmatrizes terminadas em nulo , charmatrizes com o comprimento codificado no primeiro byte (comumente conhecido como "strings Pascal"), wchar_tversões baseadas em ambos os acima e charmatrizes que combinam os dois métodos: comprimento codificado no primeiro byte e um caractere nulo que termina a cadeia.
Mark
4
@ Mark Interfaces com vários componentes / aplicativos de terceiros ou uma bagunça de código legado?
Dan is Fiddling por Firelight
2
@ DanNeely, tudo isso acima. Cordas Pascal para interface com o MacOS clássico, cordas C para uso interno e Windows, cordas largas para adicionar suporte a Unicode e cordas bastardas porque alguém tentou ser inteligente e criar uma string que pudesse interagir com o MacOS e o Windows ao mesmo tempo.
Mark
1
@ Mark ... e, é claro, ninguém está disposto a gastar dinheiro para pagar a dívida técnica, porque o MacOS clássico está morto há muito tempo, e as seqüências bastardas são um clusterfrak duplo sempre que precisam ser tocadas. Minhas condolências.
Dan is Fiddling por Firelight
22

A determinação do caractere final depende do compilador para literais e da implementação da biblioteca padrão para cadeias em geral. Não é determinado pelo sistema operacional.

A convenção de NULrescisão remonta ao C pré-padrão e, em mais de 30 anos, não posso dizer que me deparei com um ambiente que faz qualquer outra coisa. Esse comportamento foi codificado em C89 e continua a fazer parte do padrão da linguagem C (o link está para um rascunho de C99):

  • A Seção 6.4.5 define o estágio para NULcadeias terminadas exigindo que um NULseja anexado a literais de cadeias.
  • A Seção 7.1.1 traz isso para as funções da biblioteca padrão, definindo uma sequência como "uma sequência contígua de caracteres terminados por e incluindo o primeiro caractere nulo".

Não há razão para que alguém não possa escrever funções que lidam com seqüências terminadas por algum outro caractere, mas também não há razão para reverter o padrão estabelecido na maioria dos casos, a menos que seu objetivo seja adequar os programadores. :-)

Blrfl
fonte
2
Uma razão seria evitar ter que encontrar o fim da mesma string repetidamente.
Paŭlo Ebermann 21/03
@ PaŭloEbermann Certo. À custa de ter que passar dois valores em vez de um. O que é um pouco cansativo se você apenas passar uma string literal como em printf("string: \"%s\"\n", "my cool string"). A única maneira de contornar quatro parâmetros nesse caso (além de algum tipo de byte de terminação) seria definir uma string como algo std::stringem C ++, que tem seus próprios problemas e limitações.
cmaster - restabelece monica 22/03
1
A Seção 6.4.5 não exige que uma literal de cadeia seja encerrada com um caractere nulo. Ele explicitamente notas " corda Um personagem necessidade literal não ser uma string (ver 7.1.1), porque um caractere nulo pode ser embutido nele por uma seqüência \ 0 fuga. "
bzeaman
1
@bzeaman A nota de rodapé diz que você pode construir uma string literal que não atenda à definição de 7.1.1 da string, mas a frase referente a ela diz que compiladores compatíveis os NULterminam, não importa o que: "Na fase de tradução 7, um byte ou código do valor zero é anexado a cada sequência de caracteres multibyte que resulta de uma string literal ou literals ". As funções da biblioteca que usam a definição do 7.1.1 são interrompidas no momento em NULque encontram e não sabem nem se importam com a existência de caracteres adicionais além dela.
Blrfl
Eu estou corrigido. Procurei vários termos como 'nulo', mas perdi o 6.4.5.5 mencionando o 'valor zero'.
bzeaman
3

Estou trabalhando com sistemas embarcados ... sem sistema operacional ... estou ... usando a idéia de ter ponteiros de caracteres terminados com NULL e tratando-os como cadeias de caracteres em que o NULL significa o fim. Eu sei que isso é bastante comum, mas você pode sempre contar com esse para ser o caso?

Não há tipo de dados de string na linguagem C, mas existem literais de string .

Se você colocar uma string literal em seu programa, ela normalmente será encerrada em NUL (mas veja o caso especial, discutido nos comentários abaixo.) Ou seja, se você colocar "foobar"em um local onde const char *se espera um valor, o compilador emitirá foobar⊘para o segmento const / code / seção do seu programa e o valor da expressão será um ponteiro para o endereço em que ele armazenou o fcaractere. (Nota: estou usando para significar o byte NUL.)

O único outro sentido em que a linguagem C possui seqüências é que ela possui algumas rotinas de biblioteca padrão que operam em seqüências de caracteres terminadas em NUL. Essas rotinas de biblioteca não existirão em um ambiente bare metal, a menos que você as porte.

Eles são apenas códigos - não diferentes do código que você escreve. Se você não quebrá-los quando os portar, eles farão o que sempre fazem (por exemplo, parem em um NUL).

Salomão Lento
fonte
2
Re: "Se você colocar uma string literal no seu programa, ela será sempre NUL terminada": Você tem certeza disso? Tenho certeza de que (por exemplo) char foo[4] = "abcd";é uma maneira válida de criar uma matriz de quatro caracteres com terminação não nula.
Ruakh 21/03
2
@ruakh, Opa! esse é um caso que eu não considerei. Eu estava pensando em uma string literal que aparece em um lugar onde uma char const * expressão é esperada. Eu esqueci que os inicializadores C às vezes podem obedecer a regras diferentes.
Solomon Slow
@ruakh A string literal é terminada em NUL. A matriz não é.
jamesdlin
2
@ruakh você tem um char[4]. Isso não é uma string, mas foi inicializado a partir de uma
Caleth 22/03
2
@Caleth, "inicializado de um" não é algo que deve acontecer em tempo de execução. Se adicionarmos a palavra static- chave ao exemplo de Ruakh, o compilador poderá emitir um "abcd" não NUL terminado para um segmento de dados inicializado, para que a variável seja inicializada pelo carregador do programa. Portanto, Ruakh estava certo: há pelo menos um caso em que a aparência de uma string literal em um programa não requer que o compilador emita uma string terminada em NUL. (ps, na verdade, compilei o exemplo com o gcc 5.4.0 e o compilador não emitiu o NUL.)
Solomon Slow
2

Como outros já mencionaram, o encerramento nulo de seqüências de caracteres é uma convenção da C Standard Library. Você pode manipular as strings da maneira que desejar, se não for usar a biblioteca padrão.

Isso vale para qualquer sistema operacional com um compilador 'C' e, também, você pode escrever programas 'C' que não são executados em um sistema operacional verdadeiro, como você mencionou na sua pergunta. Um exemplo seria o controlador de uma impressora a jato de tinta que eu projetei uma vez. Em sistemas incorporados, a sobrecarga de memória de um sistema operacional pode não ser necessária.

Em situações de falta de memória, eu examinaria as características do meu compilador em relação ao conjunto de instruções do processador, por exemplo. Em um aplicativo em que as strings são processadas muito, pode ser desejável usar descritores como comprimento da string. Estou pensando em um caso em que a CPU é particularmente eficiente em trabalhar com desvios curtos e / ou desvios relativos com registros de endereço.

Então, o que é mais importante no seu aplicativo: tamanho e eficiência do código ou compatibilidade com um sistema operacional ou biblioteca? Outra consideração pode ser a manutenção. Quanto mais você se afastar da convenção, mais difícil será para alguém manter.

Hugh Buntu
fonte
1

Outros abordaram a questão de que, em C, as strings são basicamente o que você faz delas. Mas parece haver alguma confusão em sua pergunta no próprio terminador e, de uma perspectiva, pode ser com isso que alguém em sua posição está preocupado.

As strings C são terminadas em nulo. Ou seja, eles são finalizados pelo caractere nulo NUL,. Eles não são finalizados pelo ponteiro nulo NULL, que é um tipo de valor completamente diferente, com uma finalidade completamente diferente.

NULé garantido que o valor inteiro seja zero. Dentro da string, ele também terá o tamanho do tipo de caractere subjacente, que geralmente será 1.

NULLnão é garantido que tenha um tipo inteiro. NULLdestina-se ao uso em um contexto de ponteiro e, geralmente, espera-se que ele tenha um tipo de ponteiro, que não deve ser convertido em um caractere ou número inteiro se o seu compilador for bom. Embora a definição de NULLenvolva o glifo 0, não é garantido que ele realmente tenha esse valor [1], e a menos que o seu compilador implemente a constante como um caractere #define(muitos não, porque muitos não devem , porque NULL realmente não devem ser significativos em um elemento não contexto do ponteiro), portanto, não é garantido que o código expandido envolva realmente um valor zero (mesmo que de forma confusa envolva um glifo zero).

Se NULLdigitado, também não será provável que tenha um tamanho de 1 (ou outro tamanho de caractere). Isso pode causar problemas adicionais, embora as constantes reais de caracteres também não tenham tamanho de caracteres.

Agora, a maioria das pessoas verá isso e pensará: "ponteiro nulo como qualquer coisa que não seja zero-bits? Que bobagem" - mas suposições como essa só são seguras em plataformas comuns como o x86. Como você mencionou explicitamente o interesse em segmentar outras plataformas, é necessário levar esse problema em consideração, pois separou explicitamente seu código de suposições sobre a natureza do relacionamento entre ponteiros e números inteiros.

Portanto, enquanto as strings C são terminadas em nulo, elas não são terminadas por NULL, mas por NUL(geralmente escritas '\0'). O código que usa explicitamente NULLcomo um terminador de strings funcionará em plataformas com uma estrutura de endereços direta e até compilará com muitos compiladores, mas não está absolutamente correto.


[1] o valor real do ponteiro nulo é inserido pelo compilador quando ele lê um 0 token em um contexto em que seria convertido em um tipo de ponteiro. Esta não é uma conversão do valor inteiro 0 e não é garantida a retenção se algo diferente do 0próprio token for usado, como um valor dinâmico de uma variável; a conversão também não é reversível e um ponteiro nulo não precisa gerar o valor 0 quando convertido em um número inteiro.

Leushenko
fonte
Ótimo ponto. Enviei uma edição para ajudar a esclarecer isso.
Monty mais duro
" NULé garantido que o valor inteiro seja zero." -> C não define NUL. Em vez C define que as cordas têm uma final chracter nulo , um byte com todos os bits em 0.
Chux - Restabeleça Monica
1

Eu tenho usado string em C, significa caracteres com terminação nula é chamado Strings.

Não haverá problemas quando você usar em baremetal ou em qualquer sistema operacional como Windows, Linux, RTOS: (FreeRTO, OSE).

No mundo incorporado, a terminação nula realmente ajuda mais a simbolizar o caractere como string.

Eu tenho usado seqüências de caracteres em C assim em muitos sistemas críticos de segurança.

Você pode estar se perguntando, o que é realmente uma string em C?

Seqüências de caracteres no estilo C, que são matrizes, também existem literais, como "this". Na realidade, esses dois tipos de cadeias de caracteres são apenas conjuntos de caracteres sentados um ao lado do outro na memória.

Sempre que você escreve uma cadeia de caracteres, entre aspas duplas, C cria automaticamente uma matriz de caracteres para nós, contendo essa cadeia, terminada pelo caractere \ 0.

Por exemplo, você pode declarar e definir uma matriz de caracteres e inicializá-la com uma constante de sequência:

char string[] = "Hello cruel world!";

Resposta direta: você realmente não precisa se preocupar com o uso de caracteres com terminação nula, este trabalho independente de qualquer plataforma.

danglingpointer
fonte
Obrigado, não sabia que, quando declarado com aspas duplas, a NULé automaticamente anexado.
Snoop
1

Como outros já disseram, a terminação nula é praticamente universal para o padrão C. Mas (como outros também apontaram) não 100%. Para outro exemplo, o sistema operacional VMS normalmente usava o que chamava de "descritores de string" http://h41379.www4.hpe.com/commercial/c/docs/5492p012.html acessado em C por #include <descrip.h >

As coisas no nível do aplicativo podem usar terminação nula ou não, no entanto, o desenvolvedor considera adequado. Mas as coisas de baixo nível do VMS exigem absolutamente descritores, que não usam terminação nula (veja o link acima para detalhes). Isso ocorre principalmente para que todos os idiomas (C, assembly etc.) que usam diretamente os componentes internos do VMS possam ter uma interface comum.

Portanto, se você está prevendo qualquer tipo de situação semelhante, pode ser um pouco mais cuidadoso do que o "encerramento nulo universal" pode sugerir que é necessário. Seria mais cuidadoso se estivesse fazendo o que você está fazendo, mas para minhas coisas no nível do aplicativo é seguro assumir o encerramento nulo. Eu apenas não sugeriria o mesmo nível de segurança para você. É possível que seu código precise interagir com o código de idioma assembly e / ou outro código de idioma em algum momento futuro, que nem sempre está em conformidade com o padrão C de seqüências terminadas em nulo.

John Forkosh
fonte
Hoje, a terminação 0 é realmente bastante incomum. C ++ std :: string não, Java String não, Objective-C NSString não, Swift String não - como resultado, cada biblioteca de idiomas suporta strings com códigos NUL dentro da string (o que é impossível com C por razões óbvias).
gnasher729
@ gnasher729 Alterei "... praticamente universal" para "praticamente universal para o padrão C", o que espero elimine qualquer ambiguidade e permaneça correto hoje (e foi isso que eu quis dizer, conforme o assunto e a pergunta do OP).
John Forkosh
0

Na minha experiência em sistemas embarcados, críticos para segurança e em tempo real, não é incomum usar as convenções de string C e PASCAL, ou seja, fornecer o comprimento das strings como o primeiro caractere (que limita o comprimento a 255) e finalizar o string com pelo menos um 0x00, ( NUL), que reduz o tamanho utilizável para 254.

Uma razão para isso é saber quantos dados você espera após o primeiro byte ter sido recebido e outra é que, nesses sistemas, os tamanhos dinâmicos de buffer são evitados sempre que possível - alocar um tamanho fixo de 256 buffers é mais rápido e seguro (não precisa verificar se mallocfalhou). Outra é que os outros sistemas com os quais você está se comunicando podem não estar escritos em ANSI-C.

Em qualquer trabalho incorporado, é importante estabelecer e manter um IDC (Interface Control Document), que define todas as suas estruturas de comunicação, incluindo formatos de string, endianness, tamanhos inteiros etc., o mais rápido possível ( ideal antes de iniciar ), e deve ser o seu livro sagrado, e todas as equipes, ao escrever o sistema - se alguém deseja introduzir uma nova estrutura ou formato, ele deve ser documentado primeiro e todos que possam ser impactados informados, possivelmente com a opção de vetar a mudança .

Steve Barnes
fonte