Порядок ввода чисел и знаков однозначен, оказывается (Сперва вводим знаки, потом цифры, но не наоборот!)

Модератор: Модераторы разделов

promov
Сообщения: 384
Статус: Участник
ОС: Debian GNU/Linux

Порядок ввода чисел и знаков однозначен, оказывается

Сообщение promov » 15.10.2007 11:20

Чем дальше в лес, тем больше дров.
Вот рабочий код. В нём всё понятно. Cперва команда на ввод знака, потом ввод знака, потом команда на ввод числа, потом ввод числа.

Код: Выделить всё

#include <stdio.h>
int main (){
 char c;
 int a;
 printf ("Введите какую-нибудь букву\n");
 scanf ("%c", & c);
 printf ("Введите число типа int\n");
 scanf ("%i", & a);
 return 0;
}


А вот в этом коде нужно наоборот, сперва число ввести, а потом знак

Код: Выделить всё

#include <stdio.h>
int main () {
 char c;
 int a;
 printf ("Введите число типа int\n");
 scanf ("%i", & a);
 printf ("Введите какую-нибудь букву\n");
 scanf ("%c", & c);
 return 0;
}


Код нерабочий. Последнее, что можно видеть на экране, это "Введите какую-нибудь букву". Всё это очень-очень странно. Cкажите пожалуйста, это оговорено где-нибудь?
Ещё. Cмею предположить, что обучение языкам программирования начинается с обучения командам ввода-вывода. Месяц назад, когда я эти команды штутдировал (настолько, конечно, насколько литература позволяла) я бы не посмел обратиться с таким вопросом. Но теперь я их (команды) благополучно прошёл, что называется, тонкости такой оговорённой нигде не встретил... Да я и сейчас-то на эту проблему случайно, можно сказать, наткнулся. Не случайность бы, так бы и оставался в неведении и шпарил бы себе дальше по учебнику... В общем, что за тонкость такая? Где о ней можно прочесть?

C уважением, promov.

...Да тоже, когда я Pascal изучал, была там такая комада readln. Она нужна для того, чтобы приостанавливать выполнение программы до нажатия Enter. Пишешь, то есть, в нужном месте такую команду и, доходя до этого места программа приостанавливается и ждёт, когда ты на Enter нажмёшь. Очень удобно и нужно. Так вот, иногда одиночное readln не срабатывала, а срабатывала двойная. Я не разобрался в своё время- двойная так двойная, ну и писал двойную. (И то сказать, где мне самому разобраться было быстро?) Теперь, видать, пришла пора. Я это всё к чему- сдаётся мне, вот эта вот readln и scanf из одной оперы. Что-то их связывает, какая-то глобальная концепция, мне одному непонятная. Мне бы краем глаза на эту концепцию взглянуть, а то я не знаю даже, в каком направлении думать!
Зачем хорёк пошел в ларёк, зачем барсук полез на сук...
Мораль легко уразуметь: зачем на бал пришёл медведь?
Спасибо сказали:

Аватара пользователя
Attila
Сообщения: 125
Статус: Тролль-Лѣсовичокъ
ОС: Свободная aka ArchLinux

Re: Порядок ввода чисел и знаков однозначен, оказывается

Сообщение Attila » 15.10.2007 13:11

Во втором случае. Вы вводите число и, по-видимому, клацаете на Enter. Функция scanf считывает это число и останавливается перед переводом строки (что-то вроде того). Следующая scanf считывает единственный символ (эту 10). И ничего вводить не просит.
Добавьте, например, для убедительности printf("%d", c); перед return. Вы убедитесь, что считалась именно 10.

Если Вы, например, хотите пропустить этот "перевод на новую сторку", то добавьте пробел в спецификацию формата: scanf (" %c", & c); Так будут пропускаться все символы-разделители.
Спасибо сказали:

sergio
Сообщения: 436
Статус: Интересующийся новичок
ОС: Debian GNU/Linux 4 & 5

Re: Порядок ввода чисел и знаков однозначен, оказывается

Сообщение sergio » 15.10.2007 13:31

Вам ответили. Вот рабочий вариант вашего кода.

Код: Выделить всё

#include <stdio.h>
int main () {
  char c;
  int a;
  printf ("Введите число типа int\n");
  scanf ("%i", & a);
  scanf ("%c", & c); /* считать NL и выбросить нафиг*/
  printf ("Введите какую-нибудь букву\n");
  scanf ("%c", & c);  /* а теперь считать то, что надо */
  return 0;
}


И читаем у К.Р раздел "Форматный ввод" чуть внимательнее.
"%c ... обычный пропуск символов разделителей подавляется ... чтобы прочесть символ, отличный от разделителя используем %1s "
Еще (и более) рабочий вариант:

Код: Выделить всё

#include <stdio.h>
int main () {
  char c[2]; // !!!!!!!!!!!
  int a;
  printf ("Введите число типа int\n");
  scanf ("%i", & a);
  printf ("Введите какую-нибудь букву\n");
  scanf ("%1s", c);
  return 0;
}
Debian GNU/Linux 4 -- AMD Athlon64 3000+ / Asus 7600GS -- Gnome
Debian GNU/Linux 5 -- Dell (Vostro) 500 (Celeron M560 / iGM965) -- Gnome
Спасибо сказали:

sergio
Сообщения: 436
Статус: Интересующийся новичок
ОС: Debian GNU/Linux 4 & 5

Re: Порядок ввода чисел и знаков однозначен, оказывается

