Язык Си в примерах/Таблица умножения: различия между версиями

Содержимое удалено Содержимое добавлено
м <source> -> <syntaxhighlight> (phab:T237267)
 
Строка 12:
 
{{Якорь |multable.c}}
<sourcesyntaxhighlight lang="c">
#include <assert.h>
#include <limits.h>
Строка 46:
return 0;
}
</syntaxhighlight>
</source>
 
Примета работы программы (построение таблицы умножения для <var >n</var> = 5):
Строка 82:
 
Телом ''цикла строк'' должна стать, очевидно, некая процедура вывода заданной строки, которую мы вновь оформляем как цикл — использующий уже новую переменную <var >j</var>:
<sourcesyntaxhighlight lang="c">
long j;
for (j = 1; j <= n; j++) {
Строка 88:
}
putchar ('\n');
</syntaxhighlight>
</source>
 
{{Якорь |%*}}
Строка 111:
 
{{Якорь |ceil |log}}
В итоговом выражении — <sourcesyntaxhighlight lang="c" enclose="none" >(int)ceil (2 * log (1 + n) / log (10))</sourcesyntaxhighlight> — мы используем функции <code >ceil</code> и <code >log</code>, значением которых являются результат округления «от нуля» (в сторону роста ''абсолютного значения'') и {{w |натуральный логарифм}} аргумента, соответственно. Эти функции являются частью ''стандартной библиотеки'' и ''объявлены'' в ''заголовке'' <code >math.h</code>.<ref name="ceil" /><ref name="log" />
 
Ясно, что данное выражение теряет смысл при <var >n</var> &lt; 0. Кроме того, при <var >n</var> = 0, реализованные нами циклы вычисления—вывода сформируют ''пустой'' результат, который также <em >можно</em> считать ошибочным. Чтобы этого избежать, перед вычислением искомого выражения мы ''требуем'' (макроподстановкой <code >assert</code><ref name="assert" />) выполнения условия <sourcesyntaxhighlight lang="c" enclose="none" >n >= 1</sourcesyntaxhighlight>.
 
Здесь же мы требуем, чтобы значение <var >n</var> не превышало квадратного корня из максимального допустимого значения для используемого нами типа <code >long</code> (макроподстановка <var >LONG_MAX</var> — ''объявлена'' в ''заголовке'' <code >limits.h</code> ''стандартной библиотеки''.) В противном случае, один или более из результатов умножения может ''переполнить'' разрядность данного типа. (Отметим, впрочем, что формирование таблицы умножения теряет практический смысл уже при много меньших значениях <var >n</var>.)
Строка 121:
Наконец, рассмотрим получение в программе переданного как аргумент командной строки значения <var >n</var>.
 
<sourcesyntaxhighlight lang="c">
assert (argc - 1 == 1);
long n;
Строка 130:
assert (*tail == '\0');
}
</syntaxhighlight>
</source>
 
Данный фрагмент кода начинается ''требованием'' (макроподстановкой <code >assert</code><ref name="assert" />) наличия ''единственного'' аргумента командной строки. Здесь следует отметить, что переменная <var >argc</var> отражает общую длину массива <var >argv</var>, в который входит — нулевым элементом — имя самой программы: <code ><var >argv</var>[0]</code>. Тем самым, условие наличия и единственности аргумента может быть записано как <code ><var >argc</var> == 2</code> — или же как приведено в коде.
 
В свою очередь, переменные <var >argc</var> и <var >argv</var> являются аргументами главной функции <code >main</code>, при ''объявлении'' ее следующим образом:<ref name="startup" />
<sourcesyntaxhighlight lang="c">
int
main (int argc, char *argv[])
</syntaxhighlight>
</source>
 
{{Якорь |strtol}}
Строка 158:
 
''Областью видимости'' (англ. {{lang |en|scope}}) переменной (или ''идентификатора'' в общем) как правило оказывается часть содержащего переменную блока — от точки определения и до закрытия этого блока. Однако, вложенные блоки могут ''скрывать'' переменные вышестоящих блоков, объявляя свои собственные переменные с теми же именами. Так, например, функция <code >foo</code> в следующем примере выведет на стандартный вывод код перевода строки (из переменной <var >a</var> внутреннего блока) и ''вернет'' значение 1 (из одноименной переменной внешнего):
<sourcesyntaxhighlight lang="c">
static int
foo (void)
Строка 170:
return a;
}
</syntaxhighlight>
</source>
 
