C - printf проти cout в c, code q - a російська (ru)

[15.1] Чому я повинен використовувати замість традиційного ?

Підвищіть безпеку типів, зменшіть помилки, дозвольте розширюваність і забезпечуйте успадкування.

printf (). можливо, не порушений, і scanf (). можливо, придатний для життя, незважаючи на те, що він схильний до помилок, однак обидва обмежені щодо того, що може робити C ++ I / O. C ++ I / O (з використанням <<и>>) Є відносно C (з використанням printf () і scanf ()):

  • Більш безпечний тип: за допомогою тип об'єкту, який є I / O'd, відомий статично компілятором. навпаки, використовує поля «%» для динамічного визначення типів.
  • Менше помилок: за допомогою немає зайвих токенов «%», які повинні відповідати фактичним об'єктів, які є I / O'd. Видалення надмірності усуває клас помилок.
  • Можливість розширення: механізм C ++ дозволяє вводити нові призначені для користувача типи I / O'd без порушення існуючого коду. Уявіть собі хаос, якщо кожен одночасно додавав нові несумісні поля «%» в printf () і scanf ().
  • Inheritable: механізм C ++ побудований з реальних класів, таких як std :: ostream і std :: istream. На відміну від 'FILE *. це справжні класи і, отже, успадковані. Це означає, що у вас можуть бути інші визначені користувачем речі, які виглядають і діють як потоки, але тим не менше роблять щось дивне і чудове. Ви автоматично можете використовувати ціліони рядків коду введення / виведення, написані користувачами, яких ви навіть не знаєте, і їм не потрібно знати про вашому класі «розширений потік».

Я здивований, що все в цьому питанні стверджують, що std :: cout краще, ніж printf. навіть якщо питання просто задав питання про відмінності. Тепер є різниця - std :: cout - це C ++, а printf - C (проте ви можете використовувати його на C ++, як і все інше з C). Тепер, я буду чесний тут; І printf і std :: cout мають свої переваги.

розтяжність

std :: cout є розширюваним. Я знаю, що люди скажуть, що printf розширюємо, але таке розширення не згадується в стандарті C (тому вам доведеться використовувати нестандартні функції, але не існує загальної нестандартної функції), і такі розширення є одним (Так що легко конфліктує з уже існуючим форматом) .

На відміну від printf. std :: cout повністю залежить від перевантаження оператора, тому немає проблем з одними форматами - все, що ви робите, це визначення підпрограми, в якій std :: ostream є першим аргументом, а ваш тип - другим. Таким чином, немає проблем з простором імен, оскільки у вас є клас (який не обмежений одним символом), ви можете мати для нього std :: ostream перевантаження std :: ostream.

Проте, я сумніваюся, що багато людей захочуть розширити ostream (чесно кажучи, я рідко бачив такі розширення, навіть якщо їх легко зробити). Однак, він тут, якщо вам це потрібно.

Як легко помітити, і printf і std :: cout використовують інший синтаксис. printf використовує стандартний синтаксис функції з використанням рядка шаблону і списків аргументів змінної довжини. Фактично, printf є причиною того, що C має їх - формати printf занадто складні, щоб їх можна було використовувати без них. Однак std :: cout використовує інший API - operator <

Як правило, це означає, що версія C буде коротше, але в більшості випадків це не має значення. Різниця помітна, коли ви друкуєте багато аргументів. Якщо вам потрібно написати щось на зразок Error 2: File not found. Припускаючи номер помилки, і його опис є заповнювачем, код буде виглядати так. Обидва приклади працюють однаково (ну, начебто, std :: endl насправді скидає буфер).

Хоча це не здається занадто божевільним (це всього в два рази більше), все стає більш божевільним, коли ви фактично форматуєте аргументи, а не просто друкуєте їх. Наприклад, друк чогось на кшталт 0x0424 просто божевільна. Це викликано станом змішування std :: cout і фактичними значеннями. Я ніколи не бачив мови, де щось на зразок std :: setfill було б типом (крім C ++, звичайно). printf чітко розділяє аргументи і фактичний тип. Я б вважав за краще зберегти версію printf (навіть якщо він виглядає досить загадковим) в порівнянні з його версією iostream (оскільки він містить занадто багато шуму).

Саме тут лежить реальна перевага printf. Рядок формату printf добре. рядок. Це робить його дуже легко перевести, в порівнянні з operator <<злоупотребление iostream. Предполагая, что функция gettext() переводится, и вы хотите показать Error 2: File not found. Код для перевода ранее показанной строки формата будет выглядеть так:

Тепер припустимо, що ми переводимо в Fictionish, де номер помилки після опису. Перекладена рядок буде виглядати як% 2 $ s oru% 1 $ d. \ N Тепер, як це зробити на C ++? Ну, я й гадки не маю. Я думаю, ви можете зробити підроблений iostream який будує printf який ви можете передати gettext або щось ще для цілей перекладу. Звичайно, $ не є стандартом C, але це настільки поширене, що, на мій погляд, безпечно використовувати.