Сообщение sergio » 15.10.2007 13:52

Ну и наконец цивильный подход при _интерактивном_ вводе с консоли сводится примерно к следующему:
1) выводится приглашение ввести что-то
2) считывается это значение
3) проверяется, что значие должного типа удалось считать
4) проверяется, что значение отвечает нужному кретерию (0< n < 10 к примеру)
5) если все ОК то возвращаем значение
5.5, С++ only) очищается состояние входного потока
6) очищается "очередь" входного потока путем игнорирования всего, что в нем осталось, до NL включительно
7) выводится сообщение об ошибке ввода (и опционально в чем именно ошибка)
8) переходим на 1)
Совсем логично было б очищать "очередь" пунктом 0.5..1.5, но это по независящим от нас обстоятельствам затруднительно.

Но обыкновенно потоковый ввод используется ни интерактивно, и достаточно правильно игнорировать whitespaces, а если что-то не так считалось - вывести "Error: Input file corrupted." и успокоиться. :)

Да. это именно имеет отношение вот к этому:
сдаётся мне, вот эта вот readln и scanf из одной оперы. Что-то их связывает, какая-то глобальная концепция, мне одному непонятная. Мне бы краем глаза на эту концепцию взглянуть, а то я не знаю даже, в каком направлении думать!

Эта концепция называется концепцией буфера ввода. :)
Debian GNU/Linux 4 -- AMD Athlon64 3000+ / Asus 7600GS -- Gnome
Debian GNU/Linux 5 -- Dell (Vostro) 500 (Celeron M560 / iGM965) -- Gnome
Спасибо сказали:

promov
Сообщения: 384
Статус: Участник
ОС: Debian GNU/Linux

Re: Порядок ввода чисел и знаков однозначен, оказывается

Сообщение promov » 15.10.2007 23:41

Attila писал(а):
15.10.2007 13:11
Во втором случае. Вы вводите число и, по-видимому, клацаете на Enter. Функция scanf считывает это число и останавливается перед переводом строки (что-то вроде того). Следующая считывает единственный символ (эту 10). И ничего вводить не просит.
Добавьте, например, для убедительности printf("%d", c); перед return. Вы убедитесь, что считалась именно 10.

Если Вы, например, хотите пропустить этот "перевод на новую сторку", то добавьте пробел в спецификацию формата: scanf (" %c", & c); Так будут пропускаться все символы-разделители.


Давайте так. Первый раз число функция scanf считала? Считала. ("Cчитала" в моём понимании: сделала так что в том месте в компьютере, которое отведено для хранения значения переменной, стало находиться введённое число в соответствующем виде- в виде нолей и единиц. Что из себя представляют ноли и единицы внутри компьютера, я ума не приложу.) А Вы пишите, что вторая scanf тоже считала и далее по тексту. Ничего не пойму. А зачем второй-то раз считывать число, если оно уже считано первой scanf? Да и то: всегда же одной scanf хватало, чтобы считать число!

Что такое NL? Скажите, пожалуйста!
Зачем хорёк пошел в ларёк, зачем барсук полез на сук...
Мораль легко уразуметь: зачем на бал пришёл медведь?
Спасибо сказали:

Аватара пользователя
Uncle_Theodore
Сообщения: 3339
ОС: Slackware 12.2, ArchLinux 64

Re: Порядок ввода чисел и знаков однозначен, оказывается

Сообщение Uncle_Theodore » 16.10.2007 00:34

NL == new line. Символ возврата каретки, который вводится в компьютер, когда Вы нажимаете Enter.
Именно его и считала вторая scanf
Спасибо сказали:

sergio
Сообщения: 436
Статус: Интересующийся новичок
ОС: Debian GNU/Linux 4 & 5

Re: Порядок ввода чисел и знаков однозначен, оказывается

Сообщение sergio » 16.10.2007 00:40

promov писал(а):
15.10.2007 23:41
Что такое NL? Скажите, пожалуйста!

NewLine, '\n'.
На юникс раскрывается в символ с цифровым значением 10 или LF, LineFeed
На маках раскрывается в символ с цифровым значением 13 или CR, CarrigeReturn
На оффтопике раскрывается в два символа с цифровыми значениями 13,10 или CR,LF
В этом разнообразии причина проблем в сети. Вот, например, тут опять всплыла тема "как удалить символы ^M" - это как раз про это. :happy:
Когда вы вводите последовательно, в ответ на два запроса, 13245 и 'a', вы дважды нажимаете энтер.
Во входном потоке си приложения у вас оказываются символы

Код: Выделить всё

1 3 2 4 5 <NL> a <NL>

Оператор считывания числа доходит до первого НЛ и останавливается, поскольку НЛ не входит в его формат числа. Следующий оператор считывает символ. Этим символом оказывается НЛ.
Когда читаете в обратном порядке - оператор символа получает свой символ, оператор читающий число игнорирует leading whitespaces, в число которых входят в частности пробелы, НЛ и табуляции, с первого отличного от вайтспейса символа он пытается прочитать число. Оператор считывания чара вайтспейсы не выкиыдвает, в отличие от остальных, о чем я вам и цитировал из К.Р.
Запустите свою "неработающую" версию программы с перенаправлением из фала

Скорее всего она выдаст ожидаемый результат. С перенаправлением из файла

Код: Выделить всё

123455 a

чар получит значение "пробел". А вашу правильную программу - попросите сосчитать из файла

Код: Выделить всё

