1

Тема: Рахунок слів. Мова С.(Kernigan&Ritchie)

Доброго дня, шановні.

Нещодавно, за порадою знайомого, почав вивчати мову програмування С за допомогою відеоуроків на Youtube та придбаної книги Kernigan & Ritchie.
Трошки переглянув цей форум і зрозумів, що шукати тут ментора безглуздо, тож просто буду задавати свої питання.

Тож, питання перше. Є программа, яка рахує введені лінії, слова та символи:

#include <stdio.h>

#define IN 1    
#define OUT 0    

main()
{
    int с, nl, nw, nc, state;

    state = OUT;

    nl = nw = nc = 0;

    while ((с = getchar()) != EOF) {
        ++nc;
        if (c == '\n')
            ++nl;
        if (c == ' ' || c == '\n' || c == '\t')
            state = OUT;
        else if (state == OUT) {
            state = IN;
            ++nw;
        }
    }
    printf(“%d %d %d\n”, nl, nw, nc);
}

Отож саме питання: я не зовсім розумію, що конкретно виконує змінна state. Вона просто приймає значення 0 і 1 в залежності від обставин? Просто розділяє випадки?
Також я не дуже розумію вираз "else if (state == OUT)". В чому різниця між "state = OUT" і "state == OUT"?

2

Re: Рахунок слів. Мова С.(Kernigan&Ritchie)

MisterSun написав:

Просто розділяє випадки?

Саме так. Визначає, цикл зараз всередині слова чи зовні. Фактично функція лічить зміни станів з OUT на IN, тобто "входи" в слова - очевидно, їх стільки ж, скільки й слів.

MisterSun написав:

В чому різниця між "state = OUT" і "state == OUT"?

a == b - порівняння. Якщо значення a та b однакові, повертає ненульове значення (істину), якщо різні - нульове (неправду), що й використовується в if.

if(age==100)
  printf("Вітаю, дідусю!");

a = b - присвоювання, замінює значення a на b.

a = 3;//значення a == 3
a = 5;//значення a == 5
printf("a = %d", a); //виведе значення a на цей момент, тобто 5

В C присвоювання також повертає присвоєне значення, яке можна використовувати:

a = b = 5;//те саме, що й a = (b=5) - спершу b стає рівним 5, а потім a теж.
a = (b = 3) + 1;//a==4, b==3

Хоча зараз загалом вважається дурним тоном так робити.
Ну і можна робити так:

a = a + 1;//збільшити a на 1; ясно, чому?

Цей вираз настільки часто використовується, що має альтернативні форми

a += 1; // те саме, що й a = a + 1;
++a; // те саме
a++; // а отут складніше: a збільшується, але значення всього цього виразу - СТАРЕ значення a

Відповідно,

if(age=100)
  printf("Вітаю, дідусю!");

можна умовно переписати як

age=100;
if(age)
  printf("Вітаю, дідусю!");

А оскільки ненульове значення розглядається як істинне, то цей код завжди виведе "Вітаю, дідусю!".

І так, ця потенційна помилка (плутанина = та ==) настільки дістала програмістів, що багато хто радить використовувати "Йода-нотацію" (на честь магістра Йоди), де в порівняннях одна змінна пишеться праворуч:

if(100==age)
  printf("Вітаю, дідусю!");

Тут вже = і == не переплутати, вираз 100=age не має сенсу.

Подякували: MisterSun1

3

Re: Рахунок слів. Мова С.(Kernigan&Ritchie)

В чому різниця між "state = OUT" і "state == OUT"?

В тому, що «=» здійснює присвоєння (записує в змінну state значення OUT), а «==» — порівняння (якщо state дорівнює OUT, повертає 1, інакше повертає 0, сама змінна state при цьому не змінюється). Вирази з «==» зазвичай використовуються в заголовках конструкцій if, while — проте, треба бути уважним, бо синтаксис мови дозволяє використовувати в тих місцях і «=», яке зробить зовсім не те, і програма це спокійно проковтне, вважаючи будь-яке ненульове значення істиною, а 0 — хибністю.
Тому, наприклад, такий фрагмент коду надрукує нулик і на цьому завершиться:

