std :: string ile printf?


157

Anladığım kadarıyla, bu ad stringalanının bir üyesi, stdneden aşağıdakiler oluyor?

#include <iostream>

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;
}

resim açıklamasını buraya girin

Program her çalıştığında, myStringyukarıdaki çıktıda olduğu gibi 3 karakterli rastgele görünen bir dize yazdırır.


8
Sadece size bildirmek için, birçok kişi bu kitabı eleştiriyor . Hangi anlayabiliyorum, çünkü nesne yönelimli programlama hakkında çok şey yok, ama insanların iddia ettiği kadar kötü olduğunu düşünmüyorum.
Jesse Good

hilarant! kitaptan geçerken bunu akılda tutmak güzel. Eminim o gelecek yıl boyunca okuyacağım tek C ++ kitap olmayacak, bu yüzden çok fazla
zarar

En yüksek derleyici uyarı kullanılması sorunuza cevap olur - ne zaman gcc ile derleme. MSVC bunu nasıl ele alıyor - bilmiyorum.
Peter VARGA

Yanıtlar:


237

printfC anlamda 1 değişken değişkenleri kullandığından tür güvenli olmadığından derleniyor . printfseçeneği yoktur std::string, yalnızca C stili bir dize vardır. Beklediği şeyin yerine başka bir şey kullanmak kesinlikle istediğiniz sonuçları vermeyecektir. Aslında tanımsız bir davranış, bu yüzden her şey olabilir.

Bunu düzeltmenin en kolay yolu, C ++ kullandığınız için, normalde ile yazdırmaktır std::cout, çünkü std::stringoperatör aşırı yüklenmesi ile bunu destekler:

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

Herhangi bir nedenden ötürü, C stili dize ayıklamanız gerekirse , boş sonlandırılmış bir c_str()yöntem elde std::stringetmek için yöntemini kullanabilirsiniz const char *. Örneğinizi kullanma:

#include <iostream>
#include <string>
#include <stdio.h>

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;
}

