Начинаю осваивать C, появляются вопросы...
Модератор: Модераторы разделов
-
- Сообщения: 989
- Статус: Чайник со свистком
- ОС: GNU/Linux
Re: Начинаю осваивать C, появляются вопросы...
Вопрос не столь важен, но на всякий случай повторюсь, что когда у меня какая-нибудь getchar() в написанной программе работает, то удалять символы бэкспейсом не могу. Собственно, это и послужило одной из предпосылок задуматься над вопросом.
- /dev/random
- Администратор
- Сообщения: 5289
- ОС: Gentoo
Re: Начинаю осваивать C, появляются вопросы...
QWERTYASDF писала: ↑13.09.2018 10:44Вопрос не столь важен, но на всякий случай повторюсь, что когда у меня какая-нибудь getchar() в написанной программе работает, то удалять символы бэкспейсом не могу. Собственно, это и послужило одной из предпосылок задуматься над вопросом.
Код: Выделить всё
#include <stdio.h>
int main() {
int c;
while ((c = getchar()) != EOF) {
printf("%i\n", c);
}
return 0;
}
Спасибо сказали:
-
- Сообщения: 989
- Статус: Чайник со свистком
- ОС: GNU/Linux
Re: Начинаю осваивать C, появляются вопросы...
А, ну это у меня в xterm не получается убирать символы бэкспейсом. В tty оно работает. Видимо - настройки xterm.../dev/random писал: ↑13.09.2018 10:51Всё спокойно удаляется, а столбец чисел появляется только после нажатия Enter или Ctrl+D.
- /dev/random
- Администратор
- Сообщения: 5289
- ОС: Gentoo
Re: Начинаю осваивать C, появляются вопросы...
Возможно, режим cbreak() остался от одного из ваших предыдущих экспериментов? Проверьте, будет ли удаляться, если перезапустить xterm.QWERTYASDF писала: ↑13.09.2018 11:15А, ну это у меня в xterm не получается убирать символы бэкспейсом. В tty оно работает. Видимо - настройки xterm...
-
- Сообщения: 989
- Статус: Чайник со свистком
- ОС: GNU/Linux
Re: Начинаю осваивать C, появляются вопросы...
В учебнике K&R написано, что в языке С строка символов всегда имеет на конце '\0', что это стандартный подход. Что спецификация формата %s в вызове printf предполагает, "что соответствующий аргумент будет строкой, представленной именно в такой форме". Там-же, в этом разделе есть пример программы - в массив считываются строки из входного потока, и обязательно в конец каждой ставится '\0' - что выразительно должно подтверждать данное утверждение. Допустим, я хочу проверить экспериментально так ли это. Например так:
Получаю на выходе:
Как я это понимаю - printf() получает строку с 'Z' в конце, никакого '\0' там нету, и все в порядке. Т.е. это противоречит утверждению из учебника. Что я не понимаю?
Код: Выделить всё
#include <stdio.h>
int main()
{
int i;
char c = 'A';
char line[25];
for (i=0; i<26; ++i)
line[i] = c + i;
printf("%s\n", line);
printf("%d\n", line[25]);
return 0;
}
Код: Выделить всё
$./a.out
ABCDEFGHIJKLMNOPQRSTUVWXYZ
90
Последний раз редактировалось QWERTYASDF 13.09.2018 11:28, всего редактировалось 1 раз.
-
- Сообщения: 989
- Статус: Чайник со свистком
- ОС: GNU/Linux
Re: Начинаю осваивать C, появляются вопросы...
Перезагрузила, тоже самое./dev/random писал: ↑13.09.2018 11:22Возможно, режим cbreak() остался от одного из ваших предыдущих экспериментов? Проверьте, будет ли удаляться, если перезапустить xterm.QWERTYASDF писала: ↑13.09.2018 11:15А, ну это у меня в xterm не получается убирать символы бэкспейсом. В tty оно работает. Видимо - настройки xterm...
- serzh-z
- Бывший модератор
- Сообщения: 8259
- Статус: Маньяк
- ОС: Arch, Fedora, Ubuntu
- Контактная информация:
Re: Начинаю осваивать C, появляются вопросы...
Просто повезло. В более сложной функции оно напечатает мусор или упадет.
Спасибо сказали:
- /dev/random
- Администратор
- Сообщения: 5289
- ОС: Gentoo
Re: Начинаю осваивать C, появляются вопросы...
char line[25] объявляет массив из 25 char'ов, пронумерованных от 0 до 24. Этот массив находится не в вакууме, а где-то в памяти, и сверху и снизу от него тоже память, используемая в каких-то других целях. Символы от A до Y вы записали в элементы этого массива. Символ Z - за его пределами, изуродовав чью-то ещё память. Это обычно приводит к очень трудноуловимым и проявляющимся совершенно в других местах программы багам, избегайте этого любой ценой. Но в данной программе это не успело проявиться. Далее, вы передали этот массив printf()у. printf() начал читать и выводить символ за символом, от начала массива и до символа \0. Он вывел все символы из массива (от A до Y), никакого \0 не встретил, и пошёл дальше. Вывел Z, находящийся уже за пределами массива. Прочитал следующий - и так уж получилось, что он оказался символом \0, оставшимся от кого-то ещё. Вывод остановился.QWERTYASDF писала: ↑13.09.2018 11:25Как я это понимаю - printf() получает строку с 'Z' в конце, никакого '\0' там нету, и все в порядке. Т.е. это противоречит утверждению из учебника. Что я не понимаю?
Спасибо сказали:
-
- Сообщения: 989
- Статус: Чайник со свистком
- ОС: GNU/Linux
Re: Начинаю осваивать C, появляются вопросы...
Спасибо. Охо-хо...Надо было внимательней учебник читать, где про массивы говорилось...
Единственно, не понятно, почему не появилось никаких ошибок на стадии компиляции. И почему без проблем можно читать память, по некоему индексу line[25], который, как оказывается, не является частью этого массива...
Единственно, не понятно, почему не появилось никаких ошибок на стадии компиляции. И почему без проблем можно читать память, по некоему индексу line[25], который, как оказывается, не является частью этого массива...
Re: Начинаю осваивать C, появляются вопросы...
Может ты забыл добавить компилятору параметр?
Хотя тут и просто -O2 выдаёт эти ворнинги.
Код: Выделить всё
-Wall
Код: Выделить всё
g++ -O2 -Wall test.cpp
test.cpp: В функции «int main()»:
test.cpp:10:21: предупреждение: iteration 25 invokes undefined behavior [-Waggressive-loop-optimizations]
line[i] = c + i;
~~~~~~~~^~~~~~~
test.cpp:9:13: замечание: within this loop
for (i=0; i<26; ++i)
~^~~
test.cpp:13:24: предупреждение: array subscript 25 is above array bounds of «char [25]» [-Warray-bounds]
printf("%d\n", line[25]);
~~~~~~~^
Спасибо сказали:
-
- Сообщения: 989
- Статус: Чайник со свистком
- ОС: GNU/Linux
Re: Начинаю осваивать C, появляются вопросы...
Хах, а printf(), получается, не найдя /0 в последнем индексе массива, идет дальше и дальше, пока его где-то не найдет. Сейчас поэксперементировала - каждый раз строка, содержащаяся в массиве выдается корректно, но дальше идут некие произвольные в моем понимании символы, после которых видимо имеется в памяти /0. Например:
Код: Выделить всё
#include <stdio.h>
int main()
{
int i;
char c = 'A';
char line[50];
for (i=0; i<50; ++i)
line[i] = c + i;
printf("%s\n", line);
printf("%d\n", line[49]);
}
Код: Выделить всё
$./a.out
ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrA2
114
Последний раз редактировалось QWERTYASDF 13.09.2018 12:19, всего редактировалось 1 раз.
- serzh-z
- Бывший модератор
- Сообщения: 8259
- Статус: Маньяк
- ОС: Arch, Fedora, Ubuntu
- Контактная информация:
Re: Начинаю осваивать C, появляются вопросы...
В драйвере терминала, когда он работает в каноническом режиме, есть своя буферизация, видимо ваш эмулятор терминала настраивает драйвер иначе, нежели тот, который у рэндома. При работе с stdio разработчика это не должно беспокоить, так как он работает через абстракцию FILE.
Спасибо сказали:
- /dev/random
- Администратор
- Сообщения: 5289
- ОС: Gentoo
Re: Начинаю осваивать C, появляются вопросы...
Строго говоря, обсуждаемый getchar() тоже находится в stdio и работает через абстракцию FILE. Он является синонимом для getc(stdin). И построчной буферизацией ввода (в отличие от вывода) занимается терминал и только терминал, glibc туда лезть даже не пытается.serzh-z писал: ↑13.09.2018 12:19В драйвере терминала, когда он работает в каноническом режиме, есть своя буферизация, видимо ваш эмулятор терминала настраивает драйвер иначе, нежели тот, который у рэндома. При работе с stdio разработчика это не должно беспокоить, так как он работает через абстракцию FILE.
Код: Выделить всё
#include <stdio.h>
int main() {
int i;
scanf("%i", &i);
printf("\n=== %i ===\n", i);
return 0;
}
Shell
$ stty cbreak; ./a.out; stty -cbreak
<нажимаю 1 2 3 пробел>
123
=== 123 ===
$ _
Спасибо сказали:
Re: Начинаю осваивать C, появляются вопросы...
Потому как line[25] это *(line+25) -- взяли указатель на начальный элемент массива, добавили к нему 25*sizeof(*line[0]), получили адрес в памяти. Что с ним делать дальше будет видно по выражению: записать туда чего-то или наоборот прочитать оттуда что-либо размером в sizeof(char).QWERTYASDF писала: ↑13.09.2018 11:54И почему без проблем можно читать память, по некоему индексу line[25], который, как оказывается, не является частью этого массива...
PS. Более строго относящиеся к типам языки есть, и их немало:
Код: Выделить всё
var line: array [0..24] of Char;
begin
line[25]:='Z';
end.
Код: Выделить всё
a.pas(3): Error 76: Constant out of range.
line[25]:='Z';
^
Спасибо сказали:
-
- Сообщения: 989
- Статус: Чайник со свистком
- ОС: GNU/Linux
Re: Начинаю осваивать C, появляются вопросы...
Спасибо, но скажу честно - я мало из написанного понимаю, пока еще этого по моим учебникам не было...) Ну а специально сейчас заниматься этим вопросом - тоже, наверно, не есть правильно. Главное, как мне кажется, усвоено - массив объявляется с кол-м индексов ("ячеек памяти") считая от единицы, но при обращении к нему нумерация идет от нуля.bormant писал(а): ↑13.09.2018 12:49Потому как line[25] это *(line+25) -- взяли указатель на начальный элемент массива, добавили к нему 25*sizeof(*line[0]), получили адрес в памяти. Что с ним делать дальше будет видно по выражению: записать туда чего-то или наоборот прочитать оттуда что-либо размером в sizeof(char).QWERTYASDF писала: ↑13.09.2018 11:54И почему без проблем можно читать память, по некоему индексу line[25], который, как оказывается, не является частью этого массива...
- /dev/random
- Администратор
- Сообщения: 5289
- ОС: Gentoo
- Hephaestus
- Сообщения: 3729
- Статус: Многоуважаемый джинн...
- ОС: Slackware64-14.1/14.2
- Контактная информация:
Re: Начинаю осваивать C, появляются вопросы...
Будьте внимательней. QWERTYASDF всё-таки дама.
Re: Начинаю осваивать C, появляются вопросы...
Некоторые компиляторы некоторых языков умеют самостоятельно вставлять код, проверяющий условия во время выполнения, если это включено параметром или директивой компилятора./dev/random писал: ↑13.09.2018 12:59... в тех редких случаях, когда это можно определить на этапе компиляции, а не выполнения.
В Си общепринятым является расстановка assert-ов вручную. А вот есть ли у кого-то автоматические аналоги, навскидку и не скажу...
- /dev/random
- Администратор
- Сообщения: 5289
- ОС: Gentoo
Re: Начинаю осваивать C, появляются вопросы...
Массив объявляется с количеством индексов. Точка. Количество - оно количество и есть. Индексы (в зависимости от языка и прочего) могут быть от 0 до 9, от 1 до 10, от 105 до 114, от sin3 до √2, от "привет" до "пока" - но если между двумя крайними индексами есть ещё 8 штук, то количество индексов остаётся 10.QWERTYASDF писала: ↑13.09.2018 12:58массив объявляется с кол-м индексов ("ячеек памяти") считая от единицы
- Hephaestus
- Сообщения: 3729
- Статус: Многоуважаемый джинн...
- ОС: Slackware64-14.1/14.2
- Контактная информация:
Re: Начинаю осваивать C, появляются вопросы...
Да. Работа с указателями, адресная арифметика и связанные с этим премудрости ещё впереди.QWERTYASDF писала: ↑13.09.2018 12:58я мало из написанного понимаю, пока еще этого по моим учебникам не было...)
На мой взгляд это одна из самых сложных тем (может быть, самая сложная), но и самая интересная, наверно.
Кстати, нумерация элементов массива с нуля - это во многих ЯП так, не только в Си. А в других - с единицы.QWERTYASDF писала: ↑13.09.2018 12:58Главное, как мне кажется, усвоено - массив объявляется с кол-м индексов ("ячеек памяти") считая от единицы, но при обращении к нему нумерация идет от нуля.
И здесь неплохо было бы твердо усвоить разницу между максимальным индексом и количеством элементов массива. Сам иногда путаю, особенно, если доводится "переключаться" с одного языка на другой.
Re: Начинаю осваивать C, появляются вопросы...
Хорошая мнемоника для запоминания на примере интервала: [0; N) -- от нуля до N, не включая N.
- Bizdelnick
- Модератор
- Сообщения: 20792
- Статус: nulla salus bello
- ОС: Debian GNU/Linux
Re: Начинаю осваивать C, появляются вопросы...
Вот поэтому я и писал выше: массив — это указатель, что бы кто ни говорил, и что бы ни было сказано по этому поводу в стандарте. Работая с массивом, всегда думайте о нём, как об указателе, а об извлечении i-го элемента массива — как об обращении к памяти по смещению i от этого указателя, и всё встанет на свои места.QWERTYASDF писала: ↑13.09.2018 11:54И почему без проблем можно читать память, по некоему индексу line[25], который, как оказывается, не является частью этого массива...
Пишите правильно:
в консоли вку́пе (с чем-либо) в общем вообще | в течение (часа) новичок нюанс по умолчанию | приемлемо проблема пробовать трафик |
Спасибо сказали:
Re: Начинаю осваивать C, появляются вопросы...
по смещению ( имя_массива + i*sizeof(элемент) ) (хотя это зависит от семантики операции "+" ;-) )
сильно похож на указатель по месту упоминания, но нет. Почти всегда в выражении имя массива будет преобразовано в указатель на его первый элемент (это к первой части), но не всегда (этим отличается и об этом надо помнить). Переубеждать не буду, но для QWERTYASDF лишний раз обращу внимание на то, что разница есть, и компилятор о ней знает; знает ровно до тех пор, пока в выражении не выполнено преобразование массива в указатель на его первый элемент.Bizdelnick писал: ↑13.09.2018 19:50массив — это указатель, что бы кто ни говорил, и что бы ни было сказано по этому поводу в стандарте
Последний раз редактировалось bormant 13.09.2018 22:12, всего редактировалось 1 раз.
-
- Сообщения: 989
- Статус: Чайник со свистком
- ОС: GNU/Linux
Re: Начинаю осваивать C, появляются вопросы...
Спасибо еще раз. Но повторюсь - важные выводы мною сделаны, а про указатели сотоварищи мне пока рано голову забивать...)
Re: Начинаю осваивать C, появляются вопросы...
А вот и нет! Этим занимается в ядре слой tty. Отключить можно, но перед этим стоит дважды подумать, потому как с логикой stdio или iolib не сильно согласуется.serzh-z писал: ↑13.09.2018 09:55QWERTYASDF
Буферизацией занимается libc. См. setbuf/setvbuf для настройки. По-умолчанию, для потоков, связанных с TTY (кроме stderr), устанавливается режим _IOLBF.
-
- Сообщения: 989
- Статус: Чайник со свистком
- ОС: GNU/Linux
Re: Начинаю осваивать C, появляются вопросы...
В K&R есть самостоятельное упражнение (1.18):
Я несколько сомневаюсь насчет того, что есть "пустые строки" - это строки, содержающие только \0 (ничего) в конце (правильно ли говорить о том, что \0 является частью строки?), или это строки, содержащие только \n на конце?Напишите программу для удаления лишних пробелов и табуляций в хвосте каждой поступающей строки входного потока, которая бы также удаляла полностью пустые строки.
Re: Начинаю осваивать C, появляются вопросы...
А чем вас не устраивает конструкция вида
P.S. Кстати, \n в конце строки вовсе не обязателен.
Вполне себе "пустая" строка.char *temp = "";
P.S. Кстати, \n в конце строки вовсе не обязателен.
-
- Сообщения: 989
- Статус: Чайник со свистком
- ОС: GNU/Linux
Re: Начинаю осваивать C, появляются вопросы...
Да меня то устраивает. Просто, насколько сейчас понимаю, "пустую строку" можно трактовать по-разному. Или я ошибаюсь и все однозначно. В этом и вопрос.
- serzh-z
- Бывший модератор
- Сообщения: 8259
- Статус: Маньяк
- ОС: Arch, Fedora, Ubuntu
- Контактная информация:
Re: Начинаю осваивать C, появляются вопросы...
Нуль является не более чем признаком конца строки, применительно к последовательности байт, и частью этой последовательности (но не строки).
Буфер, последовательность байт - это контейнер со служебной информацией в начале (в случае использования распределителя памяти типа malloc) и конце (нуль). Строка - текстовые данные в этом контейнере.
Спасибо сказали: