2016-06-18 17 views
0

Я начинаю читать книгу K & R Программирование C (2-е издание). И я застрял на 1.6-массиве; Я просто не могу понять, что делает код (даже если он говорит, что он подсчитывает цифры, пробелы и другие!). Вот код:K & R 1.6 Array. Не понимаю код

#include <stdio.h> 
/* count digits, white space, others */ 
main() 
{ 
    int c, i, nwhite, nother; 
    int ndigit[10]; 
    nwhite = nother = 0; 
    for (i = 0; i < 10; ++i) 
     ndigit[i] = 0; 
    while ((c = getchar()) != EOF) 
     if (c >= '0' && c <= '9') 
      ++ndigit[c-'0']; 
     else if (c == ' ' || c == '\n' || c == '\t') 
      ++nwhite; 
     else 
      ++nother; 
    printf("digits ="); 
    for (i = 0; i < 10; ++i) 
     printf(" %d", ndigit[i]); 
    printf(", white space = %d, other = %d\n",nwhite, nother); 
} 

Итак, сначала он определяет Целые числа, (c, i, nwhite, nother); После того, что он создает массив из 10 цифр, (0 -9) После этого он устанавливает Nwhite и ругой 0.

для петель установить I к 0, я < 10 означает, что если его ниже, добавьте я = i + 1. ndigit [i] = 0? Я не совсем понимаю это, разве я уже 0?

в то время как ((с = GetChar()! = EOF) означает, что когда-либо вход и разве в конце файла ?.
После этой части я вроде заблудились, и я не уверен, что

if (c >= '0' && c <= '9') 
++ndigit[c-'0']; 

ли вообще

И я не совсем понимаю, почему для (я = 0; я < 10; + = я).. Повторяю я понимаю английский язык, но некоторое дорогое использование слов будет путайте меня, поэтому, если вы не возражаете, пожалуйста, сохраните это для меня. Я действительно надеюсь, что есть кто-то, кто может помочь мне понять этот код на 100%. все, кто хочет программиста, который не может даже понять код? :)

+0

Этот цикл повторяется, потому что первый раз он обнуляет цифры, а второй раз печатает подсчеты, скопированные по строке '++ ndigit [c-'0 '];' И что * эта * строка 'if (c> = '0' && c <= '9') ++ ndigit [c-'0 ']; 'is, is, чтобы проверить, был ли введенный символ фактически цифрой, если так считать его вхождения, ASCII, чтобы он мог правильно индексировать массив. –

+0

