Начинаю осваивать C, появляются вопросы...

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

QWERTYASDF
Сообщения: 874
Статус: Чайник со свистком
ОС: GNU/Linux

Re: Начинаю осваивать C, появляются вопросы...

Сообщение QWERTYASDF » 13.09.2018 10:44

Вопрос не столь важен, но на всякий случай повторюсь, что когда у меня какая-нибудь getchar() в написанной программе работает, то удалять символы бэкспейсом не могу. Собственно, это и послужило одной из предпосылок задуматься над вопросом.
Спасибо сказали:

Аватара пользователя
/dev/random
Администратор
Сообщения: 4695
ОС: Gentoo

Re: Начинаю осваивать C, появляются вопросы...

Сообщение /dev/random » 13.09.2018 10:51

QWERTYASDF писала:
13.09.2018 10:44
Вопрос не столь важен, но на всякий случай повторюсь, что когда у меня какая-нибудь getchar() в написанной программе работает, то удалять символы бэкспейсом не могу. Собственно, это и послужило одной из предпосылок задуматься над вопросом.

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

#include <stdio.h>
int main() {
	int c;
	while ((c = getchar()) != EOF) {
		printf("%i\n", c);
	}
	return 0;
}
Всё спокойно удаляется, а столбец чисел появляется только после нажатия Enter или Ctrl+D.
Спасибо сказали:

QWERTYASDF
Сообщения: 874
Статус: Чайник со свистком
ОС: GNU/Linux

Re: Начинаю осваивать C, появляются вопросы...

Сообщение QWERTYASDF » 13.09.2018 11:15

/dev/random писал:
13.09.2018 10:51
Всё спокойно удаляется, а столбец чисел появляется только после нажатия Enter или Ctrl+D.
А, ну это у меня в xterm не получается убирать символы бэкспейсом. В tty оно работает. Видимо - настройки xterm...
Спасибо сказали:

Аватара пользователя
/dev/random
Администратор
Сообщения: 4695
ОС: Gentoo

Re: Начинаю осваивать C, появляются вопросы...

Сообщение /dev/random » 13.09.2018 11:22

QWERTYASDF писала:
13.09.2018 11:15
А, ну это у меня в xterm не получается убирать символы бэкспейсом. В tty оно работает. Видимо - настройки xterm...
Возможно, режим cbreak() остался от одного из ваших предыдущих экспериментов? Проверьте, будет ли удаляться, если перезапустить xterm.
Спасибо сказали:

QWERTYASDF
Сообщения: 874
Статус: Чайник со свистком
ОС: GNU/Linux

Re: Начинаю осваивать C, появляются вопросы...

Сообщение QWERTYASDF » 13.09.2018 11:25

В учебнике K&R написано, что в языке С строка символов всегда имеет на конце '\0', что это стандартный подход. Что спецификация формата %s в вызове printf предполагает, "что соответствующий аргумент будет строкой, представленной именно в такой форме". Там-же, в этом разделе есть пример программы - в массив считываются строки из входного потока, и обязательно в конец каждой ставится '\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
Как я это понимаю - printf() получает строку с 'Z' в конце, никакого '\0' там нету, и все в порядке. Т.е. это противоречит утверждению из учебника. Что я не понимаю?
Последний раз редактировалось QWERTYASDF 13.09.2018 11:28, всего редактировалось 1 раз.
Спасибо сказали:

QWERTYASDF
Сообщения: 874
Статус: Чайник со свистком
ОС: GNU/Linux

Re: Начинаю осваивать C, появляются вопросы...

Сообщение QWERTYASDF » 13.09.2018 11:27

/dev/random писал:
13.09.2018 11:22
QWERTYASDF писала:
13.09.2018 11:15
А, ну это у меня в xterm не получается убирать символы бэкспейсом. В tty оно работает. Видимо - настройки xterm...
Возможно, режим cbreak() остался от одного из ваших предыдущих экспериментов? Проверьте, будет ли удаляться, если перезапустить xterm.
Перезагрузила, тоже самое.
Спасибо сказали:

