разница между %ms и %s scanf

Читая руководство scanf, я встречаю эту строку:

Необязательный символ 'm'. Это используется с преобразованиями строк (%s, %c, %[),

Может ли кто-нибудь объяснить это на простом примере, указав разницу и необходимость такой опции в некоторых случаях?


person rondino    schedule 31.07.2016    source источник
comment
Думаете, я только первую строчку прочитал?! -_- Я не понял, если честно! вот почему я просто хотел крошечный пример с простым объяснением, указывающим на разницу простыми словами?   -  person rondino    schedule 31.07.2016
comment
Ненавижу звучать грубо, но такое впечатление оставляет ваш вопрос. Параграф объясняет разницу довольно хорошо. Если вы не поняли конкретную фразу, почему бы не упомянуть об этом?   -  person n. 1.8e9-where's-my-share m.    schedule 31.07.2016
comment
Я все еще новичок в C, и я делаю все возможное, чтобы понять его достаточно хорошо. Я ненавижу читать sthg и просто пропускаю это! Я правда ценю это ! большое спасибо @BLUEPIXY   -  person rondino    schedule 31.07.2016


Ответы (1)


Стандарт C не определяет такой необязательный символ в форматах scanf().

GNU lib C определяет необязательный индикатор a таким образом (из справочной страницы для scanf):

Необязательный символ a. Это используется со строковыми преобразованиями и освобождает вызывающую сторону от необходимости выделять соответствующий буфер для хранения ввода: вместо этого scanf() выделяет буфер достаточного размера и присваивает адрес этого буфера соответствующему аргументу указателя, который должен быть указатель на переменную char * (эту переменную не нужно инициализировать перед вызовом).

Вызывающий должен впоследствии free этот буфер, когда он больше не требуется. Это расширение GNU; C99 использует символ a в качестве спецификатора преобразования (и он также может использоваться как таковой в реализации GNU).

В разделе ПРИМЕЧАНИЯ справочной страницы говорится:

Модификатор a недоступен, если программа скомпилирована с gcc -std=c99 или gcc -D_ISOC99_SOURCE (если не указано также _GNU_SOURCE), и в этом случае a интерпретируется как спецификатор для чисел с плавающей запятой (см. выше).

Начиная с версии 2.7, glibc также предоставляет модификатор m для той же цели, что и модификатор a. Модификатор m имеет следующие преимущества:

  • Его также можно применять к спецификаторам преобразования %c (например, %3mc).

  • Это позволяет избежать двусмысленности в отношении спецификатора преобразования с плавающей запятой %a (и не зависит от gcc -std=c99 и т. д.).

  • Это указано в предстоящей версии стандарта POSIX.1.

Страница интерактивного руководства по Linux по адресу http://linux.die.net/man/3/scanf только документирует эту опцию как:

Необязательный символ 'm'. Это используется со строковыми преобразованиями (%s, %c, %[) и избавляет вызывающую сторону от необходимости выделять соответствующий буфер для хранения ввода: вместо этого scanf() выделяет буфер достаточного размера и присваивает адрес этого буфера соответствующий аргумент-указатель, который должен быть указателем на переменную char * (эту переменную не нужно инициализировать перед вызовом). Вызывающий должен впоследствии free(3) этот буфер, когда он больше не требуется.

Стандарт Posix документирует это расширение в редакции POSIX.1-2008 (см. http://pubs.opengroup.org/onlinepubs/9699919799/functions/fscanf.html ):

Спецификаторы преобразования %c, %s и %[ должны принимать необязательный символ назначения-распределения m, который должен вызывать выделение буфера памяти для хранения преобразованной строки, включая завершающий нулевой символ. В таком случае аргумент, соответствующий спецификатору преобразования, должен быть ссылкой на переменную-указатель, которая получит указатель на выделенный буфер. Система должна выделить буфер, как если бы был вызван malloc(). Приложение должно нести ответственность за освобождение памяти после использования. Если для выделения буфера недостаточно памяти, функция должна установить errno в [ENOMEM], и результатом будет ошибка преобразования. Если функция возвращает EOF, любая память, успешно выделенная для параметров с использованием символа назначения-распределения m этим вызовом, должна быть освобождена до возврата функции.

Используя это расширение, вы можете написать:

char *p;
scanf("%ms", &p);

Заставляет scanf анализировать слово из стандартного ввода и выделять достаточно памяти для хранения его символов плюс завершающий '\0'. Указатель на выделенный массив будет сохранен в p, а scanf() вернет 1, если только из stdin нельзя будет прочитать символы, отличные от пробелов.

Вполне возможно, что другие системы используют m для аналогичной семантики или для чего-то совсем другого. Нестандартные расширения не переносимы, и их следует использовать очень осторожно, задокументировав как таковые, в обстоятельствах, когда стандартный подход громоздок, непрактичен или вообще невозможен.

Обратите внимание, что синтаксический анализ слова произвольного размера действительно невозможен со стандартной версией scanf():

Вы можете разобрать слово с максимальным размером и должны указать максимальное количество символов для хранения перед '\0':

char buffer[20];
scanf("%19s", buffer);

Но это не говорит вам, сколько еще символов доступно для анализа в стандартном вводе. В любом случае, непередача максимального количества символов может привести к неопределенному поведению, если ввод достаточно длинный, и злоумышленник может даже использовать специально созданный ввод для взлома вашей программы:

char buffer[20];
scanf("%s", buffer); // potential undefined behavior,
                     // that could be exploited by an attacker.
person chqrlie    schedule 31.07.2016
comment
Это часть стандарта POSIX 2013, поэтому любая система, соответствующая этому стандарту, будет его поддерживать. - person Chris Dodd; 31.07.2016
comment
Его можно найти онлайн здесь - person Chris Dodd; 31.07.2016