LINUX.ORG.RU

The C Programming Language. K&R

 ,


0

1

Какая-то загадка для меня происходит при выполнении упражнения 1-17 из K&R. Вот что нужно сделать:

Exercise 1-17. Write a program to print all input lines that are longer than 80 characters.

Функцию getline() я взял как есть из книги, немного подправил лишь main() вот так:

#include <stdio.h>
#include "inc/getline.h"

#define MAXLINE 1000			/* maximum input line size */
#define MINLENGTH 80

/* Exercise 1-17. Write a program to print all input lines that are
 * longer than 80 characters.
 */

int main()
{
	int len;					/* current line length */
	char line[MAXLINE];			/* current input line */

	while ((len = _getline(line, MAXLINE)) > 0) {
		printf("DBG >>>\n");
		printf("MINLENGTH = %d\n", MINLENGTH);
		printf("len = %d\n", len);
		printf("DBG <<<\n\n");
		if (len > MINLENGTH) {
			printf(line);
		}
	}
	return 0;
}

Собираю и запускаю всё это дело таким образом:

x86_64-pc-linux-gnu-gcc -g3 -Wall -Wpedantic -std=c99 ex_1-17.c inc/getline.c -o ex_1-17 && ./ex_1-17 < ex_1-17_test.txt 
DBG >>>
MINLENGTH = 80
len = 80
DBG <<<

DBG >>>
MINLENGTH = 80
len = 81
DBG <<<

this is the line which is 80 characters wide: ----------------------------------
DBG >>>
MINLENGTH = 80
len = 82
DBG <<<

this is the line which is 81 characters wide: -----------------------------------
DBG >>>
MINLENGTH = 80
len = 91
DBG <<<

this is the line which is 90 characters wide: --------------------------------------------

Файл ex_1-17_test.txt содержит такие строки:

this is the line which is 79 characters wide: ---------------------------------
this is the line which is 80 characters wide: ----------------------------------
this is the line which is 81 characters wide: -----------------------------------
this is the line which is 90 characters wide: --------------------------------------------

Я не могу понять, почему выводится строка длиной в 80 чаров, если по условию len > MINLENGTH? А также дебажная инфа изначально печатается без строки в 80 чаров, почему потом эта строка печатается?

DBG >>>
MINLENGTH = 80
len = 80
DBG <<<

DBG >>>
MINLENGTH = 80
len = 81
DBG <<<

this is the line which is 80 characters wide: ----------------------------------
DBG >>>
MINLENGTH = 80
len = 82
DBG <<<


А кто перевод строки будет считать? Кроме того, никогда не делай

printf(string);

, только

printf("%s", string);
slovazap ★★★★★ ()
Ответ на: комментарий от slovazap

А разве '\n' не входит в эти 80 чаров?

int _getline(char s[], int lim)
{
	int c, i;

	for (i = 0; i < lim - 1 && (c = getchar()) != EOF && c != '\n'; ++i)
		s[i] = c;
	if (c == '\n') {
		s[i] = c;
		++i;
	}
	s[i] = '\0';
	return i;
}
i инкрементируется же если c == '\n'

Кроме того, никогда не делай

потому что?

ubik ()
Ответ на: комментарий от ubik

Потому что если в строке есть символ процента, то ты в пролетишь как фанера над парижем.

По сабжу. Тебя просят вывести строки длиннее 80, подразумевая, что \n в эти 80 не входит. А ты выводишь с учетом разделителя. Отними один от len-а перед сравнением, и все получится. Способ форматирования отладочного выхлопа у тебя тоже пониманию не способствует.

len = 80
len = 81
this is the line which is 80 characters wide: ----------------------------------
len = 82
this is the line which is 81 characters wide: -----------------------------------
len = 91
this is the line which is 90 characters wide: --------------------------------------------

Удобнее парсить же.

arturpub ★★ ()
Ответ на: комментарий от Deleted

сравнение c с \n даёт ложь

Запутаннее и не спросишь наверное. Цикл прекращается без выполнения итератора, когда условие цикла не выполняется. Подробнее в удаленных.

arturpub ★★ ()
Ответ на: комментарий от ubik

i инкрементируется же если c == '\n'

Инкрементируется. Поэтому len для строки из 80 символов с учётом \n будет 81, поэтому она и печатается на len > MINLENGTH

slovazap ★★★★★ ()
Ответ на: комментарий от arturpub

Нет, но он явно инкрементируется в следующем

Нет, инкрементируется. Сорри что запутанно спросил, я запутанно воспринял эту конструкцию :D

Дело так в общем: цикл выполняется, пока не находит \n. Достигнув 79(80 символов, включая 0) s[i(79)] = '-'; Затем, в следующей, последней итерации в «С» у нас '\n', «i» стало 80, и тело цикла не выполняется, цикл прекращается.

А в следующей конструкции, мы сравниваем уже «с» с '\n', и в s[80] записываем '\n'. i у нас уже 80. Затем инкрементируем снова «i», и записываем терминальный ноль i=81, s = '\0'.

Так вот. Довольно необычно, по крайней мере для меня, что «i» инкрементируется когда условие в цикле ложно. Логичней было бы, чтоб дальнейшее выполнение прерывалось.

Deleted ()
Ответ на: комментарий от Deleted

что «i» инкрементируется когда условие в цикле ложно

Не инкрементируется после того, как оно стало ложным. А до — инкрементируется. Рассмотрим более простой случай:

const char *t = "abc\n"; // 'a', 'b', 'c', '\n', '\0'
int i, c;

for (int i = 0; (c = t[i]) != 0 && c != '\n'; i++) {
    s[i] = c;

Развертка:

  i = 0
l0:
  if !((c = t[i]) != 0 && c != '\n') goto l1
//if   (c = t[i]) == 0 || c == '\n'  goto l1
  s[i] = c
  i++
  goto l0
l1:

Трейс:

  i = 0
  condition is true (t[0] is 'a')
  s[0] = 'a'
  i = 1
  condition is true (t[1] is 'b')
  s[1] = 'b'
  i = 2
  condition is true (t[2] is 'c')
  s[2] = 'c'
  i = 3
  condition is FALSE (t[3] is '\n')

// i == 3, s == "abc[garbage]"
arturpub ★★ ()
Последнее исправление: arturpub (всего исправлений: 1)
Ответ на: комментарий от Deleted

Возможно тебя смущает off-by-one, кстати. Длина численно на один больше максимального индекса в ней, учитывай это при расчетах и прикидках.

There are two hard things in computer science: cache invalidation, naming things, and off-by-one errors.

arturpub ★★ ()
Ответ на: комментарий от arturpub

Ога, я протупил: если i инкрементируется после истинного сравнения, тогда как мы заносим в s[0]?))

Спасибо)

P.S.

Возможно тебя смущает off-by-one, кстати.

Неинтуитивно, по мне. У существования этого способа есть объективные причины?

Deleted ()
Ответ на: комментарий от Deleted

Это точно такой же итерационный цикл общего назначения, как и во всех других языках, ничем не отличается. Проверяем условие, выполняем тело, выполняем итератор, повторяем. Ты же не можешь в очередной (не первый) раз проверить условие, пока не выполнишь итератор, а значит при выходе итератор будет за пределами условия по определению.

Индексация с нуля тоже вполне распространенное явление. Ты просто слегка загнался, имхо.

arturpub ★★ ()
Ответ на: комментарий от arturpub

Не, я тяжело туплю и мне пора спать) Еще раз спасибо.

P.S. По поводу индексации остановился на этом: «В массивах в квадратных скобках просто пишут не адрес а _смещение_». Всё, теперь я готов укрыться одеялом. Спокойной ночи)

Deleted ()

slovazap, arturpub, парни спасибо за ответы. Я как всегда не видел общности картины.

chinarulezz, спасибо тебе тоже, хороший вопрос задал ☺

ubik ()
Ответ на: комментарий от slovazap

Ещё лучше

fputs(string, stdout);
printf нужен для форматов, для сырых строк подойдёт и простой (f)puts.

Deleted ()
Последнее исправление: romeo250501 (всего исправлений: 1)
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.