Programms from Kernighan and Ritchie

Pages: 1234
Упражнение 7.4. Напишите свою версию scanf по аналогии с minprintf из предыдущего параграфа.
#include <stdio.h>
#include <stdarg.h>

#define MAXLEN 10

void minscanf(char *, ...);

int main(void)
{
int in;
double db;
char str[MAXLEN];

printf("Input int double string: ");
minscanf("f %s", &in, &db, str);
printf("Output: lf, %s", in, db, str);
return 0;
}

/* minscanf: минимальный scanf с переменным числом аргументов */
void minscanf(char *fmt, ...)
{
va_list ap; /* указатель на очередной безымянный аргумент */
char *p, *sval;
int *ival;
double *dval;

va_start(ap, fmt); /* устанавливает ap на 1-й безымянный аргумент */
for (p=fmt; *p; p++) {
if (*p =='%') {
switch (*++p) {
case 'd':
ival = va_arg(ap, int *);
scanf ("%d", ival);
break;
case 'f':
dval = va_arg(ap, double *);
scanf("%lf", dval);
break;
case 's':
sval = va_arg(ap, char *);
scanf("%s", sval);
break;
default:
putchar(*p);
break;
}
}
}
va_end(ap); /* очистка, когда все сделано */
}
Упражнение 7.5. Перепишите основанную на постфиксной записи программу калькулятора из главы 4 таким образом, чтобы для ввода и преобразования чисел она использовала scanf и/или sscanf.
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

#define MAXOP 100 /* макс. размер операнда или оператора */
#define NUMBER '0' /* признак числа */

int getop (char *);
void push (double);
double pop (void);

/* калькулятор с обратной польской записью */
int main()
{
int op;
double op2;
char s[MAXOP];

while ((op = scanf("%s", s)) && op != EOF) {
switch (getop(s)) {
case NUMBER:
push (atof(s));
break;
case '+':
push (pop() + pop());
break;
case '*':
push (pop() * pop());
break;
case '-':
op2 = pop();
push (pop() - op2);
break;
case '/':
op2 = pop();
if (op2 != 0.0)
push (pop() / op2);
else
printf("ошибка: деление на нуль\n");
break;
default:
printf("ошибка: неизвестная операция %s\n", s);
break;
}
}
printf("\t%.8g\n", pop());

return 0;
}

/* getop: получает следующий оператор или операнд */
int getop(char *s)
{
if (!isdigit(s[0]) && s[0] != '.')
return s[0]; /* не число */
else
return NUMBER;
}
Упражнение 7.6. Напишите программу, сравнивающую два файла и печатающую первую строку, в которой они различаются.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define MAXLINE 100

/* filecmp: сравнение содержимого двух файлов */
int main(int argc, char *argv[])
{
int i = 0;
FILE *fp1, *fp2;
char *s1, *s2, line1[MAXLINE], line2[MAXLINE], *prog = argv[0]; /* имя программы */

if (argc != 3) {
fprintf(stderr, "usage: filecmp file1 file2\n");
exit(1);
}
if ((fp1 = fopen(argv[1], "r")) == NULL) {
fprintf(stderr, "s\n", prog, argv[1]);
exit(1);
}
if ((fp2 = fopen(argv[2], "r")) == NULL) {
fprintf(stderr, "s\n", prog, argv[2]);
exit(1);
}
if (!strcmp(argv[1], argv[2])) { /* не одинаковы ли файлы? */
printf("no differences\n");
exit(0);
}
while ((s1 = fgets(line1, MAXLINE, fp1)) != NULL && (s2 = fgets(line2, MAXLINE, fp2)) != NULL) {
i++;
if (strcmp(line1, line2)) {
printf("\ndifference:\n");
printf("line s: %s", i, argv[1], s1);
printf("line s: %s", i, argv[2], s2);
exit(0);
}
}
/* если один из файлов закончился раньше другого */
if (s1 && !s2) {
printf("\ndifference:\n");
printf("line s: %s", i, argv[1], s1);
printf("EOF in %s\n", argv[2]);
}
else if (!s1 && s2) {
printf("\ndifference:\n");
printf("EOF in %s\n", argv[1]);
printf("line s: %s", i, argv[2], s2);
}
else
printf("no differences\n");
exit(0);
}
Упражнение 7.7. Модифицируйте программу поиска по образцу из главы 5 таким образом, чтобы она брала текст из множества именованных файлов, а если имен файлов в аргументах нет, то из стандартного ввода. Следует ли печатать имя файла, в котором найдена подходящая строка?
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define MAXLINE 1000

int except = 0, number = 0, found = 0;

void finder(char *, FILE *, int);

/* find: поиск строк по образцу из командной строки */
int main(int argc, char *argv[])
{
char *pattern, *prog = argv[0];
FILE *inp;
int c, t;

while (--argc > 0 && (*++argv)[0] == '-')
while ((c = *++argv[0]))
switch (c) {
case 'x':
except = 1;
break;
case 'n':
number = 1;
break;
default:
fprintf(stderr, "find: неверный параметр %c\n", c);
argc = 0;
found = -1;
break;
}
if (argc < 1) {
fprintf(stderr, "usage: find -x -n pattern\n");
exit(1);
}
pattern = *argv;
if (!--argc) {
finder(pattern, stdin, MAXLINE);
exit(0);
}
while (argc-- && (inp = fopen(*++argv, "r")) != NULL) {
finder(pattern, inp, MAXLINE);
}
if (!argc) {
fprintf(stderr, "s\n", prog, *argv);
exit(1);
}
return found;
}

/* finder: поиск по образцу pattern в файле input */
void finder(char *pattern, FILE *input, int maxline) {
long lineno = 0;
char line[MAXLINE], *p;

while ((p = fgets(line, MAXLINE, input)) != NULL) {
lineno++;
if ((strstr(line, pattern) != NULL) != except) {
if (number)
printf("%ld:", lineno);
printf("%s", line);
found++;
}
}
}
Упражнение 7.8. Напишите программу, печатающую несколько файлов. Каждый файл должен начинаться с новой страницы, предваряться заголовком и иметь свою нумерацию страниц.
#include <stdio.h>
#include <stdlib.h>

#define MAXLINE 1000
#define PAGELEN 15


void fprinter(FILE *, int);

/* fileprint: печать файлов, имена которых заданы в командной строке */
int main(int argc, char *argv[])
{
char *prog = argv[0];
FILE *inp;

if (argc == 1) {
fprintf(stderr, "usage: fileprint file1, file2,...\n");
exit(1);
}
while (--argc) {
if ((inp = fopen(*++argv, "r")) != NULL) {
printf("-------- begin of file %s --------\n", *argv);
fprinter(inp, MAXLINE);
printf("--------- end of file %s ---------\n", *argv);
}
else {
fprintf(stderr, "s\n", prog, *argv);
exit(1);
}
}
return 0;
}

/* fprinter: печать файла input */
void fprinter(FILE *input, int maxline) {
long t, lineno = 0;
int pageno = 1;
char line[MAXLINE], *p;

printf("-%d-\n", pageno);
while ((p = fgets(line, MAXLINE, input)) != NULL) {
if (lineno++ < PAGELEN)
printf("%s", line);
else {
lineno = 0;
printf("-%d-\n", ++pageno);
}
}
if ((t = PAGELEN - lineno) > 0)
while (--t)
printf("\n");
}
Упражнение 7.9. Реализуя функции вроде isupper, можно экономить либо память, либо время. Напишите оба варианта функции.
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <time.h>

#define my_isupper3(c) (c >= 'A' && c <= 'Z')

int my_isupper1(char );
int my_isupper2(char );

int main(void)
{
int i, count, num = 20000000;
double t1, t2;
char line[20] = "My sample String";

t1 = clock();
while (--num)
for (i=0; line[i]; i++)
if (my_isupper3(line[i]))
count++;
t2 = (clock() - t1)/CLOCKS_PER_SEC;
printf("%lf\n", t2);

return 0;
}

int my_isupper1(char c) {
return (c >= 'A' && c <= 'Z');
}

int my_isupper2(char c) {
return (strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ",c) != NULL);
}
Упражнение 1.4. Напишите программу, которая будет печатать таблицу соответствия температур по Цельсию температурам по Фаренгейту.

Упражнение 1.5. Измените программу преобразования температур так, чтобы она печатала таблицу в обратном порядке, т. е. от 300 до 0.

#include <stdio.h>

#define LOWER 0 /* нижняя граница таблицы */
#define UPPER 300 /* верхняя граница */
#define STEP 20 /* размер шага */

/* печать таблицы температур по Фаренгейту и Цельсию */
int main()
{
int fahr;
for (fahr = UPPER; fahr >= LOWER; fahr = fahr - STEP)
printf("%3d %6.1f\n", fahr, (5.0/9.0)*(fahr-32));
return 0;
}
Упражнение 1.6. Убедитесь в том, что выражение getchar() != EOF получает значение 0 или 1.

#include <stdio.h>

int main()
{
int c;

while ((c = getchar()) != EOF)
putchar(c);
return 0;
}
Упражнение 1.7. Напишите программу, печатающую значение EOF.

#include <stdio.h>

int main()
{
printf ("The value of EOF is %d\n\n" , EOF);
return 0;
}
Упражнение 1.8. Напишите программу для подсчета пробелов, табуляций и новых строк.

#include <stdio.h>

/* подсчет строк, пробелов и табуляций входного потока */
int main()
{
int c, nl, ns, nt;
nl = 0;
ns = 0;
nt = 0;
while ((c = getchar()) != EOF)
if (c == '\n')
++nl;
else if (c == ' ')
++ns;
else if (c == 't')
++nt;
printf("lines=%d, spaces=%d, tabs=%d\n", nl, ns, nt);
return 0;
}
Last edited on
Упражнение 1.9. Напишите программу, копирующую символы ввода в выходной поток и заменяющую стоящие подряд пробелы на один пробел.

#include <stdio.h>

int main()
{
int c, sflag;

sflag = 0;
while ((c = getchar()) != EOF){
if (c != ' '){
putchar(c);
sflag = 0;
}
else if (sflag == 0){
putchar(c);
sflag = 1;
}
}
return 0;
}
Упражнение 1.10. Напишите программу, копирующую вводимые символы в выходной поток с заменой символа табуляции на \t, символа забоя на \b и каждой обратной наклонной черты на \\. Это сделает видимыми все символы табуляции и забоя.

#include <stdio.h>

int main()
{
int c;

while ((c = getchar()) != EOF)
if (c == '')
printf("\\");
else if (c == '\t')
printf("t");
else if (c == '\b')
printf("b");
else
putchar(c);

return 0;
}
Упражнение 1.12. Напишите программу, которая печатает содержимое своего ввода, помещая по одному слову на каждой строке.

#include <stdio.h>

#define IN 1 /* внутри слова */
#define OUT 0 /* вне слова */

/* вывод слов в новой строке */
int main()
{
int c, state;
state = OUT;
while ((c = getchar()) != EOF) {
if (c == ' ' || c == '\n' || c == '\t'){
state = OUT;
putchar('\n');
}
else if (state == OUT) {
state = IN;
putchar(c);
}
else
putchar(c);
}
return 0;
}
Упражнение 1.13. Напишите программу, печатающую гистограммы длин вводимых слов. Гистограмму легко рисовать горизонтальными полосами. Рисование вертикальными полосами -- более трудная задача.

#include <stdio.h>

#define MAXLEN 10 /* максимальная длина слова + 1*/

/* вывод длин слов */
int main()
{
int i, norm, c, ncw;
int wlength[MAXLEN];

ncw = 0;
for (i = 0; i <= 9; ++i)
wlength[i] = 0;

while ((c = getchar()) != EOF) {
if (c == ' ' || c == '\n' || c == '\t'){
if (ncw >= MAXLEN)
wlength[0] += 1;
else if (ncw > 0)
wlength[ncw] += 1;
ncw = 0;
}
else
++ncw;
}

// заголовок гистограммы
for (i = 0; i < MAXLEN; ++i)
printf("%d", i);
printf("\n");

// построение гистограммы
norm = 1; // произвольное ненулевое значение
while (norm > 0) {
norm = 0;
for (i = 0; i < MAXLEN; ++i){
if (wlength[i] > 0) {
printf("-");
wlength[i] -= 1;
}
else
printf(" ");
norm += wlength[i];
}
printf("\n");
}
return 0;
}
Упражнение 1.14. Напишите программу, печатающую гистограммы частот "встречаемости" вводимых символов.

#include <stdio.h>

#define LEN 12

/* построение гистограммы частот употребления цифр,
* символов-разделителей и прочих символов */
int main()
{
int c, i, j;
int nchar[LEN];

for (i = 0; i < LEN; ++i)
nchar[i]= 0;

while ((c = getchar()) != EOF)
if (c >='0' && c <= '9')
++nchar[c-'0'];
else if (c == ' ' || c == '\n' || c == '\t')
++nchar[10];
else
++nchar[11];

for (i = 0; i < LEN; ++i){
printf("%d : ", i);
for (j = 1; j <= nchar[i]; ++j)
printf("|");
printf(" > %d\n", nchar[i]);
}

return 0;
}
Упражнение 1.17. Напишите программу печати всех вводимых строк, содержащих более 80 символов.

int main()
{
int len; /* длина текущей строки */
int cont; /* продолжать ли расчет? */
int last;
char line[MAXLINE]; /* текущая строка */

len = 0;
cont = 0;
while ((len = getl(line, MAXLINE)) > 0) {
last = line[len-1];
if (len == MAXLINE-1) {
printf("%s", line);
cont = 1;
}
else if (cont == 1) {
printf("%s\n", line);
cont = 0;
}
}
return 0;
}
Упражнение 1.18. Напишите программу, которая будет в каждой вводимой строке заменять стоящие подряд символы пробелов и табуляций на один пробел и удалять пустые строки.

#include <stdio.h>

#define MAXLINE 1000 /* максимальный размер вводимой строки + 1 */

int getl(char line[], int maxline);
int filter(char to[], char from[]);

int main()
{
int len; /* длина текущей строки */
char line[MAXLINE]; /* текущая строка */
char newline[MAXLINE]; /* новая строка */

len = 0;
while ((len = getl(line, MAXLINE)) > 0) {
if ((len = filter(newline, line)) != 1)
printf("| %s", newline);
}
return 0;
}

/* getline: читает строку в s, возвращает длину */
int getl(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;
}

/* filter: преобразует содержимое 'from' в 'to'
* убирает повторные пробелы и табуляции */
int filter(char to[], char from[])
{
int c, i, j, sflag;

i = 0;
j = 0;
sflag = 0;
while ((c = from[i]) != '\n') {
if (c == ' ' || c == '\t'){
if (sflag == 0) {
to[j] = ' ';
sflag = 1;
++j;
}
}
else {
to[j] = c;
sflag = 0;
++j;
}
++i;
}

if (c == '\n') {
to[j] = c;
++j;
}
to[j] = '\0';
return j;
}
Упражнение 1.19. Напишите функцию reverse(s), размещающую символы в строке s в обратном порядке. Примените ее при написании программы, которая каждую вводимую строку располагает в обратном порядке.

#include <stdio.h>

#define MAXLINE 100 /* максимальный размер вводимой строки + 1 */

int getl(char line[], int maxline);
void reverse(char to[], char from[], int len);

/* печать обращенных строк */
int main()
{
int len; /* длина текущей строки */
char line[MAXLINE]; /* текущая строка */
char rline[MAXLINE]; /* обращенная строка */

len = 0;
while ((len = getl(line, MAXLINE)) > 0) {
reverse(rline, line, len);
printf("| %s", rline);
}
return 0;
}

/* getline: читает строку в s, возвращает длину */
int getl(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;
}

/* copy: обращает строку из 'from' и записывает в 'to' */
void reverse(char to[], char from[], int lim)
{
int i;

i = 0;
for (i = 0; i < lim-1; ++i) {
to[lim-2-i] = from[i];
}
to[lim-1] = '\n';
to[lim] = '\0';
}
Упражнение 1.20. Напишите программу detab, заменяющую символы табуляции во вводимом тексте нужным числом пробелов (до следующего “стопа” табуляции). Предполагается, что “стопы” табуляции расставлены на фиксированном расстоянии друг от друга, скажем, через n позиций. Как лучше задавать n -- в виде значения переменной или в виде именованной константы?

/* detab: преобразует символы табуляции 'from' в заданное
* число пробелов 'to' */
void detab(char to[], char from[])
{
int i, j, k;

i = j = 0;
while ((to[j] = from[i]) != '\0') {
if (to[j] == 't')
for (k = 0; k < TABSIZE; ++k, ++j)
to[j] = ' ';
else
++j;
++i;
}
to[j] = '\0';
}
Упражнение 1.21. Напишите программу entab, заменяющую строки из пробелов минимальным числом табуляций и пробелов таким образом, чтобы вид напечатанного текста не изменился. Используйте те же “стопы” табуляции, что и в detab. В случае, когда для выхода на очередной “стоп” годится один пробел, что лучше -- пробел или табуляция?

/* entab: преобразует символы пробелов 'from' в заданное
* число пробелов и табуляций 'to' */
void entab(char to[], char from[])
{
int i, j, k, ns, nt, c, m;

i = j = ns = nt = 0;
while ((c = from[i]) != '\0') {
if (c == ' ')
++ns;
else if (ns > 0) {
nt = ns/(TABSIZE-1);
for (m = 0; m < nt; ++m, ++j)
to[j] = '\t';
for (k = 0; k < ns-nt*(TABSIZE-1)-(nt-1); ++k, ++j)
to[j] = ' ';
to[j] = c;
ns = 0;
++j;
}
else {
to[j] = c;
++j;
}
++i;
}
to[j] = '\0';
}
Pages: 1234