Не потрібно запам'ятовувати / шукати спеціальний синтаксис целочисленного типу

Ви не можете надрукувати байт NUL, \ 0

Оскільки printf використовує рядка C, а не рядки C ++, він не може друкувати NUL-байт без особливих трюків. У деяких випадках можна використовувати% c з '\ 0' в якості аргументу, хоча це явно хак.

подання

Оновлення. Виявляється, iostream настільки повільний, що він зазвичай повільніше вашого жорсткого диска (якщо ви перенаправляє свою програму в файл). Відключення синхронізації за допомогою stdio може допомогти, якщо вам потрібно вивести велику кількість даних. Якщо продуктивність є реальною проблемою (на відміну від написання кількох рядків у STDOUT), просто використовуйте printf.

Ви можете легко помітити, що два рядки і 2 (число) поміщаються як аргументи printf. От і все; Немає нічого іншого. Для порівняння це iostream скомпільований для збірки. Ні, немає інкрустації; кожен operator <

Однак, чесно кажучи, це нічого не означає, тому що I / O є вузьким місцем в будь-якому випадку. Я просто хотів показати, що iostream не швидше, тому що це «безпечний тип». Більшість реалізацій C реалізують формати printf з використанням обчисленого goto, тому printf працює так само швидко, як це може бути, навіть якщо компілятор не знає про printf (не те, що вони не є), деякі компілятори можуть оптимізувати printf в певних випадках - постійна рядок, що закінчується на \ n зазвичай оптимізується для puts).

успадкування

Я не знаю, чому ви хотіли б наслідувати ostream. але мені байдуже. Це можливо і для FILE.

Тип безпеки

Правда, списки аргументів змінної довжини не мають безпеки, але це не має значення, оскільки популярні компілятори C можуть виявляти проблеми з рядком формату printf. якщо ви включаєте попередження. Насправді, Clang може це зробити без включення попереджень.

В умовах високого рівня основними відмінностями є безпека типів (cstdio не має), продуктивність (більшість реалізацій iostreams повільніше, ніж у cstdio) і розширюваність (iostreams дозволяє налаштовувати вихідні цілі і бесшовно виводити певні користувачем типи).

Люди часто стверджують, що printf набагато швидше. Це в значній мірі міф. Я тільки що протестував його, з наступними результатами:

Висновок: якщо ви хочете тільки нові рядки, використовуйте printf; В іншому випадку cout буде майже таким же швидким або навіть швидше. Більш детальну інформацію можна знайти в моєму блозі.

Щоб бути ясним, я не намагаюся сказати, що iostream завжди краще, ніж printf; Я просто хочу сказати, що ви повинні прийняти обгрунтоване рішення на основі реальних даних, а не дика припущення, засноване на деякому поширеному, що вводить в оману припущенні.

Оновлення: ось повний код, який я використовував для тестування. Скомпільовано з g ++ без будь-яких додаткових опцій (крім -lrt для синхронізації).

Одна з них - це функція, яка друкує на stdout. Інший об'єкт, який надає кілька функцій-членів і перевантаження operator<<которые печатаются в stdout. Есть еще много различий, которые я мог бы перечислить, но я не уверен, что вам нужно.

Для мене справжні відмінності, які змусили б мене перейти на «cout», а не «printf»:

1) <<оператор может быть перегружен для моих классов.

3) Я знаходжу cout більш читабельним, особливо коли у нас багато параметрів.

Одна з проблем з cout - це опції форматування. Форматування даних (точність, справедливість і т. Д.) У printf простіше.

Два пункту, не згаданих тут інакше, я знаходжу значними:

1) cout несе багато багажу, якщо ви ще не використовуєте STL. Він додає в два рази більше коду для вашого об'єктного файлу як printf. Це також вірно для string. і це основна причина, по якій я схильний використовувати свою власну бібліотеку рядків.

2) cout використовує перевантажені <<операторы, которые я нахожу неудачными. Это может добавить путаницу, если вы также используете оператор <<по своему назначению (сдвиг влево). Я лично не люблю перегружать операторов для целей, имеющих тангенциальное значение для их предполагаемого использования.

Підсумок: я буду використовувати cout (і string), якщо я вже використовую STL. В іншому випадку я намагаюся уникати цього.

З примітивами, ймовірно, зовсім не важливо, який з них ви використовуєте. Я кажу, де це корисно, коли ви хочете вивести складні об'єкти.

Наприклад, якщо у вас є клас,

Тепер вищесказане може здатися не дуже хорошим, але давайте припустимо, що ви повинні виводити це в декількох місцях вашого коду. Не тільки це, припустимо, ви додаєте поле «int d». За допомогою cout вам потрібно тільки змінити його в одному місці. Проте, з printf, вам доведеться змінити його в можливо багато місць, і не тільки це, ви повинні нагадати собі, які з них вивести.

