Почему я должен включить модификатор длины в качестве аргумента в scanf () - члены семьи? В чем выгода? Что делает scanf с модификатором длины? - PullRequest
0 голосов
/ 26 октября 2019

Существует необязательная возможность включить модификатор длины в качестве аргумента в утверждение scanf() или одного из членов его семьи.

Почему я должен точно реализовывать модификатор длины в выражении, независимо от того, объявил ли я уже присваивающую переменную как, например, long int, правильную или нет?

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

Например, l (ell) внутри %2ld:

long int i;
fscanf(stdin,"%2ld",&i);

Я понимаю, что такое модификатор длины, но не причина его реализации.

Я провел небольшое исследование, даже в C99, для него, но не могу найти подробного описания того, что он действительно делает в деталях.

На этом сайте указано:

длина (-модификатор)

Один из hh, h, l, ll, j, z, t, L (необязательно). Это изменяет ожидаемый тип хранилища, на которое указывает соответствующий аргумент (см. Ниже).

Означает ли это, что модификатор длины изменяет тип назначенной переменной, если он на самом деле не соответствуетзначение формата, определенное с помощью спецификатора преобразования?

Что точно делает scanf() с модификатором длины?

В чем выгода?

Большое спасибо за то, что выслушали.

Ответы [ 2 ]

2 голосов
/ 26 октября 2019

scanf необходимо знать, к какому типу объекта он пишет. Предположим, что в этой реализации C a char - это один байт, short - это два байта, int - это четыре байта, а long int - это восемь байтов. Аргумент scanf получает указатель, но единственный способ scanf узнать, на что указывает этот указатель, - через строку формата.

Если аргумент указывает на long int, тогда scanf необходимозаписать восемь байтов в это место. Если он указывает на short, то scanf должен записать в это место два байта, а не восемь.

Модификаторы длины являются просто частью кода, который сообщает scanf о типах. Мы могли бы создать код, где d означает int, D означает long int, S означает short int и так далее. Или мы могли бы сделать другие коды. Просто выбранный нами код использует d для int, hd для short int, ld для long int и т. Д.

Другой вариант мог бы заключаться в разделенииинформация о типе пункта назначения с информацией о том, что анализировать. d означает как int, так и «читать десятичное число по умолчанию». Более чистый дизайн мог иметь один код для типа (short, int и т. Д.) И другой код для формата ввода (десятичный, восьмеричный, шестнадцатеричный, что-то еще).

Это такжеВозможно, сам указатель имеет различное представление в зависимости от того, на что он указывает. Это необычно в современных реализациях C, но возможно. Так что scanf использует модификатор длины и для того, чтобы знать, к какому типу объекта он пишет и какой тип указателя был передан ему.

1 голос
/ 26 октября 2019

EDIT:

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


Скомпилируйте следующую программу (не забывайте игнорировать предупреждения!) :

#include <stdio.h>

int main ()
{
    long int i;
    long int j;
    scanf("%d", &i);
    scanf("%ld", &j);
    printf("i == %ld\n", i); 
    printf("j == %ld\n", j);
    return 0;
}

Запустите ее и введите целое число больше, чем 2147483647 двараз, и вы получите свои ответы.


СПОЙЛЕР:

Я сделал это с 4294967297, и было напечатано следующее:

i == 1
j == 4294967297

Конечно,Ответ зависит от архитектуры, и я предположил, что ваша машина хранит int в 32 битах. Но если есть причина использовать long int для хранения в нем числа, лучше использовать модификатор длины (если это не имеет значения, вам не нужно использовать long int).

Но чтобы ответить на ваши вопросы немного более явно:

Означает ли это, что модификатор длины изменяет тип назначенной переменной, если он на самом деле не соответствует значению формата, определенного с помощьюспецификатор преобразования?

Нет. Как вы видели, когда пытаетесь сохранить значение, которое должно вписаться в long int без модификатора длины, вы будете действовать так, как если бы вы сохраняли в int (даже если бы происходило переполнение). Если вы используете %ld с scanf, чтобы попытаться сохранить значение в переменной int, то не будет выброшено segfault, и это может привести к плохим вещам. Компиляция следующего (и необходимость игнорировать предупреждение ради эксперимента):

#include <stdio.h>

int main ()
{
    int k[2] = {0,0};
    printf("k[0] == %d\n", k[0]);
    printf("k[1] == %d\n", k[1]);
    scanf("%ld", &k);
    printf("k[0] == %d\n", k[0]);
    printf("k[1] == %d\n", k[1]);
    return 0;
} 

и запуск его путем ввода 4294968300 распечаток:

k[0] == 0
k[1] == 0
4294968300 
k[0] == 1004
k[1] == 1

означает, что пресловутый scanf записал прошлое k[0] в k[1] совершенно незамеченным!

Почему я [sic] должен точно применять модификатор длины в операторе, независимо от того, объявил ли я [sic] присваивающую переменную как, например, long int, правильную или нет?

Я совершенно не понимаю, почему я [sic] должен это делать, и не вижу никакой разницы в выводе между включением его и выполнением без модификатора длины.

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

...