<space> а 123455

чар получит значение пробел, а инт значения не получит, получит ошибку чтения, потому что из буквы "а" десятичное число не получается. :)

Кстати, можете и так на первый запрос числа ввести не 123, а 1234ж - и посмотрите что получится, это интересно. :) P.S.: лучше не "ж", потому что ютф8. Что-нибудь английское. =)
Debian GNU/Linux 4 -- AMD Athlon64 3000+ / Asus 7600GS -- Gnome
Debian GNU/Linux 5 -- Dell (Vostro) 500 (Celeron M560 / iGM965) -- Gnome
Спасибо сказали:

Аватара пользователя
Attila
Сообщения: 125
Статус: Тролль-Лѣсовичокъ
ОС: Свободная aka ArchLinux

Re: Порядок ввода чисел и знаков однозначен, оказывается

Сообщение Attila » 16.10.2007 00:47

NL - по-видимому, new line
Вы ввели число, после этого нажали Enter, то есть, ввели символ NL.
Так как Вы явно указали функции scanf, что хотите считать число, то она считывает все байты из потока ввода, пока не дойдёт до символа LF(Line Feed, перевод строки). (Который появился там, после того как Вы нажали Enter).
После этого Вы считываете следующим scanf'ом один-единственный символ. Этим единственным сиволом является символ новой строки, который благополучно считывается.
В кодировке ASCII код символа LF "перевод на новую строку" равен 10 или 0x0A. Поэтому если перед return добавить printf("%d", c), будет выведено именно 10 - код символа "перевод на новую строку" в кодировке ASCII, который появлися в потоке после Вашего нажатия Enter. Можете, например, попробывать на приглашение ввести число ввести что-то вроде 1234W и нажать Enter. В переменную а считается 1234 (число),в переменную с символ W.
Спасибо сказали:

promov
Сообщения: 384
Статус: Участник
ОС: Debian GNU/Linux

Re: Порядок ввода чисел и знаков однозначен, оказывается

Сообщение promov » 16.10.2007 11:39

Спешу отписаться.
Я уловил, надеюсь, самую суть. Вопросы, конечно, ещё возникнут, но это будет потом. Плохо, что заданий на эту тему нет- хорошая (читай: единственная) книга Культина, конечно, далеко не то. Она передо мной, кстати.
Предположу, что я даже понял, почему переменные, объявленные как char есть возможность вывести как int. И какие цифры будут при выводеб например, знака t, я тоже могу определить- таблица передо мной. А теперь цитата из Кернигана и Ричи:

"Каждая конструкция с символом % в первом строковом аргументе функции printf должна иметь соответствие: второй, третий и т. д. аргумент; их количество и типы должны быть согласованы , иначе будут выданы неверные ответы."
В связи с таким правилом то, что мы делаем (объявляем переменную как char, а выводим как int) как охарактеризовать:

1) мы нашли брешь в компиляторе и ей пользуемся, или же
2) возможность такого вот вывода официально оговорена (мелкими буквами, среди перечня чего-то там, как бы между прочим, в конце некоторой книги)

Cпасибо всем за помощь!
Зачем хорёк пошел в ларёк, зачем барсук полез на сук...
Мораль легко уразуметь: зачем на бал пришёл медведь?
Спасибо сказали:

v04bvs
Сообщения: 636
ОС: Debian GNU/Linux

Re: Порядок ввода чисел и знаков однозначен, оказывается

Сообщение v04bvs » 16.10.2007 11:56

promov писал(а):
16.10.2007 11:39
Спешу отписаться.
Я уловил, надеюсь, самую суть. Вопросы, конечно, ещё возникнут, но это будет потом. Плохо, что заданий на эту тему нет- хорошая (читай: единственная) книга Культина, конечно, далеко не то. Она передо мной, кстати.
Предположу, что я даже понял, почему переменные, объявленные как char есть возможность вывести как int. И какие цифры будут при выводеб например, знака t, я тоже могу определить- таблица передо мной. А теперь цитата из Кернигана и Ричи:

"Каждая конструкция с символом % в первом строковом аргументе функции printf должна иметь соответствие: второй, третий и т. д. аргумент; их количество и типы должны быть согласованы , иначе будут выданы неверные ответы."
В связи с таким правилом то, что мы делаем (объявляем переменную как char, а выводим как int) как охарактеризовать:

1) мы нашли брешь в компиляторе и ей пользуемся, или же
2) возможность такого вот вывода официально оговорена (мелкими буквами, среди перечня чего-то там, как бы между прочим, в конце некоторой книги)

Cпасибо всем за помощь!


Я бы рекомендовал такой стиль: printf("%d", (int) c); думаю здесь всё понятно.
Я затрудняюсь сказать, является ли printf("%d", c) допустимой конструкцией по стандарту.
Спасибо сказали:

Аватара пользователя
drBatty
Сообщения: 8735
Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
ОС: Slackware-current

Re: Порядок ввода чисел и знаков однозначен, оказывается

Сообщение drBatty » 16.10.2007 11:58

promov писал(а):
16.10.2007 11:39
"Каждая конструкция с символом % в первом строковом аргументе функции printf должна иметь соответствие: второй, третий и т. д. аргумент; их количество и типы должны быть согласованы , иначе будут выданы неверные ответы."
В связи с таким правилом то, что мы делаем (объявляем переменную как char, а выводим как int) как охарактеризовать:

