C ++ printf com std :: string?

Meu entendimento é que string é um membro do namespace std , então por que o seguinte ocorre?

 #include  int main() { using namespace std; string myString = "Press ENTER to quit program!"; cout << "Come up and C++ me some time." << endl; printf("Follow this command: %s", myString); cin.get(); return 0; } 

insira a descrição da imagem aqui

Cada vez que o programa é executado, myString imprime uma seqüência aparentemente aleatória de 3 caracteres, como na saída acima.

Ele está compilando porque o printf não é seguro, já que usa argumentos variables ​​no sentido C 1 . printf não tem opção para std::string , apenas uma string no estilo C. Usar outra coisa no lugar do que ela espera definitivamente não lhe dará os resultados desejados. Na verdade, é um comportamento indefinido, então tudo pode acontecer.

A maneira mais fácil de corrigir isso, já que você está usando o C ++, está imprimindo normalmente com o std::cout , já que o std::string suporta isso através da sobrecarga do operador:

 std::cout < < "Follow this command: " << myString; 

Se, por algum motivo, você precisar extrair a string de estilo C, você pode usar o método c_str() de std::string para obter um const char * que é terminado em null. Usando seu exemplo:

 #include  #include  #include  int main() { using namespace std; string myString = "Press ENTER to quit program!"; cout < < "Come up and C++ me some time." << endl; printf("Follow this command: %s", myString.c_str()); //note the use of c_str cin.get(); return 0; } 

Se você quiser uma function que seja como printf , mas digite safe, procure em modelos variadic (C ++ 11, suportado em todos os principais compiladores do MSVC12). Você pode encontrar um exemplo de um aqui . Não há nada que eu saiba implementado assim na biblioteca padrão, mas pode haver no Boost, especificamente no boost::format .


[1]: Isso significa que você pode passar qualquer número de argumentos, mas a function depende de você informar o número e os tipos desses argumentos. No caso de printf , isso significa uma string com informações de tipo codificadas como %d significando int . Se você mentir sobre o tipo ou número, a function não possui uma maneira padrão de conhecer, embora alguns compiladores tenham a capacidade de verificar e dar avisos quando você estiver.

Por favor, não use printf("%s", your_string.c_str());

Use cout < < your_string; em vez de. Curto, simples e seguro. De fato, quando você está escrevendo C ++, você geralmente quer evitar o printf inteiramente - é um resto do C que raramente é necessário ou útil em C ++.

Quanto ao motivo pelo qual você deve usar cout vez de printf , as razões são numerosas. Aqui está uma amostra de alguns dos mais óbvios:

  1. Como a pergunta mostra, o printf não é seguro para o tipo. Se o tipo que você passar for diferente do especificado no especificador de conversão, o printf tentará usar o que encontrar na pilha como se fosse do tipo especificado, dando um comportamento indefinido. Alguns compiladores podem avisar sobre isso sob algumas circunstâncias, mas alguns compiladores não podem / não querem, e nenhum pode sob todas as circunstâncias.
  2. printf não é extensível. Você só pode passar tipos primitivos para ele. O conjunto de especificadores de conversão que ele entende é codificado em sua implementação e não há como adicionar mais / outros. O C ++ mais bem escrito deve usar esses tipos principalmente para implementar tipos orientados para o problema que está sendo resolvido.
  3. Isso torna a formatação decente muito mais difícil. Para um exemplo óbvio, quando você está imprimindo números para as pessoas lerem, você normalmente deseja inserir separadores de milhares em cada poucos dígitos. O número exato de dígitos e os caracteres usados ​​como separadores variam, mas o cout também cobre isso. Por exemplo:

     std::locale loc(""); std::cout.imbue(loc); std::cout < < 123456.78; 

    O local sem nome (o "") escolhe uma localidade com base na configuração do usuário. Portanto, na minha máquina (configurada para inglês dos EUA) isso é impresso como 123,456.78 . Para alguém que tenha seu computador configurado para (digamos) a Alemanha, imprimiria algo como 123.456,78 . Para alguém com ele configurado para a Índia, seria impresso como 1,23,456.78 (e, claro, há muitos outros). Com printf eu recebo exatamente um resultado: 123456.78 . É consistente, mas é sempre errado para todos em todos os lugares. Essencialmente, a única maneira de contornar isso é fazer a formatação separadamente, em seguida, passar o resultado como uma string para printf , porque printf si simplesmente não fará o trabalho corretamente.

  4. Embora sejam bastante compactas, as strings de formato printf podem ser bastante ilegíveis. Mesmo entre os programadores C que usam o printf virtualmente todos os dias, eu acho que pelo menos 99% precisaria procurar as coisas para ter certeza do que o # em %#x significa, e como isso difere do que o # em %#f significa (e sim, eles significam coisas completamente diferentes).