Я все еще смущен. Ehm Итак, что вы вкладываете в терминал, он переходит к строке if (c> = '0' && c <= '9')? И если он на самом деле является целым числом или символом (ASCII), он перейдет к следующей строке, которая равна ++ ndigit [c-'0] и превратит этот ASCII int или символ в фактический int и добавит его в массив ?:) Я совершенно новичок в C, так что извините, если я звук реального noobish. –

+0

Лучше всего работать через великолепный ответ от @thurizas шаг за шагом. –

ответ

5

Пройдем через код и посмотрим, что происходит.

main() 
{ 
    int c, i, nwhite, nother; 
    int ndigit[10]; 
    nwhite = nother = 0; 

В первой первой строке кода мы объявляющего [0] (в компиляторе), что c, i, nwhite и nother будет целочисленные переменные. На данный момент, пока мы объявили эти переменные, мы не дали им никакой ценности.

В следующей строке мы объявляем, что ndigit будет массивом из 10 целых чисел, опять же никакой инициализации не происходит, поэтому мы не имеем представления о том, каково значение этих десяти целых чисел.

В третьей строке мы определяем nwhite и nother равными нулю, другими словами, мы инициализируем их до некоторого значения.

for (i = 0; i < 10; ++i) 
     ndigit[i] = 0; 

В этом цикле мы инициализируем переменную i равным нулю, и мы будем увеличивать его один раз когда-либо через петлю, пока значение станет десять или больше. Тело цикла устанавливает каждый элемент массива в ноль. Это общий c-idiom для инициализации элементов массива.

while ((c = getchar()) != EOF) 
    { 
     if (c >= '0' && c <= '9') 
      ++ndigit[c-'0']; 
     else if (c == ' ' || c == '\n' || c == '\t') 
      ++nwhite; 
     else 
      ++nother; 
    } 

Следующий блок кода делает фактический подсчет. Хотя код в K & R синтаксически правильный, я предпочитаю включать кодировку цикла while с фигурными фигурными скобками, мне становится легче читать, но это личное дело [1].

Условие цикла while ((c = getchar()) != EOF), может быть путаным. Сначала мы выполняем операцию в скобках, которая равна c = getchar(), что приводит к получению следующего символа и присвоению его переменной c. (помните, что в C символ (т. е. переменная типа char) - это просто небольшое целое число, поэтому мы можем назначить тип символа целочисленному типу).Оператор присваивания имеет возвращаемое значение [2], в котором он возвращает значение в правой части оператора присваивания, поэтому операция в скобках возвращает значение getchar(), которое затем сравнивается с EOF, а если оно не равным EOF, мы вводим тело оператора while.

Первый оператор if проверяет, является ли символ числом. В ASCII число имеет значение 0x30 ('0') через 0x39 ('9'), поэтому мы проверяем, находится ли символ в этом диапазоне. Если это так, мы увеличиваем соответствующее значение в массиве ndigit. Например, предположим, что мы прочитали символ «5», который имеет значение ASCII 0x35. Поскольку 0x35 находится между 0x30 и 0x39, мы имеем цифру. Выполнение вычитания c - '0' эквивалентно 0x35 - 0x30, которое равно 0x05. Затем мы используем это как индекс в массиве и увеличиваем соответствующее значение с помощью ++ndigit[c-'0'].

Следующая ветка if-блока проверяет, является ли c пробелом, то есть выражение c == ' ' || c == '\n' || c == '\t' проверить, является ли c пробелом или c является новой строкой, или если c является вкладкой , Если c является одним из этих символов, тогда мы получаем increment nwhite.

И, наконец, ветвь else берется, если у нас нет цифры или пробела, и затем мы увеличиваем число.

printf("digits ="); 
    for (i = 0; i < 10; ++i) 
     printf(" %d", ndigit[i]); 
    printf(", white space = %d, other = %d\n", nwhite, nother); 
} 

Последний бит кода просто распечатывает результаты. Поскольку мы хотим посмотреть все десять элементов массива ndigit, нам нужно снова пройти через массив, чтобы мы использовали структуру for loop для просмотра каждого элемента массива.

Надеюсь, это прояснит некоторые вещи. Что-то, что вы можете попробовать, это изменить этот код, чтобы он учитывал буквы, которые появляются и на входе. Сначала попробуйте подсчитать буквы, не учитывая случай, а затем посмотрите, можете ли вы считать буквы верхнего и нижнего регистра.

примечания: [0] Объявление переменной просто указывает имя и тип переменной, поэтому int x; - это просто объявление. Мы предоставляем компилятору достаточно информации о том, что он может проверить наше использование x. Определение - это когда мы присваиваем значение переменной, поэтому x=5; - это определение. Обратите внимание, что декларация и определение могут быть объединены в одну строку int x = 5;. На уровне сборки объявление приводит к тому, что хранилище будет выделено для переменной, но не устанавливает то, что содержит место хранения.

[1] Грамматика C говорит о том, что фигурные скобки не нужны на время блока, если он состоит из одного оператора, т.е.

while(n > 10) 
     c--; 

и

while(n > 10) 
    { 
     c--; 
    } 

эквивалентны, я просто считаю, что второй легче читать. Кроме того, грамматика C говорит, что фигурные скобки не нужны для тела, если заявление, если тело состоит из одного оператора, так, например

if(n < 10) 
     n = n - 10; 

и

if(n < 10) 
    { 
     n = n - 10; 
    } 

эквивалентны.

Наконец, еще если и закончить все являются частью, если заявление так заявление

if (c >= '0' && c <= '9') 
     ++ndigit[c-'0']; 
    else if (c == ' ' || c == '\n' || c == '\t') 
     ++nwhite; 
    else 
     ++nother; 

эффективно один оператор, и, таким образом, почему фигурные скобки не нужны.

Кроме того, для удобства чтения и обслуживания я стараюсь использовать фигурные скобки с if/else if/else block, но опять же это личное мнение.

[2] Оператор присваивания имеет возвращаемое значение левой стороны, поэтому простое выражение a = 10; возвращаемое значение просто игнорируется. Имея возвращаемое значение, мы можем написать что-то вроде a = b = c = 10, которое будет иметь эффект установки a, b и c на 10. Помимо наличия возвращаемого значения оператор присваивания является правильным ассоциативным, поэтому вышеприведенное выражение будет равно как a = (b = (c = 10)).

-T.

+1

+1 но один маленький дополнение. 'getchar' возвращает тип' int', так что 'EOF' (-1) можно отличить от 8-битного значения unsigned char, поэтому это * must *, а не выбор. Обратите внимание, что '' 0'' имеет тип 'int', как показано, когда вы смотрите' sizeof '0''. –

+0

Прежде всего. Большое вам спасибо за то, что вы вошли в такие подробности для меня. Такие люди, как ты, делают мир лучше и легче понимать место :) Я просто получил несколько вопросов/подтвердил. Итак, что вы сказали здесь: char '5' значение 0x35. 0x35 находится между 0x30 и 0x39, у нас есть цифра. вычитание c - '0' эквивалентно 0x35 - 0x30, что равно 0x05. Затем мы используем это как индекс в массиве и увеличиваем соответствующее значение с помощью ++ ndigit [c-'0 ']. «Означает ли это, что 0x05 или 5 будут добавлены в i, и я добавлю в массив? –

+0

@Recksome_learning_C, когда вы набираете« 5 »(я использую одинарные кавычки для обозначения символа) на клавиатуре, он читается в как символ (т. е. имеет значение ASCII 0x35). Все символы представлены внутри кода ASCII. Теперь код для charaacter zero, то есть «0», равен 0x30, код для символа один - 0x31 и и т. д. В вычитании c - '0' мы сначала преобразуем как c, так и '0' в их числовое значение, а затем выполняем вычитание. Конечный результат меняет нулевой символ на число 0 (и т. д.), (см. часть 2 ниже) – thurizas

Смежные вопросы