1) мы нашли брешь в компиляторе и ей пользуемся, или же
2) возможность такого вот вывода официально оговорена (мелкими буквами, среди перечня чего-то там, как бы между прочим, в конце некоторой книги)

и 1 и 2. Это не беда компилятора а беда самого Cи(но не С++). Точнее беда всех функций с переменным числом параметров(в том числе int printf(char *, ...)) Такие функции получаеют переменное число параметров любого типа, тип определяется во время выполнения. И если ты написал printf("%d", x), и при этом накормишь свою printf иксом типа char* никаких ошибок компиляции ты не получишь. А вот при выполнении ты получишь "неопределённое поведение". Т.e. может случится всё что угодно. Это по стандарту, реально в pc под gcc ты получишь просто значения указателя на строку x. Но не в коем случае так не делай, возможно на другом компе или под другим gcc у тебя отформатируется жёсткий диск :)
http://emulek.blogspot.ru/ Windows Must Die
Учебник по sed зеркало в github

Скоро придёт
Осень
Спасибо сказали:

Аватара пользователя
drBatty
Сообщения: 8735
Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
ОС: Slackware-current

Re: Порядок ввода чисел и знаков однозначен, оказывается

Сообщение drBatty » 16.10.2007 12:06

v04bvs писал(а):
16.10.2007 11:56
Я бы рекомендовал такой стиль: printf("%d", (int) c); думаю здесь всё понятно.
Я затрудняюсь сказать, является ли printf("%d", c) допустимой конструкцией по стандарту.

Допустима с точки зрения компиляции, т.е. любой компилятор обязан откомпилировать без ошибок, возможно с предупреждениями. А вот как это работать будет в стандарте не сказано, точнее сказано что может работать(или не работать) как угодно.
http://emulek.blogspot.ru/ Windows Must Die
Учебник по sed зеркало в github

Скоро придёт
Осень
Спасибо сказали:

v04bvs
Сообщения: 636
ОС: Debian GNU/Linux

Re: Порядок ввода чисел и знаков однозначен, оказывается

Сообщение v04bvs » 16.10.2007 12:10

drBatty писал(а):
16.10.2007 12:06
v04bvs писал(а):
16.10.2007 11:56
Я бы рекомендовал такой стиль: printf("%d", (int) c); думаю здесь всё понятно.
Я затрудняюсь сказать, является ли printf("%d", c) допустимой конструкцией по стандарту.

Допустима с точки зрения компиляции, т.е. любой компилятор обязан откомпилировать без ошибок, возможно с предупреждениями. А вот как это работать будет в стандарте не сказано, точнее сказано что может работать(или не работать) как угодно.




Про компиляцию понятно. Просто у меня какие то смутные ассоциации с тем, что char-ы будут в этом случае приведены к int-у автоматически. Хотя наверное я ошибаюсь.
Спасибо сказали:

Аватара пользователя
Red Gremlin
Сообщения: 445
Статус: самоучка
ОС: Rosa 2016 Fresh

Re: Порядок ввода чисел и знаков однозначен, оказывается

Сообщение Red Gremlin » 16.10.2007 12:10

drBatty писал(а):
16.10.2007 12:06
v04bvs писал(а):
16.10.2007 11:56
Я бы рекомендовал такой стиль: printf("%d", (int) c); думаю здесь всё понятно.
Я затрудняюсь сказать, является ли printf("%d", c) допустимой конструкцией по стандарту.

Допустима с точки зрения компиляции, т.е. любой компилятор обязан откомпилировать без ошибок, возможно с предупреждениями. А вот как это работать будет в стандарте не сказано, точнее сказано что может работать(или не работать) как угодно.

А работоспособность этого не связана случайно с алигментом? Обьясните, кто знает.
"В мире есть случайность, есть предопределенность и есть то, что ты планируешь совершить."
Спасибо сказали:

v04bvs
Сообщения: 636
ОС: Debian GNU/Linux

Re: Порядок ввода чисел и знаков однозначен, оказывается

Сообщение v04bvs » 16.10.2007 12:15

drBatty писал(а):
16.10.2007 12:06
v04bvs писал(а):
16.10.2007 11:56
Я бы рекомендовал такой стиль: printf("%d", (int) c); думаю здесь всё понятно.
Я затрудняюсь сказать, является ли printf("%d", c) допустимой конструкцией по стандарту.

Допустима с точки зрения компиляции, т.е. любой компилятор обязан откомпилировать без ошибок, возможно с предупреждениями. А вот как это работать будет в стандарте не сказано, точнее сказано что может работать(или не работать) как угодно.



судя по http://www.eskimo.com/~scs/cclass/int/sx11c.html я был прав, char и short автоматически приводятся к int, float приводится к double. Поэтому работать это должно без проблем.
Спасибо сказали:

sergio
Сообщения: 436
Статус: Интересующийся новичок
ОС: Debian GNU/Linux 4 & 5

Re: Порядок ввода чисел и знаков однозначен, оказывается

Сообщение sergio » 16.10.2007 13:15

v04bvs писал(а):
16.10.2007 12:10
drBatty писал(а):
16.10.2007 12:06
v04bvs писал(а):
16.10.2007 11:56
Я бы рекомендовал такой стиль: printf("%d", (int) c); думаю здесь всё понятно.
Я затрудняюсь сказать, является ли printf("%d", c) допустимой конструкцией по стандарту.

Допустима с точки зрения компиляции, т.е. любой компилятор обязан откомпилировать без ошибок, возможно с предупреждениями. А вот как это работать будет в стандарте не сказано, точнее сказано что может работать(или не работать) как угодно.