Аватара пользователя
serzh-z
Бывший модератор
Сообщения: 7724
Статус: Маньяк
ОС: Android, GNU/Linux, Windows

Re: Начинаю осваивать C, появляются вопросы...

Сообщение serzh-z » 13.09.2018 11:44

QWERTYASDF писала:
13.09.2018 11:25
Что я не понимаю?
Просто повезло. В более сложной функции оно напечатает мусор или упадет.
Scio me nihil scire.
Спасибо сказали:

Аватара пользователя
/dev/random
Администратор
Сообщения: 4695
ОС: Gentoo

Re: Начинаю осваивать C, появляются вопросы...

Сообщение /dev/random » 13.09.2018 11:46

QWERTYASDF писала:
13.09.2018 11:25
Как я это понимаю - printf() получает строку с 'Z' в конце, никакого '\0' там нету, и все в порядке. Т.е. это противоречит утверждению из учебника. Что я не понимаю?
char line[25] объявляет массив из 25 char'ов, пронумерованных от 0 до 24. Этот массив находится не в вакууме, а где-то в памяти, и сверху и снизу от него тоже память, используемая в каких-то других целях. Символы от A до Y вы записали в элементы этого массива. Символ Z - за его пределами, изуродовав чью-то ещё память. Это обычно приводит к очень трудноуловимым и проявляющимся совершенно в других местах программы багам, избегайте этого любой ценой. Но в данной программе это не успело проявиться. Далее, вы передали этот массив printf()у. printf() начал читать и выводить символ за символом, от начала массива и до символа \0. Он вывел все символы из массива (от A до Y), никакого \0 не встретил, и пошёл дальше. Вывел Z, находящийся уже за пределами массива. Прочитал следующий - и так уж получилось, что он оказался символом \0, оставшимся от кого-то ещё. Вывод остановился.
Спасибо сказали:

QWERTYASDF
Сообщения: 874
Статус: Чайник со свистком
ОС: GNU/Linux

Re: Начинаю осваивать C, появляются вопросы...

Сообщение QWERTYASDF » 13.09.2018 11:54

Спасибо. Охо-хо...Надо было внимательней учебник читать, где про массивы говорилось...

Единственно, не понятно, почему не появилось никаких ошибок на стадии компиляции. И почему без проблем можно читать память, по некоему индексу line[25], который, как оказывается, не является частью этого массива...
Спасибо сказали:

Аватара пользователя
Vascom
Сообщения: 1314
ОС: Fedora 29

Re: Начинаю осваивать C, появляются вопросы...

Сообщение Vascom » 13.09.2018 12:03

Может ты забыл добавить компилятору параметр?

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

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]);
                 ~~~~~~~^
Хотя тут и просто -O2 выдаёт эти ворнинги.
Спасибо сказали:

QWERTYASDF
Сообщения: 874
Статус: Чайник со свистком
ОС: GNU/Linux

Re: Начинаю осваивать C, появляются вопросы...

Сообщение QWERTYASDF » 13.09.2018 12:17

Хах, а 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
Бывший модератор
Сообщения: 7724
Статус: Маньяк
ОС: Android, GNU/Linux, Windows

Re: Начинаю осваивать C, появляются вопросы...

Сообщение serzh-z » 13.09.2018 12:19

QWERTYASDF писала:
13.09.2018 10:44
удалять символы бэкспейсом не могу
В драйвере терминала, когда он работает в каноническом режиме, есть своя буферизация, видимо ваш эмулятор терминала настраивает драйвер иначе, нежели тот, который у рэндома. При работе с stdio разработчика это не должно беспокоить, так как он работает через абстракцию FILE.
Scio me nihil scire.
Спасибо сказали:

Аватара пользователя
/dev/random
Администратор
Сообщения: 4695
ОС: Gentoo

Re: Начинаю осваивать C, появляются вопросы...

Сообщение /dev/random » 13.09.2018 12:40