Benzeri printfancak güvenli yazılan bir işlev istiyorsanız , değişken şablonlara bakın (C ++ 11, MSVC12'den itibaren tüm büyük derleyicilerde desteklenir). Burada bir örnek bulabilirsiniz . Standart kütüphanede böyle uygulandığını bildiğim hiçbir şey yok, ancak özellikle Boost'da olabilir boost::format.


[1]: Bu, istediğiniz sayıda bağımsız değişkeni iletebileceğiniz anlamına gelir, ancak işlev, bu bağımsız değişkenlerin sayısını ve türlerini belirtmeniz konusunda size güvenir. Durumunda printf, bu kodlanmış tür bilgisine sahip bir dize anlamına gelir.%d anlam anlamına gelir int. Tür veya sayı hakkında yalan söylüyorsanız, bazı derleyicilerin yalan söylediğinde kontrol etme ve uyarı verme yeteneğine sahip olmasına rağmen, işlevin standart bir bilme yolu yoktur.


@MooingDuck, İyi bir nokta. Jerry'nin cevabında, ama kabul edilen cevap olarak, insanlar bunu görüyorlar ve diğerlerini görmeden ayrılabilirler. Bu seçeneği, görülen ilk çözüm ve önerilen çözüm olacak şekilde ekledim.
chris

43

Lütfen kullanma printf("%s", your_string.c_str());

cout << your_string;Bunun yerine kullanın . Kısa, basit ve tipik. Aslında, C ++ yazarken, genellikle printftamamen kaçınmak istersiniz - C ++ 'da nadiren ihtiyaç duyulan veya yararlı olan C'den bir artıktır.

Gelince neden kullanmanız gerektiğini coutyerine printf, nedenleri çoktur. İşte en bariz olanlardan birkaçı:

  1. Sorunun gösterdiği gibi printf, tür güvenli değildir. Geçtiğiniz tür, dönüşüm belirticisinde verilen türden farklıysa,printf bulduğu şeyi belirtilen türdeymiş gibi kullanmaya çalışarak tanımsız davranış verir. Bazı derleyiciler bazı durumlarda bu konuda uyarıda bulunabilir, ancak bazı derleyiciler hiçbir şekilde yapamaz / yapamaz ve hiçbir koşulda hiçbiri bunu yapamaz.
  2. printfgenişletilemez. Sadece ilkel türleri geçirebilirsiniz. Anladığı dönüşüm belirleyicileri kümesi, uygulamasında sabit kodlanmıştır ve daha fazla / başkaları eklemenin bir yolu yoktur. En iyi yazılmış C ++ bu türleri öncelikle çözülecek soruna yönelik türleri uygulamak için kullanmalıdır.
  3. İyi biçimlendirmeyi çok daha zor hale getirir. Açık bir örnek olarak, insanların okuması için sayı yazdırırken, genellikle her birkaç basamakta binlerce ayırıcı eklemek istersiniz. Tam sayı ve ayırıcı olarak kullanılan karakter sayısı değişir, ancak coutbu sayı da kapsanır. Örneğin:

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

    Adsız yerel ayar (""), kullanıcının yapılandırmasına bağlı olarak bir yerel ayar seçer. Bu nedenle, makinemde (ABD İngilizcesi için yapılandırılmış) bu şekilde yazdırılır 123,456.78. Bilgisayarları Almanya için yapılandırılmış (diyelim ki) biri için böyle bir şey yazdıracaktır 123.456,78. Hindistan için yapılandırılmış biri için, 1,23,456.78(ve elbette başkaları da var) olarak yazdırılır . İle printfI tam olarak bir sonuç almak: 123456.78. Tutarlı, ancak her yerdeki herkes için sürekli yanlış . Esasen çevresinde işin tek yolu, daha sonra ayrı ayrı biçimlendirme yapmak için bir dizge olarak geçmektir printfçünkü printfkendisi basitçe olacak değil doğru işi yapmak.

  4. Oldukça kompakt olmalarına rağmen printfbiçim dizeleri okunamayabilir. Hatta kullanmak C programcıları arasında printfher gün neredeyse, ben en az% 99 emin olmak için bir şeyler aramak gerekir tahmin ediyorum #içinde %#xyollarla ve nasıl ne o farklıdır #içinde %#fevet, onlar tamamen farklı şeyler ifade vasıtası (ve ).

11
@ TheDarkIn1978: Muhtemelen unuttun #include <string>. VC ++, başlıklarında, bir dize tanımlamanıza izin veren ancak üstbilgiyi couteklemeden dizeye göndermeyeceğiniz bazı tuhaflıklar içerir <string>.
Jerry Coffin

28
@ Jerry: Büyük boyutlu verilerle uğraşırken printf kullanmanın cout kullanmaktan çok daha hızlı olduğunu belirtmek isteriz. Bu nedenle, lütfen işe yaramaz olduğunu söyleme: D
Programcı

7
@Programmer: bkz. Stackoverflow.com/questions/12044357/… . Özet: çoğu zaman bu coutdaha yavaştır, çünkü yapmamanız gereken std::endlyerde kullandınız .
Jerry Coffin

29
Tipik C ++ uzman kibir. Printf varsa, neden kullanmıyorsunuz?
kuroi neko

6
Tamam, çabuk yorum için özür dilerim. Yine de, printf hata ayıklama için oldukça kullanışlıdır ve akışlar, çok daha güçlü olmasına rağmen, kodun gerçek çıktı hakkında herhangi bir fikir vermediği dezavantajına sahiptir. Biçimlendirilmiş çıktılar için printf hala uygulanabilir bir alternatiftir ve her iki sistemin daha iyi işbirliği yapamayacağı utanç vericidir. Tabii ki sadece benim fikrim.
kuroi neko

28

kullanmak myString.c_str()bir c-benzeri dize (isterseniz const char*printf ile kullanım için)

Teşekkürler



1

Bunun ana nedeni, büyük olasılıkla bir C ++ dizesi, yalnızca 0 bayt tarafından sonlandırılan karakter dizisinin adresini değil, geçerli uzunluk değerini içeren bir yapıdır. Printf ve akrabaları, bir yapı değil, böyle bir dizi bulmayı bekler ve bu nedenle C ++ dizeleriyle karışırlar.

Kendi adıma, printf'in C ++ sözdizimsel özellikleriyle kolayca doldurulamayan bir yere sahip olduğuna inanıyorum, tıpkı html'deki tablo yapılarının div tarafından kolayca doldurulamayan bir yere sahip olması gibi. Dykstra'nın daha sonra goto hakkında yazdığı gibi, bir dine başlamak niyetinde değildi ve gerçekten sadece kötü tasarlanmış kodu telafi etmek için bir çamur olarak kullanmaya karşı tartışıyordu.

GNU projesinin printf ailesini g ++ uzantılarına eklemesi oldukça hoş olurdu.


1

Printf, boyut önemliyse kullanmak oldukça iyidir. Hafızanın bir sorun olduğu bir program çalıştırıyorsanız, printf aslında çok iyi ve değerlendirici bir çözümdür. Cout aslında dizeye yer açmak için bitleri kaydırır, printf ise sadece bir tür parametreyi alır ve ekrana yazdırır. Basit bir merhaba dünya programı derleyecek olsaydınız, printf programı cout'un aksine 60 000 bitten daha az bir sürede derleyebilecekti, derlenmesi 1 milyon bitten fazla sürecekti.

Durumunuz için, id cout kullanmanızı öneririz çünkü kullanımı çok daha uygundur. Rağmen, printf bilmek iyi bir şey olduğunu iddia ediyorum.


1

printfdeğişken sayıda argümanı kabul eder. Bunlar yalnızca Düz Eski Veri (POD) türlerine sahip olabilir. printfDerleyici, biçiminizi doğru bulduğunuzu varsayacağından, yalnızca derleyicilere POD dışında bir şey ileten kod . %silgili argümanın a işaretçisi olması gerektiği anlamına gelir char. Sizin durumunuzda bu bir std::stringdeğil const char*. printfbunu bilmiyor çünkü argüman türü kayboluyor ve format parametresinden geri yüklenmesi gerekiyor. Bu std::stringargümanıconst char* sonuçtaki işaretçiye , istediğiniz C dizesi yerine ilgisiz bellek bölgesini gösterecektir. Bu nedenle kodunuz anlamsız çıktı.

Biçimlendirilmiş metni yazdırmak için mükemmel bir seçim olsa printfda (özellikle doldurma yapmak istiyorsanız), derleyici uyarılarını etkinleştirmediyseniz tehlikeli olabilir. Uyarıları her zaman etkinleştirin çünkü böyle hatalar kolayca önlenebilir. Ailenin aynı görevi daha hızlı ve daha güzel bir şekilde yapabilmesi durumunda beceriksiz mekanizmayı kullanmak için hiçbir neden yoktur . Tüm uyarıları ( ) etkinleştirdiğinizden emin olun ve iyi olacaksınız. Kendi özel uygulamanızı kullanmanız durumunda , derleyicinin biçim dizesini sağlanan parametrelere göre denetlemesini sağlayan mekanizma ile bildirmeniz gerekir .std::coutprintf-Wall -Wextraprintf__attribute__

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.