Про компиляцию понятно. Просто у меня какие то смутные ассоциации с тем, что char-ы будут в этом случае приведены к int-у автоматически. Хотя наверное я ошибаюсь.


Не будут. См. главку http://www.gnu.org/software/libc/manual/ht...w-Variadic.html макрос для работы с переменными списками, там все просто и понятно.
printf разбирает форматную строку, и для каждого спецификатора кастает указатель к указанному типу и соответствующий размер копирует в переменную, затем продвигает указатель на этот размер дальше, чтобы попасть на следующий аргумент. Если один из средних аргументов будет не соответствовать - позиционирование собьется. Если последний, то:
printf("%c", (int) c); - предполагаю, что всегда работоспособна и переносима, но выдает лажу на выходе, хотя если LE и в системе чар беззнаковый либо знаковый но значение инт укладывается в положительный чар - ответ совпадет с ожиданиями. :)
printf("%d", (char) c); - предполагаю что чистый undefined behaviour при sizeof(char) < sizeof(int), на выходе лажа, и функция будет пытаться считать со стэка больше, чем туда клали - чем это нам грозит, можно ли тут засегфолтиться и проч. - вопрос к спецам по асму. :)
Официально и то и другое undefined наверняка, и уж точно никому таких хаков не недо. :)

Последнее: GCC парсит форматную строку и контролирует типы аргументов printf и родственных ей функций. Это экстенжн его реализации. Подстраховка для невнимательных программистов. :happy:
Debian GNU/Linux 4 -- AMD Athlon64 3000+ / Asus 7600GS -- Gnome
Debian GNU/Linux 5 -- Dell (Vostro) 500 (Celeron M560 / iGM965) -- Gnome
Спасибо сказали:

v04bvs
Сообщения: 636
ОС: Debian GNU/Linux

Re: Порядок ввода чисел и знаков однозначен, оказывается

Сообщение v04bvs » 16.10.2007 19:14

sergio писал(а):
16.10.2007 13:15
v04bvs писал(а):
16.10.2007 12:10
drBatty писал(а):
16.10.2007 12:06
v04bvs писал(а):
16.10.2007 11:56
Я бы рекомендовал такой стиль: printf("%d", (int) c); думаю здесь всё понятно.
Я затрудняюсь сказать, является ли printf("%d", c) допустимой конструкцией по стандарту.

Допустима с точки зрения компиляции, т.е. любой компилятор обязан откомпилировать без ошибок, возможно с предупреждениями. А вот как это работать будет в стандарте не сказано, точнее сказано что может работать(или не работать) как угодно.


Про компиляцию понятно. Просто у меня какие то смутные ассоциации с тем, что char-ы будут в этом случае приведены к int-у автоматически. Хотя наверное я ошибаюсь.


Не будут. См. главку http://www.gnu.org/software/libc/manual/ht...w-Variadic.html макрос для работы с переменными списками, там все просто и понятно.
printf разбирает форматную строку, и для каждого спецификатора кастает указатель к указанному типу и соответствующий размер копирует в переменную, затем продвигает указатель на этот размер дальше, чтобы попасть на следующий аргумент. Если один из средних аргументов будет не соответствовать - позиционирование собьется. Если последний, то:
printf("%c", (int) c); - предполагаю, что всегда работоспособна и переносима, но выдает лажу на выходе, хотя если LE и в системе чар беззнаковый либо знаковый но значение инт укладывается в положительный чар - ответ совпадет с ожиданиями. :)
printf("%d", (char) c); - предполагаю что чистый undefined behaviour при sizeof(char) < sizeof(int), на выходе лажа, и функция будет пытаться считать со стэка больше, чем туда клали - чем это нам грозит, можно ли тут засегфолтиться и проч. - вопрос к спецам по асму. :)
Официально и то и другое undefined наверняка, и уж точно никому таких хаков не недо. :)

Последнее: GCC парсит форматную строку и контролирует типы аргументов printf и родственных ей функций. Это экстенжн его реализации. Подстраховка для невнимательных программистов. :happy:


Не поверю, пока не увижу своими глазами :) На досуге почитаю стандарт, может что интересного вычитаю.
Спасибо сказали:

Аватара пользователя
drBatty
Сообщения: 8735
Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
ОС: Slackware-current

Re: Порядок ввода чисел и знаков однозначен, оказывается

Сообщение drBatty » 16.10.2007 19:35

v04bvs писал(а):
16.10.2007 12:15
судя по http://www.eskimo.com/~scs/cclass/int/sx11c.html я был прав, char и short автоматически приводятся к int, float приводится к double. Поэтому работать это должно без проблем.

не в этом случае!
Если ты пишешь:

Код: Выделить всё

char c = 'X';
int i = c;

то всё будет нормально, и i будет равно 88(вроде).
а вот если снизу добавить

Код: Выделить всё

printf("%d",c);

получится полная фигня, которая зависит от реализации и настроек компилятора. Если хочешь узнать, посмотри в дебагере. Правила преобразований не могут работать в этом случае.
http://emulek.blogspot.ru/ Windows Must Die
Учебник по sed зеркало в github

Скоро придёт
Осень
Спасибо сказали:

v04bvs
Сообщения: 636
ОС: Debian GNU/Linux

Re: Порядок ввода чисел и знаков однозначен, оказывается

Сообщение v04bvs » 16.10.2007 19:49