Хорошей практикой представляется определение переменных так, чтобы область их видимости была <em >не большей</em>, чем строго необходимо. (Но не в ущерб ясности кода.) В этом случае, при перемещении (копировании) кода, случайное использование переменной там, где она не имеет смысла, с большей вероятностью будет диагностировано непосредственно реализацией языка.
Строка 179:
 
{{Якорь |multablex.c}}
<sourcesyntaxhighlight lang="c">
#include <assert.h>
#include <ctype.h>
Строка 230:
return 0;
}
</syntaxhighlight>
</source>
 
Пример работы программы (построение таблицы умножения для <var >n</var> = 5 в шестнадцатиричном представлении):
Строка 242:
 
По сравнению с [[#multable.c |исходным вариантом]], пример дополнился следующим фрагментом кода:
<sourcesyntaxhighlight lang="c">
char *p = strchr (argv[1], '0');
if (p == 0
Строка 255:
fmt = " %*lo";
}
</syntaxhighlight>
</source>
 
Кроме того, две из исходных ''констант'' (<code >10</code> при [[#Вычисление предельной разрядности |вычислении предельной разрядности]] и <code >" %*ld"</code> в качестве ''строки формата'' функции <code >printf</code>) заменены в данном варианте ''переменными'' <var >base</var> и <var >fmt</var>, соответственно.
Строка 262:
Во фрагменте выше, мы используем функцию <code >strchr</code> (''объявленную'' в ''заголовке'' <code >string.h</code>) поиска первого вхождения символа (кода) в строку<ref name="strchr" /> — в данном случае, поиска символа <code >0</code> в аргументе командной строки.
 
Функция возвращает ''нулевой указатель'' (0 или <code >NULL</code>) если символ не найден.<ref name="strchr" /> Поскольку это означает, что аргумент командной строки не содержит символа <code >0</code> <em >вовсе</em>, очевидно, что он не может содержать его и как часть префикса системы счисления (<code >0x</code> или <code >0</code>), а значит следует использовать десятичную систему счисления при [[#Вычисление предельной разрядности |вычислении предельной разрядности]] и ''указатель представления'' <code >d</code> — «десятичное число»: <sourcesyntaxhighlight lang="c" enclose="none" >base = 10; fmt = " %*ld";</sourcesyntaxhighlight>.
 
{{Якорь |isdigit}}
Точно так же обрабатывается случай, когда найденному символу <code >0</code> <em >предшествует</em> любая десятичная цифра — <sourcesyntaxhighlight lang="c" enclose="none" >p > argv[1] && isdigit (p[-1])</sourcesyntaxhighlight>. Используемая в этом условии функция <code >isdigit</code> (''объявлена'' в ''заголовке'' <code >ctype.h</code>) возвращает ''истину'' (отличное от 0 значение) в том и только том случае, когда ее аргумент — десятичная цифра; в противном случае — возвращается 0.<ref name="isdigit" /> Разумеется, обращение к <em >предшествующему</em> символу имеет смысл только в том случае, когда найденный символ <code >0</code> не является <em >первым</em> в строке — <sourcesyntaxhighlight lang="c" enclose="none" >p > argv[1]</sourcesyntaxhighlight>.
 
Следующим мы проверяем условие <sourcesyntaxhighlight lang="c" enclose="none" >p[1] == 'x' || p[1] == 'X'</sourcesyntaxhighlight> — наличие после <code >0</code> признака шестнадцатиричного представления <code >x</code> или <code >X</code>. В этом случае, <var >base</var> присваивается значение 16, <var >fmt</var> — <code >" %*lx"</code> или <code >" %*lX"</code> (в зависимости от регистра символа <code >x</code> в аргументе.)
 
Наконец, если условия выше (наличие цифры перед <code >0</code> ''или'' <code >x</code>, <code >X</code> — после) — не выполнены, мы присваиваем <var >base</var> значение 8, <var >fmt</var> — <code >" %*lo"</code>.