serzh-z писал:
13.09.2018 12:19
В драйвере терминала, когда он работает в каноническом режиме, есть своя буферизация, видимо ваш эмулятор терминала настраивает драйвер иначе, нежели тот, который у рэндома. При работе с stdio разработчика это не должно беспокоить, так как он работает через абстракцию FILE.
Строго говоря, обсуждаемый getchar() тоже находится в stdio и работает через абстракцию FILE. Он является синонимом для getc(stdin). И построчной буферизацией ввода (в отличие от вывода) занимается терминал и только терминал, glibc туда лезть даже не пытается.

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

#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 ===
$ _
Никакого ввода я не нажимал.
Спасибо сказали:

Аватара пользователя
bormant
Сообщения: 1161

Re: Начинаю осваивать C, появляются вопросы...

Сообщение bormant » 13.09.2018 12:43

QWERTYASDF писала:
13.09.2018 12:17
/0
\0
Внимательнее ;)
Спасибо сказали:

Аватара пользователя
bormant
Сообщения: 1161

Re: Начинаю осваивать C, появляются вопросы...

Сообщение bormant » 13.09.2018 12:49

QWERTYASDF писала:
13.09.2018 11:54
И почему без проблем можно читать память, по некоему индексу line[25], который, как оказывается, не является частью этого массива...
Потому как line[25] это *(line+25) -- взяли указатель на начальный элемент массива, добавили к нему 25*sizeof(*line[0]), получили адрес в памяти. Что с ним делать дальше будет видно по выражению: записать туда чего-то или наоборот прочитать оттуда что-либо размером в sizeof(char).

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';
         ^
Компилятору Си обычно можно дать указание предупреждения считать ошибками (-Werror), получится примерно так же.
Спасибо сказали:

QWERTYASDF
Сообщения: 874
Статус: Чайник со свистком
ОС: GNU/Linux

Re: Начинаю осваивать C, появляются вопросы...

Сообщение QWERTYASDF » 13.09.2018 12:58

bormant писал(а):
13.09.2018 12:49
QWERTYASDF писала:
13.09.2018 11:54
И почему без проблем можно читать память, по некоему индексу line[25], который, как оказывается, не является частью этого массива...
Потому как line[25] это *(line+25) -- взяли указатель на начальный элемент массива, добавили к нему 25*sizeof(*line[0]), получили адрес в памяти. Что с ним делать дальше будет видно по выражению: записать туда чего-то или наоборот прочитать оттуда что-либо размером в sizeof(char).
Спасибо, но скажу честно - я мало из написанного понимаю, пока еще этого по моим учебникам не было...) Ну а специально сейчас заниматься этим вопросом - тоже, наверно, не есть правильно. Главное, как мне кажется, усвоено - массив объявляется с кол-м индексов ("ячеек памяти") считая от единицы, но при обращении к нему нумерация идет от нуля.
Спасибо сказали:

Аватара пользователя
/dev/random
Администратор
Сообщения: 4695
ОС: Gentoo

Re: Начинаю осваивать C, появляются вопросы...

Сообщение /dev/random » 13.09.2018 12:59

bormant писал(а):
13.09.2018 12:49
Компилятору Си обычно можно дать указание предупреждения считать ошибками (-Werror), получится примерно так же.
... в тех редких случаях, когда это можно определить на этапе компиляции, а не выполнения.
Спасибо сказали:

Аватара пользователя
Hephaestus
Сообщения: 2170
Статус: Многоуважаемый джинн...
ОС: Slackware64-14.1/14,2

Re: Начинаю осваивать C, появляются вопросы...

Сообщение Hephaestus » 13.09.2018 13:08

Vascom писал:
13.09.2018 12:03
Может ты забыл добавить компилятору параметр?
Будьте внимательней. QWERTYASDF всё-таки дама.
Пускай скрипят мои конечности.
Я - повелитель бесконечности...
Мой блог
Спасибо сказали:

Аватара пользователя
bormant
Сообщения: 1161

Re: Начинаю осваивать C, появляются вопросы...

Сообщение bormant » 13.09.2018 13:09