З урахуванням сказаного, за допомогою cout, ви можете скоротити багато разів, витрачені на обслуговування вашого коду, а не тільки на те, що якщо ви повторно використовуєте об'єкт «Щось» в новому додатку, вам дійсно не потрібно турбуватися про вихід.

Я хотів би сказати, що відсутність розширюваності printf не зовсім вірно:
У C це правда. Але в C немає реальних класів.
У C ++ можна перевантажити оператор розсилки, тому, перевантажуючи оператор char * і використовуючи printf наступним чином:

Може бути можливим, якщо Foo перевантажить хороший оператор. Або якщо ви зробили хороший метод. Коротше кажучи, printf такий же розширюваний, як cout для мене.

Технічний аргумент, який я можу бачити для потоків C ++ (в загальному. Не тільки cout.):

Тіпобезопасность. (І, до речі, якщо я хочу надрукувати одиночний '\ n' я використовую putchar ( '\ n'). Я не буду використовувати ядерну бомбу, щоб вбити комаха.).

Простіше вчитися. (Ні «складних» параметрів для вивчення, просто для використання операторів <<и>>)

Працюйте з std :: string (для printf є std :: string :: c_str (). Але для scanf?)

Для printf я бачу:

Легше або, по крайней мере, коротше (з точки зору написання символів) складне форматування. Набагато читабельнее, для мене (питання смаку, я думаю).

Краще контролюйте, що зробила функція (поверніть, скільки символів написано і є% n formatter: «Нічого не надруковано. Аргумент повинен бути покажчиком на підписаний int, де зберігається кількість символів, записаних до сих пір» (From printf - C ++ Reference)

Найкраща можливість налагодження. З тієї ж причини, що і останній аргумент.

Мої особисті переваги scanf функцій printf (і scanf), головним чином тому, що я люблю короткі строки, і тому, що я не думаю, що проблеми з типом друку при друку дуже важко уникнути. Єдине, що я засуджую за допомогою функцій C-стилю, це те, що std :: string не підтримується. Ми повинні пройти char * перед тим, як std :: string :: c_str () його printf (з std :: string :: c_str () якщо ми хочемо прочитати, але як писати?)

Інші відмінності: «printf» повертає цілочисельне значення (дорівнює кількості надрукованих символів), а «cout» нічого не повертає

cout <<"y = " <<7; Не является атомарным.

printf ( "% s =.", "y", 7); Є атомарним.

Cout виконує перевірку типів, printf - немає.

Ні еквівалента iostream "% d"

Звичайно, ви можете написати «щось» трохи краще, щоб підтримувати обслуговування:

Я хотів би відзначити, що якщо ви хочете грати в потоки на C ++, якщо ви використовуєте cout ви можете отримати цікаві результати.

Розглянемо цей код:

Тепер вихід все перетасовується. Він також може давати різні результати, спробуйте виконати кілька разів:

Ви можете використовувати printf для правильного вибору, або ви можете використовувати mutex.

Вони використовуються для друку значень. Вони мають абсолютно різний синтаксис. C ++ має як, C тільки має PRINTF.

Я не експерт. Я тільки що сталося підслухати два співробітника говорили про те, як ми повинні уникати використання C ++ у вбудованих системах через проблеми з продуктивністю. Ну, досить цікаво, я зробив тест заснований на реальній задачі проекту.

У зазначеній задачі, ми повинні були написати якусь конфігурацію в ОЗУ. Щось на зразок:

кава = гарячий
цукор = немає
молока = грудей
макінтош = AA: BB: CC: DD: EE: FF

Я зробив все можливе, щоб відшліфувати їх, перш ніж я петельні їх обох 100000 раз. Ось результати:

Розмір файлу об'єкта:

Висновок: На моїй дуже конкретній платформі. з дуже конкретним процесором. працює дуже конкретної версії ядра Linux. щоб запустити програму. яка компілюється з дуже специфічною версією GCC. для того. щоб виконати дуже конкретне завдання. я б сказав. C ++ підхід є більш підходящим. оскільки він працює значно швидше і забезпечує набагато кращу чіткість. З іншого боку, C пропонує невелику площу, на мій погляд, не означає майже нічого. тому що розмір програми не наша турбота.

Я не програміст, але я був інженером людських чинників. Я відчуваю себе мову програмування повинен бути легким, щоб дізнатися, зрозуміти і використовувати, і це вимагає, щоб він мав просту і послідовну мовну структуру. Хоча всі мови Символічно і, таким чином, за своєю суттю, довільно, існують угоди і дотримання ним робить мову простіше в освоєнні і використанні.

В Python ми, звичайно. можна надрукувати за допомогою також досить стандартний object.method синтаксис, тобто variablename.print, оскільки змінні є об'єктами, але в C ++ вони не є.

Я не любить синтаксис соіЬ бо <<оператор не следует никаких правил. Это метод или функция, т.е. принимает параметр и делает что-то для него. Однако написано, как если бы это был математический оператор сравнения. Это плохой подход с точки зрения факторов человека.

printf () є функцією тоді cout є змінною.

Схожі статті