int i=0;
do  {
    printf("%d\n", i);
    } while(i==1); // Порівняння

а такий надрукує нулик і нескінченну кількість одиниць:

int i=0;
do  {
    printf("%d\n", i);
    } while(i=1); // Присвоєння замість порівняння!!!
Подякували: MisterSun1

4

Re: Рахунок слів. Мова С.(Kernigan&Ritchie)

Дякую за відповіді. Здається я зрозумів, але хотів би зробити деякі уточнення.

У нас є функція if

if (c == ' ' || c == '\n' || c == '\t')
            state = OUT;

Якщо її аргументи правдиві, то ми кажемо, що state = OUT, а тобто дорівнює нулю і завершуємо цикл. Але навіщо ми кажемо, що state = OUT, якщо в нас вже було попередньо зазначено, що state = OUT? Яким чином программа на це реагує? В принципі, в нас же ніяких видими змін не відбувається.

І далі, якщо наш if невірний, то ми переходимо до else if

else if (state == OUT) {
            state = IN;
            ++nw;

Тут ми порівнюємо значення state зі значенням константи OUT. Так як ми вже визначили на початку коду, що state=OUT=0, тоді вислів state == OUT - істина і ми виконуємо тіло функції.

5

Re: Рахунок слів. Мова С.(Kernigan&Ritchie)

Ви пропустили ключовий момент - це все відбувається в циклі

 while ((с = getchar()) != EOF) {

Тіло циклу буде виконано стільки разів, скільки символів уведено.
Змінна state означає поточний стан: ми знаходимося в слові чи за його межами. Невдала назва лише плутає, правда?
Отже, спершу ми за межами слова (бо слово не може початися до того, як щось було введене). Далі вводимо символ; якщо цей символ - розрив, то ми вийшли зі слова (незалежно від того, було в слові до того); якщо цей символ - частина слова (гілка else), то якщо ми були в слові, нам нічого не треба змінювати, а от якщо ми були за словом і "увійшли" в слово, то виконається блок (а не тіло функції, тут немає функції).

Я б на вашому місці почав з простіших задач - спершу на змінні, потім на цикли. Обробка стрічок - це вже трохи складніше.

Подякували: MisterSun1

6

Re: Рахунок слів. Мова С.(Kernigan&Ritchie)

Все, я зрозумів. Щиро дякую за відповіді! Просто в самій книзі дуже мало розписано пояснень до коду котрий там пропонується

7

Re: Рахунок слів. Мова С.(Kernigan&Ritchie)

K&R - класика, звісно, але боюся, не для зелених новачків.

8

Re: Рахунок слів. Мова С.(Kernigan&Ritchie)

Насправді я з вами погоджуся, адже вже з 1.5.4 глави стає все менш і менш зрозумілим пояснення і коди. Чи могли б ви щось порадити?

9

Re: Рахунок слів. Мова С.(Kernigan&Ritchie)

Навіть гублюся. C - мова не для новачків, звідси й проблема. Хоча дехто так і не вважає.

10

Re: Рахунок слів. Мова С.(Kernigan&Ritchie)

Я не з тих, хто пасує перед складнощами. Я вже почав, тож закінчу. Хоча питань буде - тьма тьмуща(:

11

Re: Рахунок слів. Мова С.(Kernigan&Ritchie)

То й чудово, форум наповнюватиметься.

12

Re: Рахунок слів. Мова С.(Kernigan&Ritchie)

koala написав:

Навіть гублюся. C - мова не для новачків, звідси й проблема. Хоча дехто так і не вважає.

Ну чому ж, мова як мова, з широкими можливостями прострелити собі ногу (в тому сенсі, що дозволяє широкий спектр «тихих» помилок), але синтаксично доволі проста. В моєму випадку — перша мова програмування, яку я вчив (щоправда, теоретично, комп'ютера тоді не було). Можливо, ще від підручника залежить, у мене був не Керніган та Рітчі, а Прата (і ще пара авторів) — там ніби все зрозуміло написано, як для новачка.

Подякували: leofun01, MisterSun2