Ладно, не поленился, почитал стандарт.
(6.5.2.2.6) писал(а):If the expression that denotes the called function has a type that does not include a
prototype, the integer promotions are performed on each argument, and arguments that
have type float are promoted to double. These are called the default argument
promotions.


(7.15.1.1.2) писал(а):type va_arg(va_list ap, type);

The parameter type shall be a type
name specified such that the type of a pointer to an object that has the specified type can
be obtained simply by postfixing a * to type. If there is no actual next argument, or if
type is not compatible with the type of the actual next argument (as promoted according
to the default argument promotions), the behavior is undefined
Спасибо сказали:

v04bvs
Сообщения: 636
ОС: Debian GNU/Linux

Re: Порядок ввода чисел и знаков однозначен, оказывается

Сообщение v04bvs » 16.10.2007 19:55

Вот код, в который скомпилировался char c = 1; printf("Hello world %c", c);

Код: Выделить всё

   e:   83 ec 14                sub    $0x14,%esp
  11:   c7 44 24 04 01 00 00    movl   $0x1,0x4(%esp)
  18:   00
  19:   c7 04 24 00 00 00 00    movl   $0x0,(%esp)
  20:   e8 fc ff ff ff          call   21 <main+0x21>
  25:   83 c4 14                add    $0x14,%esp

Легко видеть, что char-аргумент занимает 4 байта.
Спасибо сказали:

Аватара пользователя
drBatty
Сообщения: 8735
Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
ОС: Slackware-current

Re: Порядок ввода чисел и знаков однозначен, оказывается

Сообщение drBatty » 16.10.2007 20:01

v04bvs писал(а):
16.10.2007 19:49
Ладно, не поленился, почитал стандарт.

Да... И правда что(хотя и так понятно, данные передаются в стеке(int и тде), или в стеке сопроцессора(с пл.точкой) или указатели(для всего остального) соответственно и преобразуются). И всё равно это грязный хак.
http://emulek.blogspot.ru/ Windows Must Die
Учебник по sed зеркало в github

Скоро придёт
Осень
Спасибо сказали:

sergio
Сообщения: 436
Статус: Интересующийся новичок
ОС: Debian GNU/Linux 4 & 5

Re: Порядок ввода чисел и знаков однозначен, оказывается

Сообщение sergio » 16.10.2007 20:01

v04bvs писал(а):
16.10.2007 19:49
Ладно, не поленился, почитал стандарт.
(6.5.2.2.6) писал(а):If the expression that denotes the called function has a type that does not include a
prototype, the integer promotions are performed on each argument, and arguments that
have type float are promoted to double. These are called the default argument
promotions.


(7.15.1.1.2) писал(а):type va_arg(va_list ap, type);

The parameter type shall be a type
name specified such that the type of a pointer to an object that has the specified type can
be obtained simply by postfixing a * to type. If there is no actual next argument, or if
type is not compatible with the type of the actual next argument (as promoted according
to the default argument promotions), the behavior is undefined



Недостаточно вырезано, без контекста трудно врубиться, что именно имелось в виду в первом куске.
У функции printf прототип примерно такой:

Код: Выделить всё

printf(const char* format_string, ...)

Никакого автоприведения типов здесь быть не может, поскольку к чему приводить-то? :)
А принимающая сторона ничего не знает про вызывающую, и полагается только на форматстринг. Если написано что интеджер там - вот она указатель кастает к интеджеру, считывает значение, и продвигает указатель на sizeof(int).
Debian GNU/Linux 4 -- AMD Athlon64 3000+ / Asus 7600GS -- Gnome
Debian GNU/Linux 5 -- Dell (Vostro) 500 (Celeron M560 / iGM965) -- Gnome
Спасибо сказали:

Аватара пользователя
drBatty
Сообщения: 8735
Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
ОС: Slackware-current

Re: Порядок ввода чисел и знаков однозначен, оказывается

Сообщение drBatty » 16.10.2007 20:06

v04bvs писал(а):
16.10.2007 19:55
Вот код, в который скомпилировался char c = 1; printf("Hello world %c", c);
Легко видеть, что char-аргумент занимает 4 байта.

Да это я уже и сам понял :), какой командой снять со стека 1 байт? :)
И если напихать в стек слишком много или слишком мало, всё то же будет работать(хотя и писать фигню из стека). Если конечно это printf которая только читает, а вот если это не printf и может что-то записать, писать будет уже в стек. :mellow:
http://emulek.blogspot.ru/ Windows Must Die
Учебник по sed зеркало в github

Скоро придёт
Осень
Спасибо сказали:

Аватара пользователя
drBatty
Сообщения: 8735
Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
ОС: Slackware-current

Re: Порядок ввода чисел и знаков однозначен, оказывается

Сообщение drBatty » 16.10.2007 20:28

sergio писал(а):
16.10.2007 20:01
У функции printf прототип примерно такой:

Код: Выделить всё

printf(const char* format_string, ...)

Никакого автоприведения типов здесь быть не может, поскольку к чему приводить-то? :)

Ты совершенно прав насчёт самого C, тут другое преобразование идёт: данные преобразуются к тому, что можно комфортно снять из стека(через которые передаются параметры для функций), вот они и передаются в четырёх байтового int'а(на x86), или в стеке сопроцессора. Вот только я всегда думал, что это детали реализации, а это оказывается - стандарт(то-ли новый написали, то-ли я старый невнимательно изучал). Всё прекрасно работает(не работает), так-как сама функция данных со стека не снимает, главное в стек ничего не писать. Хотя только что писал - ничего :)
http://emulek.blogspot.ru/ Windows Must Die
Учебник по sed зеркало в github