/dev/random писал:
13.09.2018 12:59
... в тех редких случаях, когда это можно определить на этапе компиляции, а не выполнения.
Некоторые компиляторы некоторых языков умеют самостоятельно вставлять код, проверяющий условия во время выполнения, если это включено параметром или директивой компилятора.
В Си общепринятым является расстановка assert-ов вручную. А вот есть ли у кого-то автоматические аналоги, навскидку и не скажу...
Спасибо сказали:

Аватара пользователя
/dev/random
Администратор
Сообщения: 4695
ОС: Gentoo

Re: Начинаю осваивать C, появляются вопросы...

Сообщение /dev/random » 13.09.2018 13:11

QWERTYASDF писала:
13.09.2018 12:58
массив объявляется с кол-м индексов ("ячеек памяти") считая от единицы
Массив объявляется с количеством индексов. Точка. Количество - оно количество и есть. Индексы (в зависимости от языка и прочего) могут быть от 0 до 9, от 1 до 10, от 105 до 114, от sin3 до √2, от "привет" до "пока" - но если между двумя крайними индексами есть ещё 8 штук, то количество индексов остаётся 10.
Спасибо сказали:

Аватара пользователя
Hephaestus
Сообщения: 2170
Статус: Многоуважаемый джинн...
ОС: Slackware64-14.1/14,2

Re: Начинаю осваивать C, появляются вопросы...

Сообщение Hephaestus » 13.09.2018 13:17

QWERTYASDF писала:
13.09.2018 12:58
я мало из написанного понимаю, пока еще этого по моим учебникам не было...)
Да. Работа с указателями, адресная арифметика и связанные с этим премудрости ещё впереди.
На мой взгляд это одна из самых сложных тем (может быть, самая сложная), но и самая интересная, наверно.
QWERTYASDF писала:
13.09.2018 12:58
Главное, как мне кажется, усвоено - массив объявляется с кол-м индексов ("ячеек памяти") считая от единицы, но при обращении к нему нумерация идет от нуля.
Кстати, нумерация элементов массива с нуля - это во многих ЯП так, не только в Си. А в других - с единицы.
И здесь неплохо было бы твердо усвоить разницу между максимальным индексом и количеством элементов массива. Сам иногда путаю, особенно, если доводится "переключаться" с одного языка на другой.
Пускай скрипят мои конечности.
Я - повелитель бесконечности...
Мой блог
Спасибо сказали:

Аватара пользователя
bormant
Сообщения: 1161

Re: Начинаю осваивать C, появляются вопросы...

Сообщение bormant » 13.09.2018 13:26

Хорошая мнемоника для запоминания на примере интервала: [0; N) -- от нуля до N, не включая N.
Спасибо сказали:

Аватара пользователя
Bizdelnick
Модератор
Сообщения: 14675
Статус: grammatikführer
ОС: Debian GNU/Linux

Re: Начинаю осваивать C, появляются вопросы...

Сообщение Bizdelnick » 13.09.2018 19:50

QWERTYASDF писала:
13.09.2018 11:54
И почему без проблем можно читать память, по некоему индексу line[25], который, как оказывается, не является частью этого массива...
Вот поэтому я и писал выше: массив — это указатель, что бы кто ни говорил, и что бы ни было сказано по этому поводу в стандарте. Работая с массивом, всегда думайте о нём, как об указателе, а об извлечении i-го элемента массива — как об обращении к памяти по смещению i от этого указателя, и всё встанет на свои места.
Пишите правильно:
в консоли
вкупе (с чем-либо)
в общем
вообще
в течение (часа)
команда
новичок
нюанс
приемлемо
проблема
пробовать
трафик
Спасибо сказали:

Аватара пользователя
bormant
Сообщения: 1161

Re: Начинаю осваивать C, появляются вопросы...

Сообщение bormant » 13.09.2018 22:07

Bizdelnick писал:
13.09.2018 19:50
как об обращении к памяти по смещению i от этого указателя
по смещению ( имя_массива + i*sizeof(элемент) ) (хотя это зависит от семантики операции "+" ;-) )
Bizdelnick писал:
13.09.2018 19:50
массив — это указатель, что бы кто ни говорил, и что бы ни было сказано по этому поводу в стандарте
сильно похож на указатель по месту упоминания, но нет. Почти всегда в выражении имя массива будет преобразовано в указатель на его первый элемент (это к первой части), но не всегда (этим отличается и об этом надо помнить). Переубеждать не буду, но для QWERTYASDF лишний раз обращу внимание на то, что разница есть, и компилятор о ней знает; знает ровно до тех пор, пока в выражении не выполнено преобразование массива в указатель на его первый элемент.
Последний раз редактировалось bormant 13.09.2018 22:12, всего редактировалось 1 раз.
Спасибо сказали:

QWERTYASDF
Сообщения: 874
Статус: Чайник со свистком
ОС: GNU/Linux

Re: Начинаю осваивать C, появляются вопросы...

Сообщение QWERTYASDF » 13.09.2018 22:10

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

Аватара пользователя
s.xbatob
Сообщения: 605
ОС: RfRemix

Re: Начинаю осваивать C, появляются вопросы...

Сообщение s.xbatob » 14.09.2018 14:14

serzh-z писал:
13.09.2018 09:55
QWERTYASDF
Буферизацией занимается libc. См. setbuf/setvbuf для настройки. По-умолчанию, для потоков, связанных с TTY (кроме stderr), устанавливается режим _IOLBF.
А вот и нет! Этим занимается в ядре слой tty. Отключить можно, но перед этим стоит дважды подумать, потому как с логикой stdio или iolib не сильно согласуется.
Спасибо сказали:

QWERTYASDF
Сообщения: 874
Статус: Чайник со свистком
ОС: GNU/Linux

Re: Начинаю осваивать C, появляются вопросы...

Сообщение QWERTYASDF » 23.09.2018 13:07

В K&R есть самостоятельное упражнение (1.18):
Напишите программу для удаления лишних пробелов и табуляций в хвосте каждой поступающей строки входного потока, которая бы также удаляла полностью пустые строки.
Я несколько сомневаюсь насчет того, что есть "пустые строки" - это строки, содержающие только \0 (ничего) в конце (правильно ли говорить о том, что \0 является частью строки?), или это строки, содержащие только \n на конце?
Спасибо сказали:

Аватара пользователя
devilr
Сообщения: 1566
ОС: Mandriva => Gentoo (~amd64)

Re: Начинаю осваивать C, появляются вопросы...

Сообщение devilr » 23.09.2018 13:15

А чем вас не устраивает конструкция вида
char *temp = "";
Вполне себе "пустая" строка.
P.S. Кстати, \n в конце строки вовсе не обязателен.
Мудрость приходит с возрастом.
Иногда возраст приходит один.
Спасибо сказали:

QWERTYASDF
Сообщения: 874
Статус: Чайник со свистком
ОС: GNU/Linux

Re: Начинаю осваивать C, появляются вопросы...

Сообщение QWERTYASDF » 23.09.2018 13:21

Да меня то устраивает. Просто, насколько сейчас понимаю, "пустую строку" можно трактовать по-разному. Или я ошибаюсь и все однозначно. В этом и вопрос.
Спасибо сказали:

Аватара пользователя
serzh-z
Бывший модератор
Сообщения: 7724
Статус: Маньяк
ОС: Android, GNU/Linux, Windows

Re: Начинаю осваивать C, появляются вопросы...

Сообщение serzh-z » 23.09.2018 13:30

QWERTYASDF писала:
23.09.2018 13:07
правильно ли говорить о том, что \0 является частью строки?
Нуль является не более чем признаком конца строки, применительно к последовательности байт, и частью этой последовательности (но не строки).

Буфер, последовательность байт - это контейнер со служебной информацией в начале (в случае использования распределителя памяти типа malloc) и конце (нуль). Строка - текстовые данные в этом контейнере.
Scio me nihil scire.
Спасибо сказали: