Упражнение 5.9. Перепишите программы day_of_year и month_day, используя вместо индексов указатели.
/* day_of_year: определяет день года по месяцу и дню */
int day_of_year(int year, int month, int day)
{
int i, leap;
char *tmp;
// Отсчет начнем с 1918 -- года перехода на грегорианский календарь
if (year < 1918 || month < 1 || month > 12 || day < 1)
return -1;
leap = (year % 4 == 0 && year % 100 !=0) || (year % 400 == 0);
tmp = &daytab[leap][1];
for (i = 1; i < month; i++)
day += *tmp;
return day;
}
/* month_day: определяет месяц и день по дню года */
int month_day(int year, int yearday, int *pmonth, int *pday)
{
int i, leap;
char *tmp;
Упражнение 5.10. Напишите программу expr, интерпретирующую обратную польскую запись выражения, задаваемого командной строкой, в которой каждый оператор и операнд представлены отдельным аргументом.
#include <stdio.h>
#include <stdlib.h>
#define MAXOP 100 /* макс. размер операнда или оператора */
#define NUMBER '0' /* признак числа */
int getop (char *);
void push (int);
int pop (void);
/* калькулятор с обратной польской записью */
int main(int argc, char *argv[])
{
int c;
int op2;
int err=0;
char s[MAXOP];
while (--argc > 0 && (c = getop(*++argv))) {
switch (c) {
case NUMBER:
push (atoi(*argv));
break;
case '+':
push (pop() + pop());
break;
case '*':
push (pop() * pop());
break;
case '-':
op2 = pop();
push (pop() - op2);
break;
case '/':
op2 = pop();
if (op2 != 0)
push (pop() / op2);
else
printf("ошибка: деление на нуль\n");
break;
default:
err = 1;
printf("ошибка: неизвестная операция %s\n", s);
break;
}
}
if (!err)
printf("\t%d\n", pop());
int sp = 0; /* следующая свободная позиция в стеке */
int val[MAXVAL]; /* стек */
/* push: положить значение i в стек */
void push(int i)
{
if (sp < MAXVAL){
val[sp++] = i;
}
else
printf("ошибка: стек полон, %d не помещается\n", i);
}
/* pop: взять с вершины стека и выдать в качестве результата */
int pop(void)
{
if (sp > 0) {
return val[--sp];
}
else {
printf ("ошибка: стек пуст\n");
return 0;
}
}
#include <ctype.h>
#include <string.h>
/* getop: получает следующий оператор или операнд */
int getop(char *s)
{
int c = strlen(s);
int p = 0;
char *tmp = s;
while (isdigit(*tmp++))
p++;
if (p != c){
return *s; /* не число */
}
else {
return NUMBER;
}
}
Упражнение 5.12. Расширьте возможности entab и detab таким образом, чтобы при обращении вида
/* инициализация массива стопов табуляции tabstops */
void settabs(int argc, char *argv[], int tabstops[])
{
int i, c;
int tabstart = 0, tabsize = TABSIZE;
while (--argc > 0 && (**++argv == '-' || **argv == '+')) {
switch (**argv)
{
case '-':
if ((c = atoi(*argv + 1)) > 0)
tabstart = c;
break;
case '+':
if ((c = atoi(*argv + 1)) > 0)
tabsize = c;
break;
}
}
for (i=0; i < tabstart; i++)
tabstops[i] = 0;
for (i=tabstart; i < MAXLINE; i++)
tabstops[i] = ((i - tabstart) % tabsize == 0);
}
Упражнение 5.13. Напишите программу tail, печатающую n последних введенных строк. По умолчанию значение n равно 10, но при желании n можно задать с помощью аргумента.
Упражнение 5.14. Модифицируйте программу сортировки, чтобы она реагировала на параметр -r, указывающий, что объекты нужно сортировать в обратном порядке, т. е. в порядке убывания. Обеспечьте, чтобы -r работал и вместе с -n.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAXLINES 5000 /* максимальное число строк */
char *lineptr[MAXLINES]; /* указатели на строки текста */
int readlines(char *lineptr[], int nlines);
void writelines(char *lineptr[], int nlines, int reverse);
void quicksort(void *lineptr[], int left, int right,
int (*comp)(void *, void *));
int numcmp(char *, char *);
/* сортировка строк */
int main(int argc, char *argv[])
{
int nlines; /* количество прочитанных строк */
int numeric = 0; /* 1, если сорт. по числ. знач. */
int reverse = 0; /* 1, если выводить по убыванию */
int c;
while (--argc > 0 && (*++argv)[0] == '-')
while ((c = *++argv[0]))
switch (c) {
case 'n':
numeric = 1;
break;
case 'r':
reverse = 1;
break;
default:
printf("sort: неверный параметр %c\n", c);
argc = 0;
break;
}
Упражнение 5.15. Введите в программу необязательный параметр -f, задание которого делало бы неразличимыми символы нижнего и верхнего регистров (например, a и A должны оказаться при сравнении равными).
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#define MAXLINES 5000 /* максимальное число строк */
char *lineptr[MAXLINES]; /* указатели на строки текста */
int readlines(char *lineptr[], int nlines);
void setlower(char *lineptr[], int nlines);
void writelines(char *lineptr[], int nlines, int reverse);
void quicksort(void *lineptr[], int left, int right,
int (*comp)(void *, void *));
int numcmp(char *, char *);
/* сортировка строк */
int main(int argc, char *argv[])
{
int nlines; /* количество прочитанных строк */
int numeric = 0; /* 1, если сорт. по числ. знач. */
int reverse = 0; /* 1, если выводить по убыванию */
int case_insensetive = 0; /* 1, если сорт. нечувств. к регистру */
int c;
while (--argc > 0 && (*++argv)[0] == '-')
while ((c = *++argv[0]))
switch (c) {
case 'n':
numeric = 1;
break;
case 'r':
reverse = 1;
break;
case 'f':
case_insensetive = 1;
break;
default:
printf("sort: неверный параметр %c\n", c);
argc = 0;
break;
}
if ((nlines = readlines(lineptr, MAXLINES)) >= 0) {
if (case_insensetive)
setlower(lineptr, nlines);
quicksort((void **) lineptr, 0, nlines-1,
(int (*)(void*,void*))(numeric ? numcmp : strcmp));
printf("SORTED:\n");
writelines(lineptr, nlines, reverse);
return 0;
} else {
printf("Bведено слишком много строк\n");
return 1;
}
}
/* setlower: преобразование символов к нижнему регистру */
void setlower(char *lineptr[], int nlines)
{
int i, j;
for (i=0; i < nlines; i++) {
j = 0;
while(lineptr[i][j]) {
lineptr[i][j] = tolower(lineptr[i][j]);
j++;
}
}
}
Упражнение 5.16. Предусмотрите в программе необязательный параметр -d, который заставит программу при сравнении учитывать только буквы, цифры и пробелы. Организуйте программу таким образом, чтобы этот параметр мог работать вместе с параметром -f.
Теперь при сравнении нам предлагается не замечать некоторых символов, например, пунктуации. Переработаем chcmp() из упражнения 5.15 в таком ключе:
Пропустим нежелательные символы в начале строки
Сравним строки, пропуская нежелательные символы
Изменения:
- строки
int directory = 0; /* 1, если сорт. игнорирует пунктуацию */
int case_insensetive = 0; /* 1, если сорт. нечувств. к регистру */
вынесены из основной функции, чтобы эти переменные были видимы из chcmp.
- в оператор switch основной функции добавлен блок:
case 'd':
directory = 1;
break;
- переработана функция chcmp(). Теперь она обрабатывает все лексикографические сравнения, а strcmp() не используется:
/* chcmp: сравнивает s1 и s2, игнорируя разницу в регистре и/или
* символы пунктуации */
int chcmp(char *s1, char *s2)
{
if(directory) {
while(!isdigit(*s1) && !isalpha(*s1) && !isspace(*s1) && *s1)
++s1;
while(!isdigit(*s2) && !isalpha(*s2) && !isspace(*s2) && *s2)
++s2;
}
while(case_insensetive ? (tolower(*s1) == tolower(*s2)) : (*s1 == *s2)) {
if(*s1 == '\0')
return 0;
++s1;
++s2;
if(directory) {
while(!isdigit(*s1) && !isalpha(*s1) && !isspace(*s1) && *s1)
++s1;
while(!isdigit(*s2) && !isalpha(*s2) && !isspace(*s2) && *s2)
++s2;
}
}
return case_insensetive ? (tolower(*s1) - tolower(*s2)) : (*s1 - *s2);
}
В этом случае игнорируются все символы, кроме букв, цифр и пробелов. Если не игнорировать также непечатаемые символы, код будет короче, т. к. можно использовать не три функции is..., а одну -- ispunct().
Упражнение 5.17. Реализуйте в программе возможность работы с полями: возможность сортировки по полям внутри строк. Для каждого поля предусмотрите свой набор параметров. Предметный указатель этой книги (Имеется в виду оригинал книги на английским языке. – Примеч. пер.) упорядочивался с параметрами: -df для терминов и -n для номеров страниц.
main()
include <stdio.h>
include <string.h>
include <ctype.h>
include <stdlib.h>
define MAXLINES 5000 /* максимальное число строк */
char *lineptr[MAXLINES]; /* указатели на строки текста */
int readlines(char *lineptr[], int nlines); void writelines(char *lineptr[], int nlines, int reverse); void quicksort(void *lineptr[], int left, int right,
int (*comp)(void *, void *));
int numcmp(char *, char *); int chcmp(char *, char *);
int directory = 0; /* 1, если сорт. игнорирует пунктуацию */ int case_insensitive = 0; /* 1, если сорт. нечувств. к регистру */ int fs = 0, ff = 0; /* начальная и конечная позиции поля */
/* сортировка строк */
int main(int argc, char *argv[])
{
int nlines; /* количество прочитанных строк */
int numeric = 0; /* 1, если сорт. по числ. знач. */
int reverse = 0; /* 1, если выводить по убыванию */
int field = 0; /* 1, если определена начальная позиция поля */
int c;
while (--argc > 0 && ((c = (*++argv)[0]) == '-' || c == '+'))
if (c == '-')
while ((c = *++argv[0]))
switch (c) {
case 'n':
numeric = 1;
break;
case 'r':
reverse = 1;
break;
case 'f':
case_insensitive = 1;
break;
case 'd':
directory = 1;
break;
default:
printf("sort: неверный параметр %c\n", c);
argc = 0;
break;
}
else if (c == '+') {
if (!fs && !field) {
fs = atoi(++argv[0]);
field = 1;
if (fs < 0) {
printf("sort: значение позиции должно быть неотрицательным\n");
fs = 0;
}
}
else if (!ff) {
ff = atoi(++argv[0]);
if (ff < fs) {
printf("sort: конечная позиция поля должна быть больше начальной\n");
ff = 0;
}
}
}
else
printf("вызов: sort -nrfd +field_start +field_finish\n");
Упражнение 5.18. Видоизмените dcl таким образом, чтобы она обрабатывала ошибки во входной информации.
Какие именно ошибки? Дадим слово авторам: "Лишние пробелы для нее опасны. Она не предпринимает никаких мер по выходу из ошибочной ситуации, и поэтому неправильные описания также ей противопоказаны".
Действительно, если поставить пробел внутри поля аргументов функции, получим ошибку исправить которую без перезапуска невозможно:
Заметьте, что вторая попытка синтаксически корректна, но также воспринимается как ошибка.
Необходимо научиться убирать лишние пробелы:
int gettoken(void) /* возвращает следующую лексему */
{
int c;
void ungetch(int);
char *p = token;
int getter(void)
{
int c;
while ((c = getch()) == ' ' || c == '\t');
return c;
}
getter() -- широко используемая в книге операция предварительного удаления пробелов и табуляций. Вынесена в функцию, потому что необходимо применить несколько раз.
Теперь -- борьба с повторением ошибок. В такой простой программе логично выглядит такой подход: сообщить пользователю об ошибке и выйти из программы, давая ему шанс ее исправить.
Поэтому исходную функцию dirdcl() я дополнил сообщения об ошибках вызовом функций exit() из stdlib.h со статусом -1:
/* dirdcl: разбор собственно объявителя */
void dirdcl(void)
{
int type;
if (tokentype == '(') {
dcl();
if (tokentype != ')') {
printf("oшибкa: пропущена )\n");
exit(-1);
}
} else if (tokentype == NAME) /* имя переменной */
strcpy(name, token);
else {
printf("ошибка: должно быть name или (dcl)\n");
exit(-1);
}
while((type = gettoken()) == PARENS || type == BRACKETS)
if (type == PARENS)
strcat(out, "функц. возвр.");
else {
strcat(out, " массив ");
strcat(out, token);
strcat(out, " из ");
}
}
Упражнение 5.19. Модифицируйте undcl так, чтобы она не генерировала лишних скобок.
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define MAXTOKEN 100
enum {NAME, PARENS, BRACKETS};
int gettoken(void);
int nexttoken(void);
int getter(void);
int tokentype; /* тип последней лексемы */
char token[MAXTOKEN]; /* текст последней лексемы */
char name[MAXTOKEN]; /* имя */
char datatype[MAXTOKEN]; /* тип = char, int и т.д. */
char out[1000]; /* выдаваемый текст */
/* undcl: преобразует словесное описание в объявление */
int main(void)
{
int type;
char temp[MAXTOKEN];
while (gettoken() != EOF) {
strcpy(out, token);
while ((type = gettoken()) != '\n')
if (type == PARENS || type == BRACKETS)
strcat(out, token);
else if (type == '*' ) {
if ((type = nexttoken()) == PARENS || type == BRACKETS)
sprintf(temp, "(*%s)", out);
else
sprintf(temp, "*%s", out);
strcpy(out, temp);
} else if (type == NAME) {
sprintf(temp, "s", token, out);
strcpy(out, temp);
} else
printf("неверный элемент %s во фразе\n", token);
printf("%s\n", out);
}
return 0;
}
enum {NO, YES};
int prevtoken = NO;
int nexttoken(void)
{
int type;
type = gettoken();
prevtoken = YES;
return type;
}
int getch(void);
int gettoken(void) /* возвращает следующую лексему */
{
int c;
void ungetch(int);
char *p = token;
Упражнение 5.20. Расширьте возможности dcl, чтобы dcl обрабатывала объявления с типами аргументов функции, квалификаторами вроде const и т. п.
void params_dcl(void)
{
void type_dcl(void);
do {
type_dcl();
} while (tokentype == ',');
if (tokentype != ')') {
printf("oшибкa: пропущена ) в объявлении параметров\n");
prevtoken = YES;
}
}
void type_dcl(void)
{
char t[MAXTOKEN];
int istypename(char []);
int istypequal(char []);
t[0] = '\0';
gettoken();
do {
if (tokentype != NAME) {
prevtoken = YES;
dcl();
}
else if (istypename(token)) {
strcat(t, token);
gettoken();
}
else if (istypequal(token)) {
strcat(t, token);
gettoken();
}
else {
printf("ошибка: неизвестный параметр %s\n", token);
prevtoken = YES;
}
} while (tokentype == ',' && tokentype == ')');
strcat(out, t);
if (tokentype == ',')
strcat(out, ", ");
}
int istypename(char name[])
{
int i=0;
char *types[] = {"char", "int", "float"};
int ntypes = 3;
while (ntypes--)
if (strcmp(name, types[i++]) == 0)
return 1;
return 0;
}
int istypequal(char qual[])
{
int i=0;
char *quals[] = {"const", "volatile"};
int nquals = 2;
while (nquals--)
if (strcmp(qual, quals[i++]) == 0)
return 1;
Упражнение 6.1. Haшa вepcия getword не обрабатывает должным образом знак подчеркивания, строковые константы, комментарии и управляющие строки препроцессора. Напишите более совершенный вариант программы.
struct key {
char *word;
int count;
} keytab[] = {
{"_asm", 0},
{"as_m", 0},
{"asm", 0},
{"break", 0},
{"case", 0},
{"char", 0}
};
Фигурные скобки вокруг элементов отдельных структур пришлось поставить, т. к. в отличие от того, что было у K&R, мой gcc ругается из-за отсутствия этих скобок.
Переделанная getword():
/* getword: принимает следующее слово или символ из ввода */
int getword (char *word, int lim)
{
int c, getch(void);
void ungetch(int);
char *w = word;
while (isspace(c = getch()));
if (c != EOF)
*w++ = c;
if (isalpha(c) || c == '_')
for ( ; --lim > 0; w++)
if (!isalnum(*w = getch()) && *w != '_') {
ungetch(*w);
break;
}
*w = '\0';
return c;
}
Даже стала короче.
Про директивы препроцессора я не понял задания. getword(), в ее нынешнем виде, игнорирует собственно директивы, но извлекает ключевые слова, стоящие за ними. Сами же директивы не являются ключевыми словами. Может я должен, вместо препроцессора, выполнить подстановку из директив? Короче говоря, оставил как есть.
Пропуск ключевых слов, находящихся внутри строковых констант осуществляется добавлением условия
else if (c == '"') {
while ((t = getch()) != c && t != EOF);
}
Заметим, что это решение не застраховано от ложных срабатываний на экранированных двойных кавычках.
Удаление многострочных комментариев осуществляется условием:
else if (c == '/') {
if ((t = getch()) == '*') {
while ((t = getch()) != EOF )
if (t == '*') {
if ((t = getch()) == '/')
break;
else
ungetch(t);
}
}
else
ungetch(t);
}
Однострочные комментарии мы обрабатывать не будем, но идея здесь та же, только начало комментария -- "//", а конец -- "\n".
Сходная задача по "уборке" комментариев решалась также в упражнении 1.24.
getword() полностью:
int getword (char *word, int lim)
{
int c, t, getch(void);
void ungetch(int);
char *w = word;
while (isspace(c = getch()));
if (c != EOF)
*w++ = c;
if (isalpha(c) || c == '_') {
for ( ; --lim > 0; w++)
if (!isalnum(*w = getch()) && *w != '_') {
ungetch(*w);
break;
}
}
else if (c == '"') {
while ((t = getch()) != c && t != EOF);
}
else if (c == '/') {
if ((t = getch()) == '*') {
while ((t = getch()) != EOF )
if (t == '*') {
if ((t = getch()) == '/')
break;
else
ungetch(t);
}
}
else
ungetch(t);
}
*w = '\0';
return c;
}
Упражнение 6.2. Напишите программу, которая читает текст Си-программы и печатает в алфавитном порядке все группы имен переменных, в которых совпадают первые 6 символов, но последующие в чем-то различаются. Не обрабатывайте внутренности закавыченных строк и комментариев. Число 6 сделайте параметром, задаваемым в командной строке.
if (argc == 2)
num = atoi(argv[1]);
else if (argc < 2)
num = 3; /* Значение длины сравнения по умолчанию */
else {
printf("usage: tree <number>\n");
exit(-1);
}
root = NULL;
while (getword (word, MAXWORD) != EOF)
if (isalpha(word[0]))
root = addtree(root, word);
treeprint(root);
return 0;
}
/* addtree: добавляет узел со словом w в р или ниже него */
struct tnode *addtree(struct tnode *p, char *w)
{
int cond;
if (p == NULL) { /* слово встречается впервые */
p = talloc(); /* создается новый узел */
p->word = strdup(w);
p->count = 1;
p->left = p->right = NULL;
} else if ((cond = strncmp(w, p->word, num)) == 0)
p->count++; /* это слово уже встречалось */
else if (cond < 0) /* меньше корня левого поддерева */
p->left = addtree(p->left, w);
else /* больше корня правого поддерева */
p->right = addtree(p->right, w);
return p;
}
addtree() подверглась, как видно, минимальной переработке. strdup() переименована в mystrdup(), чтобы не конфликтовать с библиотечной функцией.
Вот тестовая программа:
#include <stdio.h>
int main(void)
{
char test1[] = "This is a test";
int test = 2;
/* test for ex. 6.2 */
printf("d\n", test1, test);
return 0;
}
Упражнение 6.3. Напишите программу печати таблицы "перекрестных ссылок", которая будет печатать все слова документа и указывать для каждого из них номера строк, где оно встретилось. Программа должна игнорировать "шумовые" слова, такие как "и", "или" и пр.
struct tnode { /* узел дерева */
char *word; /* указатель на текст */
int nline[MAXLINK]; /* массив номеров строк */
int count; /* индекс в nline[] */
struct tnode *left; /* левый дочерний узел */
struct tnode *right; /* правый дочерний узел */
};
/* addtree: добавляет узел со словом w в р или ниже него */
struct tnode *addtree(struct tnode *p, char *w)
{
int cond;
if (p == NULL) { /* слово встречается впервые */
p = talloc(); /* создается новый узел */
p->word = strdup(w);
p->nline[p->count] = lnum;
p->count = 1;
p->left = p->right = NULL;
} else if ((cond = strcmp(w, p->word)) == 0) {
p->nline[p->count++] = lnum; /* это слово уже встречалось */
}
else if (cond < 0) /* меньше корня левого поддерева */
p->left = addtree(p->left, w);
else /* больше корня правого поддерева */
p->right = addtree(p->right, w);
return p;
}
/* treeprint: упорядоченная печать дерева р */
void treeprint(struct tnode *p)
{
int i;
if (p != NULL) {
treeprint (p->left);
printf("\n%s ", p->word);
for (i = 0; i < MAXLINK; i++)
printf("%2d ",p->nline[i]);
treeprint(p->right);
}
}
/* getword: принимает следующее слово или символ из ввода */
int getword (char *word, int lim)
{
int c, t, getch(void);
void ungetch(int);
char *w = word;
while (isspace(c = getch()) && c != '\n');
if (c != EOF)
*w++ = c;
if (isalpha(c) || c == '_') {
for ( ; --lim > 0; w++) {
if (!isalnum(*w = getch()) && *w != '_') {
ungetch(*w);
break;
}
else if (*w == '\n') {
lnum++;
ungetch(*w);
break;
}
}
}
else if (c == '"') {
while ((t = getch()) != c && t != EOF);
}
else if (c == '/') {
if ((t = getch()) == '*') {
while ((t = getch()) != EOF )
if (t == '*') {
if ((t = getch()) == '/')
break;
else
ungetch(t);
}
}
else
ungetch(t);
}
else if (c == '\n')
lnum++;
*w = '\0';
return c;
}
Упражнение 6.4. Напишите программу, которая печатает весь набор различных слов, образующих входной поток, в порядке возрастания частоты их встречаемости. Перед каждым словом должно быть указано число вхождений.
Упражнение 6.6. Реализуйте простую версию #define-npoцeccopa (без аргументов), которая использовала бы программы этого параграфа и годилась бы для Си-программ. Вам могут помочь программы getch и ungetch.
struct nlist { /* элемент таблицы */
struct nlist *next; /* указатель на следующий элемент */
char *name; /* определенное имя */
char *defn; /* замещающий текст */
};
for (np = hashtab[hash(s)]; np != NULL; np = np->next)
if (strcmp(s, np->name) == 0)
return np; /* нашли */
return NULL; /* не нашли */
}
/* hash: получает хэш-код для строки s */
unsigned hash(char *s)
{
unsigned hashval;
for (hashval = 0; *s != '\0'; s++)
hashval = *s + 31 * hashval;
return hashval % HASHSIZE;
}
В коде присутствуют install(), lookup() и hash() из п. 6.6, но пока не используются. Здесь не зватает getword() и пары getch/ungetch -- их можно взять из прошлых упражнений этой главы.
Вводим тестовую программу:
#include <stdio.h>
#define TWO 2
int main(void)
{
char test1[] = "This is a test";
int test = TWO;
/* test for ex. 6.2 */
printf("d\n", test1, test);
#undef TWO
return 0;
}
Добавим обработку undef в основную программу:
int main(void)
{
char word[MAXWORD], tword[MAXWORD];
struct nlist *np;
while (getword (word, MAXWORD) != EOF) {
if (strcmp(word, "#") == 0) {
printf("<directive?>");
printf("[%s]", word);
getword (word, MAXWORD);
if (strcmp(word, "define") == 0) {
printf("<define>");
if (isupper(getword (word, MAXWORD))) {
printf("<NAME>");
strcpy(tword, word);
if (isalnum(getword (word, MAXWORD))) {
printf("<<defn>>");
np = install(tword, word);
}
else
printf("error: incorrect definition.\n");
}
else {
printf("error: must be only UPPER LETTERS in directive.\n");
}
}
else if (strcmp(word, "undef") == 0) {
printf("<undef>");
if (isupper(getword (word, MAXWORD))) {
printf("<NAME>");
undef(word);
}
}
else {
printf("<not>");
}
}
else if ((np = lookup(word)) != NULL) {
printf("<in table>");
strcpy(word, np->defn);
}
printf("[%s]", word);
}
printf("\n");
tableprint();
return 0;
}
Функция undef() (т. е. решение упражнения 6.5) имеет вид:
/* undef: удаление записи из хэш-таблицы hashtab */
void undef(char *s)
{
int i;
struct nlist *np, *prev;
Упражнение 7.1. Напишите программу, осуществляющую перевод ввода с верхнего регистра на нижний или с нижнего на верхний в зависимости от имени, по которому она вызывается и текст которого находится в arg[0].
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
/* upper/lower: переводит ввод в верхний/нижний регистр */
int main(int argc, char *argv[])
{
int c;
if (argc != 1) {
printf("usage: /.{upper|lower}\n");
exit(1);
}
printf("%s\n", argv[0]);
if (strcmp(argv[0], "./upper") == 0)
while ((c = getchar()) != EOF)
putchar(toupper(c));
else
while ((c = getchar()) != EOF)
putchar(tolower(c));
Упражнение 7.2. Напишите программу, которая будет печатать разумным способом любой ввод. Как минимум она должна уметь печатать неграфические символы в восьмеричном или шестнадцатеричном виде (в форме, принятой на вашей машине), обрывая длинные текстовые строки.
#include <stdio.h>
#include <ctype.h>
#define MAXLEN 30
/* печатает неграфические символы ввода как 16-ричные числа,
* разрывает длинные строки */
int main(void)
{
int n = 0;
char c;
while ((c = getchar()) != EOF) {
if (c == '\n') {
n = 0;
putchar(c);
}
else if (!isprint(c) && c != '\n') {
if (n > MAXLEN) {
putchar('\n');
n = 0;
}
printf("\\%x", c);
n++;
}
else {
if (n > MAXLEN) {
putchar('\n');
n = 0;
}
putchar(c);
n++;
}
}