Скоро придёт
Осень
Спасибо сказали:

sergio
Сообщения: 436
Статус: Интересующийся новичок
ОС: Debian GNU/Linux 4 & 5

Re: Порядок ввода чисел и знаков однозначен, оказывается

Сообщение sergio » 16.10.2007 20:37

drBatty писал(а):
16.10.2007 20:06
Да это я уже и сам понял :), какой командой снять со стека 1 байт? :)

А, ну вот и отгадка. =) Но если вы попутаете инт с дабл - ерунду все равно получите. =)
Кстати, при запуске с -Wall GCC выдает те самые ворнинги на несоотв-е спецификации и аргументов для int и double. Несоответствие инт и чар его не волнуют. :) (Спецификации для флоат у К.Р. вообще не существует...)



drBatty писал(а):
16.10.2007 20:28
Ты совершенно прав насчёт самого C, тут другое преобразование идёт: данные преобразуются к тому, что можно комфортно снять из стека(через которые передаются параметры для функций), вот они и передаются в четырёх байтового int'а(на x86), или в стеке сопроцессора. Вот только я всегда думал, что это детали реализации, а это оказывается - стандарт

Ага, понял. Выровняли по 4. Ограничения по выравниванию на слишком многих платформах должно быть сыскались, решили утвердить сие законодательно, чтобы упростить реализацию. ))
Debian GNU/Linux 4 -- AMD Athlon64 3000+ / Asus 7600GS -- Gnome
Debian GNU/Linux 5 -- Dell (Vostro) 500 (Celeron M560 / iGM965) -- Gnome
Спасибо сказали:

v04bvs
Сообщения: 636
ОС: Debian GNU/Linux

Re: Порядок ввода чисел и знаков однозначен, оказывается

Сообщение v04bvs » 16.10.2007 20:54

sergio писал(а):
16.10.2007 20:01
v04bvs писал(а):
16.10.2007 19:49
Ладно, не поленился, почитал стандарт.
(6.5.2.2.6) писал(а):If the expression that denotes the called function has a type that does not include a
prototype, the integer promotions are performed on each argument, and arguments that
have type float are promoted to double. These are called the default argument
promotions.


(7.15.1.1.2) писал(а):type va_arg(va_list ap, type);

The parameter type shall be a type
name specified such that the type of a pointer to an object that has the specified type can
be obtained simply by postfixing a * to type. If there is no actual next argument, or if
type is not compatible with the type of the actual next argument (as promoted according
to the default argument promotions), the behavior is undefined



Недостаточно вырезано, без контекста трудно врубиться, что именно имелось в виду в первом куске.
У функции printf прототип примерно такой:

Код: Выделить всё

printf(const char* format_string, ...)

Никакого автоприведения типов здесь быть не может, поскольку к чему приводить-то? :)
А принимающая сторона ничего не знает про вызывающую, и полагается только на форматстринг. Если написано что интеджер там - вот она указатель кастает к интеджеру, считывает значение, и продвигает указатель на sizeof(int).



Во втором куске говорится, что неопределённое поведение получается, если типы несовестимы (после promoting according to default argument promotion). В первом куске даётся определение default argument promotion.

drBatty писал(а):
16.10.2007 20:06
какой командой снять со стека 1 байт?


movb -1(%eax), %al например :)
Спасибо сказали:

sergio
Сообщения: 436
Статус: Интересующийся новичок
ОС: Debian GNU/Linux 4 & 5

Re: Порядок ввода чисел и знаков однозначен, оказывается

Сообщение sergio » 16.10.2007 21:00

v04bvs писал(а):
16.10.2007 20:54
Во втором куске говорится, что неопределённое поведение получается, если типы несовестимы (после promoting according to default argument promotion). В первом куске даётся определение default argument promotion.

Ага, разобрались, там все до сайзоф инт продвигается, видимо с целью выравнивания и упрощения.

v04bvs писал(а):
16.10.2007 20:54
drBatty писал(а):
16.10.2007 20:06
какой командой снять со стека 1 байт?

movb -1(%eax), %al например :)

Тем не менее, при обычном вызове аргументы чар передаются тоже выравненные по 4 байта (i386, дефолтные установки GCC):

Код: Выделить всё

  #include <stdio.h>


  void my(char a, char b, char c)  {
    printf("%p, %p, %p\n", & a, & b, & c);
  }


  void my2(float a, float b, float c)  {
    printf("%p, %p, %p\n", & a, & b, & c);
  }



  int main()  {

    my('a', 'b', 'c');
    my2(34.0F, 1.45346F, 6.0F);

    printf("sizeof(float): %d\n", sizeof(float));
    printf("sizeof(double): %d\n", sizeof(double));

    return  0;
  }

флоат в дабл в этом случае не двигается, а чар - по четыре. Видимо, оптимизация тогда?
Debian GNU/Linux 4 -- AMD Athlon64 3000+ / Asus 7600GS -- Gnome
Debian GNU/Linux 5 -- Dell (Vostro) 500 (Celeron M560 / iGM965) -- Gnome
Спасибо сказали:

v04bvs
Сообщения: 636
ОС: Debian GNU/Linux

Re: Порядок ввода чисел и знаков однозначен, оказывается