use myString.c_str () se você quiser que uma string c-like (const char *) seja usada com printf

Use o exemplo std :: printf e c_str ():

 std::printf("Follow this command: %s", myString.c_str()); 

Printf é realmente muito bom de usar se o tamanho for importante. Ou seja, se você estiver executando um programa em que a memory é um problema, então o printf é realmente uma solução muito boa e sob avaliador. Cout basicamente desloca os bits para dar espaço para a string, enquanto printf apenas aceita algum tipo de parâmetro e o imprime na canvas. Se você fosse compilar um simples programa hello world, o printf seria capaz de compilá-lo em menos de 60.000 bits, ao contrário de cout, levaria mais de 1 milhão de bits para compilar.

Para sua situação, id sugiro usar cout simplesmente porque é muito mais conveniente usar. Embora, eu diria que printf é algo bom de saber.

A principal razão é provavelmente que uma cadeia C ++ é uma estrutura que inclui um valor de comprimento atual, não apenas o endereço de uma seqüência de caracteres terminados por um byte 0. Printf e seus parentes esperam encontrar tal seqüência, não uma estrutura e, portanto, se confundem com strings C ++.

Falando por mim, acredito que o printf tem um lugar que não pode ser facilmente preenchido por resources sintáticos do C ++, assim como as estruturas de tabela em html têm um lugar que não pode ser facilmente preenchido por divs. Como Dykstra escreveu mais tarde sobre o goto, ele não tinha a intenção de começar uma religião e estava apenas argumentando contra usá-lo como um kludge para compensar o código mal projetado.

Seria muito bom se o projeto GNU adicionasse a família printf às suas extensões g + +.

printf aceita um número variável de argumentos. Esses só podem ter tipos de dados antigos simples (POD). Código que passa qualquer coisa diferente de POD para o printf só compila porque o compilador assume que você tem o seu formato correto. %s significa que o respectivo argumento deve ser um ponteiro para um char . No seu caso, é um std::string não const char* . printf não o conhece porque o tipo de argumento é perdido e deve ser restaurado a partir do parâmetro format. Ao transformar esse argumento std::string em const char* o ponteiro resultante apontará para uma região irrelevante da memory, em vez de sua string C desejada. Por esse motivo, o seu código imprime sem sentido.

Embora o printf seja uma excelente opção para imprimir texto formatado (especialmente se você pretende ter preenchimento), pode ser perigoso se você não tiver ativado os avisos do compilador. Sempre ative os avisos, pois erros assim são facilmente evitáveis. Não há razão para usar o desajeitado mecanismo std::cout se a família printf puder fazer a mesma tarefa de maneira muito mais rápida e mais bonita. Apenas certifique-se de ter ativado todos os avisos ( -Wall -Wextra ) e você será bom. Caso você use sua própria implementação personalizada de printf você deve declará-la com o mecanismo __attribute__ , que permite ao compilador verificar a string de formatação em relação aos parâmetros fornecidos .