Сообщение v04bvs » 16.10.2007 21:09

sergio писал(а):
16.10.2007 21:00
v04bvs писал(а):
16.10.2007 20:54
drBatty писал(а):
16.10.2007 20:06
какой командой снять со стека 1 байт?

movb -1(%eax), %al например :)

Тем не менее, при обычном вызове аргументы чар передаются тоже выравненные по 4 байта (i386, дефолтные установки GCC):

Код: Выделить всё

  #include <stdio.h>


   void my(char a, char b, char c)  {
     printf("%p, %p, %p\n", & a, & b, & c);
   }


   void my2(float a, float b, float c)  {
     printf("%p, %p, %p\n", & a, & b, & c);
   }



   int main()  {

     my('a', 'b', 'c');
     my2(34.0F, 1.45346F, 6.0F);

     printf("sizeof(float): %d\n", sizeof(float));
     printf("sizeof(double): %d\n", sizeof(double));

     return  0;
   }

флоат в дабл в этом случае не двигается, а чар - по четыре. Видимо, оптимизация тогда?

Ну это уж точно implementation defined. Гарантировать то, что &b - &a == sizeof(int) конечно никто не сможет.
Спасибо сказали:

Аватара пользователя
Attila
Сообщения: 125
Статус: Тролль-Лѣсовичокъ
ОС: Свободная aka ArchLinux

Re: Порядок ввода чисел и знаков однозначен, оказывается

Сообщение Attila » 17.10.2007 03:04

Почитал стандарт (С99) про функцию fprintf.
(7.19.6.1.7) писал(а):The length modifiers and their meanings are:
hh Specifies that a following d, i, o, u, x, or X conversion specifier applies to a
signed char or unsigned char argument (the argument will have
been promoted according to the integer promotions, but its value shall be
converted to signed char or unsigned char before printing); or that
a following n conversion specifier applies to a pointer to a signed char
argument.

(7.19.6.1.9) писал(а):If a conversion specification is invalid, the behavior is undefined.239) If any argument is
not the correct type for the corresponding conversion specification, the behavior is
undefined.


То есть, по стандарту правилен такой код:

Код: Выделить всё

char c = 1;
printf("%hhd", c);
Спасибо сказали:

promov
Сообщения: 384
Статус: Участник
ОС: Debian GNU/Linux

Re: Порядок ввода чисел и знаков однозначен, оказывается

Сообщение promov » 04.11.2007 15:38

Уважаемые форумчане!
В этой поднятой мной теме первоначально речь шла вот о чём. Я константировал неожидаемую рабочесть такого кода:

Код: Выделить всё

#include <stdio.h>
int main () {
  char c;
  int a;
  printf ("Введите число типа int\n");
  scanf ("%i", & a);
  printf ("Введите какую-нибудь букву\n");
  scanf ("%c", & c);
  return 0;
}


C вашей помощью я разобрался, в чём дело, а товарищ sergio показал мне, как надо делать. Вот его код:

Код: Выделить всё

#include <stdio.h>
int main () {
  char c[2]; // !!!!!!!!!!!
  int a;
  printf ("Введите число типа int\n");
  scanf ("%i", & a);
  printf ("Введите какую-нибудь букву\n");
  scanf ("%1s", c);
  return 0;
}


Этот код рабочий. Теперь, по прошествии некоторого времени я подверг его критике. Почему обхявлять char c[2], а не char c[1]? Кольскоро sergio ответ на этот вопрос не предоставил, я попытался ответить на него сам. Вот ответ: char c[2] нужен для того, чтобы компилятор в с[1] поместил символ конца строки. (Но не для того, конечно, чтобы в с[0] ,был помещён символ '10'). А раз так, попробую объявить строку с длиной 1, а, следовательно, спецификатор %1s примет вид %s. Вот новый код:

Код: Выделить всё

#include <stdio.h>
int main () {
  int a;
  char c[1]; // !!!!!!!!!!!
  printf ("Введите число типа int\n");
  scanf ("%i", & a);
  printf ("Введите какую-нибудь букву\n");
  scanf ("%s", c);
  printf ("с [0]= %c \n", c [0]);
  return 0;
}

Он, как вы можете убедиться, вполне рабочий, в памяти сэкономлено место (объявлен char c[1], а не char c[2]), добавлен оператор вывода, благодаря которой мы видим, что действительно, строка с введена. Наконец,, можем убедиться, что все первые символы '10' действительно игнорируются командой ввода строки- для этого по приглашению ввести какую-нибудь букву достаточно несколько раз нажать на <Enter>- ни одно из этих не отобразится при выводе. И значит, наше предположение о том, что в char [2] элемент с[0] для хранения символа '10' не нужен, верно. Но!

Всё будет корректно работать, если переменные с и а будут объявлены в такой последовательности:
int a;
char c [1];
Но не наоборот! А если наоборот, то по завершении работы программы машина выдаст
Segmentation fault

А теперь, после такой долгой прелюдии вопрос: я просто придерживался правила: всякая переменная перед её инициализацией должна быть объявлена и неважно, в каком порядке и в каком месте программы такое объявление будет происходить- (сейчас неохота, честно говоря, в подтверждение своих слов приводить цитаты из учебников. )
Оказывается, порядок объявления переменных важен! Объясните, пожалуйста, кто может, происходящее!
Зачем хорёк пошел в ларёк, зачем барсук полез на сук...
Мораль легко уразуметь: зачем на бал пришёл медведь?
Спасибо сказали: