C++ CSS HTML Java JavaScript MySQL Oracle PERL PHP SQL Unix VBScript XHTML XML Сети
7. Текстовая обработка.
 
7.67.  Составьте программу, которая использует функции работы со стеком для  перевода
арифметических выражений языка Си в обратную польскую запись.

	/*#!/bin/cc $* -lm
	* Калькулятор. Иллюстрация алгоритма превращения выражений
	* в польскую запись по методу приоритетов.
	*/

	<stdio.h>
	#include <stdlib.h> /* extern double atof();	*/
	#include <math.h>   /* extern double sin(),  ...	*/
	#include <ctype.h>  /* isdigit(), isalpha(), ...	*/
	#include <setjmp.h> /* jmp_buf	*/

	jmp_buf AGAIN;	/* контрольная точка */
	err(n){ longjmp(AGAIN,n);} /* прыгнуть в контрольную точку */


	/* ВЫЧИСЛИТЕЛЬ --------------------------------------- */
	/* Если вместо помещения операндов в стек stk[] просто
	* печатать операнды, а вместо выполнения операций над
	* стеком просто печатать операции, мы получим "польскую"
	* запись выражения:
	*	a+b	->	a b +
	*	(a+b)*c   ->	a b + c *
	*	a + b*c   ->	a b c * +
	*/
	/* стек вычислений */
	#define MAXDEPTH 20 /* глубина стеков */
	int sp;	/* указатель стека (stack pointer) */
	double stk[MAXDEPTH];

	double dpush(d) double d; /* занести число в стек */
	{
	if( sp == MAXDEPTH ){ printf("Стек операндов полон\n");err(1);}
	else return( stk[sp++] = d );
	}

	double dpop(){	/* взять вершину стека */
	if( !sp ){ printf("Стек операндов пуст\n"); err(2); }
	else return stk[--sp];
	}
	static double r,p; /* вспомогательные регистры */
	void add()	{ dpush( dpop() + dpop()); }
	void mult()   { dpush( dpop() * dpop()); }
	void sub()	{ r = dpop(); dpush( dpop() - r); }
	void divide() { r = dpop();
	if(r == 0.0){ printf("Деление на 0\n"); err(3); }
	dpush( dpop() / r );
	}
	void pwr() { r = dpop(); dpush( pow( dpop(), r )); }
	void dup() { dpush( dpush( dpop())); }
	void xchg(){ r = dpop(); p = dpop(); dpush(r); dpush(p); }
	void neg() { dpush( - dpop()); }
	void dsin(){ dpush( sin( dpop())); }
	void dcos(){ dpush( cos( dpop())); }
	void dexp(){ dpush( exp( dpop())); }
	void dlog(){ dpush( log( dpop())); }
	void dsqrt(){ dpush( sqrt( dpop())); }
	void dsqr(){ dup(); mult(); }
	/* M_PI и M_E определены в <math.h> */
	void pi()  { dpush( M_PI /* число пи */ ); }
	void e()   { dpush( M_E  /* число e  */ ); }
	void prn() { printf("%g\n", dpush( dpop())); }
	void printstk(){
	if( !sp ){ printf("Стек операндов пуст\n"); err(4);}
	while(sp) printf("%g ", dpop());
	putchar('\n');
	}

	#define OPENBRACKET   1	/* (  */
	#define FUNC	2	/* f( */
	#define CLOSEBRACKET  3	/* )  */
	#define COMMA	4	/* ,  */

	#define PLUS	5	/* +  */
	#define MINUS	6	/* -  */
	#define MULT	7	/* *  */
	#define DIV	8	/* /  */
	#define POWER	9	/* ** */

	/* Приоритеты */
	#define NOTDEF	333   /* не определен */
	#define INFINITY 3000   /* бесконечность */

	
	void push(n, func) void (*func)();
	{
	if(osp == MAXDEPTH){ printf("Стек операций полон\n");err(5);}
	ost[osp].cop = n;  ost[osp++].f = func;
	}
	int pop(){
	if( !osp ){ printf("Стек операций пуст\n"); err(6); }
	return ost[--osp].cop;
	}
	int top(){
	if( !osp ){ printf("Стек операций пуст\n"); err(7); }
	return ost[osp-1].cop;
	}
	void (*topf())(){
	return ost[osp-1].f;
	}
	#define drop()	(void)pop()

	void nop(){ printf( "???\n" ); } /* no operation */
	void obr_err(){ printf( "Не хватает )\n" ); err(8); }


	/* Таблица приоритетов */
	struct synt{
	int inp_prt;	/* входной приоритет	*/
	int stk_prt;	/* стековый приоритет	*/
	void (*op)();   /* действие над стеком вычислений */
	} ops[] = {
	/* BOTTOM	*/  {NOTDEF,   -1,	nop	},
	/* OPENBRACKET  */  {INFINITY,  0,	obr_err},
	/* FUNC	*/  {INFINITY,  0,	obr_err},
	/* CLOSEBRACKET */  {1,	NOTDEF, nop	},  /* NOPUSH */
	/* COMMA	*/  {1,	NOTDEF, nop	},  /* NOPUSH */
	/* PLUS	*/  {1,	1,	add	},
	/* MINUS	*/  {1,	1,	sub	},
	/* MULT	*/  {2,	2,	mult   },
	/* DIV	*/  {2,	2,	divide },
	/* POWER	*/  {3,	3,	pwr	}
	};
	#define stkprt(i)	ops[i].stk_prt
	#define inpprt(i)	ops[i].inp_prt
	#define perform(i) (*ops[i].op)()

	/* значения, заполняемые лексическим анализатором */
	double value; void (*fvalue)();
	int tprev;  /* предыдущая лексема */




	/* Транслятор в польскую запись + интерпретатор */
	void reset(){ sp = osp = 0; push(BOTTOM, NULL); tprev = END;}
	void calc&& t != END){
	if(t == NUMBER){
	if(tprev == NUMBER){
	printf("%g:Два числа подряд\n",value);
	err(9);
	}
	/* любое число просто заносится в стек */
	tprev = t; dpush(value); continue;
	}
	/* иначе - оператор */
	tprev = t;
	/* Выталкивание и выполнение операций со стека */
	while(inpprt(t) <= stkprt( top()) )
	perform( pop());
	/* Сокращение или подмена скобок */
	if(t == CLOSEBRACKET){
	if( top() == OPENBRACKET || top() == FUNC ){
	void (*ff)() = topf();
	drop(); /* схлопнуть скобки */
	/* обработка функции */
	if(ff)	(*ff)();
	}else{ printf( "Не хватает (\n"); err(10); }
	}
	/* Занесение операций в стек (кроме NOPUSH-операций) */
	if(t != CLOSEBRACKET && t != COMMA)
	push(t,   t == FUNC ? fvalue : NULL );
	}
	if( t != EOF ){
	/* Довыполнить оставшиеся операции */
	while( top() != BOTTOM )
	perform( pop());
	printstk();	/* печать стека вычислений (ответ) */
	}
	} while (t != EOF);
	}
	/* Лексический анализатор ---------------------------- */
	extern void getn(), getid(), getbrack();
	int token&& (isspace(c) || c == '\n'));
	if(c == EOF) return EOF;
	ungetc(c, stdin);
	if(isdigit(c)){  getn(); return NUMBER; }
	if(isalpha(c)){ getid(); getbrack(); return FUNC; }
	return getop();
	}


	/* Прочесть число (с точкой) */
	void getn(){
	int c, i;  char s[80];
	s[0] = getchar();
	for(i=1; isdigit(c = getchar()); i++ )  s[i] = c;
	if(c == '.'){   /* дробная часть */
	s[i] = c;
	for(i++; isdigit(c = getchar()); i++)  s[i] = c;
	}
	s[i] = '\0'; ungetc(c, stdin); value = atof(s);
	}
	/* Прочесть операцию */
	int getop(){
	int c;
	switch( c = getchar()){
	case EOF:	return EOF;
	case '=':	return END;
	case '+':	return PLUS;
	case '-':	return MINUS;
	case '/':	return DIV;
	case '*':	c = getchar();
	if(c == '*') return POWER;
	else{ ungetc(c, stdin); return MULT; }
	case '(':	return OPENBRACKET;
	case ')':	return CLOSEBRACKET;
	case ',':	return COMMA;
	default:	printf( "Ошибочная операция %c\n", c);
	return token();
	}
	}
	struct funcs{   /* Таблица имен функций */
	char *fname; void (*fcall)();
	} tbl[] = {
	{ "sin", dsin }, { "cos",   dcos  },
	{ "exp", dexp }, { "sqrt",  dsqrt },
	{ "sqr", dsqr }, { "pi",	pi	},
	{ "sum", add  }, { "ln",	dlog  },
	{ "e",   e	}, { NULL,	NULL  }
	};
	char *lastf;	/* имя найденной функции */
	/* Прочесть имя функции */
	void getid ptr->fname; ptr++ )
	if( !strcmp(ptr->fname, name)){
	fvalue = ptr->fcall;
	lastf =  ptr->fname; return;
	}
	printf( "Функция \"%s\" неизвестна\n", name ); err(11);
	}

	/* прочесть открывающую скобку после имени функции */
	void getbrack(){
	int c;
	while((c = getchar()) != EOF && c != '(' )
	if( !isspace(c) && c != '\n' ){
	printf("Между именем функции %s и ( символ %c\n", lastf, c);
	ungetc(c, stdin); err(12);
	}
	}
	void main(){ calc();}
	/* Примеры:
	( sin( pi() / 4 + 0.1 ) + sum(2, 4 + 1)) * (5 - 4/2) =
	ответ: 23.3225
	(14 + 2 ** 3 * 7 + 2 * cos(0)) / ( 7 - 4 ) =
	ответ: 24
	*/
7.68.  Приведем еще один арифметический вычислитель, использующий классический рекур-
сивный подход:

	/* Калькулятор на основе рекурсивного грамматического разбора.
	* По мотивам арифметической части программы csh (СиШелл).
	* csh написан Биллом Джоем (Bill Joy).
	: var1 = (x = 1+3) * (y=x + x++)	36
	: s = s + 1	ошибка
	: y	9
	: s = (1 + 1 << 2) == 1 + (1<<2)	0
	: var1 + 3 + -77	-38
	: a1 = 3; a2 = (a4=a3 = 2; a1++)+a4+2	8
	: sum(a=2;b=3, a++, a*3-b)	12
	*/
	#include <stdio.h>
	#include <ctype.h>
	#include <setjmp.h>

	typedef enum { NUM, ID, OP, OPEN, CLOSE, UNKNOWN, COMMA, SMC } TokenType;

	char *toknames[] = { "number", "identifier", "operation",
	"open_paren", "close_paren", "unknown", "comma", "semicolon" };

	typedef struct _Token {
	char *token;	/* лексема (слово)	*/
	struct _Token *next;	/* ссылка на следующую */
	TokenType type;	/* тип лексемы	*/
	} Token;

	extern void *malloc(unsigned); extern char *strchr(char *, char);

	char *strdup(const char *s){
	char *p = (char *)malloc(strlen(s)+1);
	if(p) strcpy(p,s); return p;
	}

	/* Лексический разбор ------------------------------------------*/
	/* Очистить цепочку токенов */
	void freelex(Token **p){
	Token *thisTok = *p;
	while( thisTok ){ Token *nextTok = thisTok->next;
	free((char *) thisTok->token); free((char *) thisTok);
	thisTok = nextTok;
	}
	*p = NULL;
	}
	/* Добавить токен в хвост списка */
	void addtoken(Token **hd, Token **tl, char s[], TokenType t){
	Token *newTok = (Token *) malloc(sizeof(Token));
	newTok->next  = (Token *) NULL;
	newTok->token = strdup(s); newTok->type = t;
	if(*hd == NULL) *hd = *tl = newTok;
	else{  (*tl)->next = newTok; *tl = newTok; }
	}
	/* Разобрать строку в список лексем (токенов) */
	#define opsym(c) ((c) && strchr("+-=!~^|&*/%<>", (c)))
	#define is_alpha(c) (isalpha(c) || (c) == '_')
	#define is_alnum(c) (isalnum(c) || (c) == '_')

	void lex(Token **hd, Token **tl, register char *s){
	char *p, csave; TokenType type;

	 '('){	type = OPEN;  s++; }
	else if(*s == ')'){	type = CLOSE; s++; }
	else if(*s == ','){	type = COMMA; s++; }
	else if(*s == ';'){	type = SMC;   s++; }
	else if(opsym(*s)){	type = OP;  while(opsym(*s))  s++; }
	else {	type = UNKNOWN;	s++; }
	csave = *s; *s = '\0'; addtoken(hd, tl, p, type); *s = csave;
	}
	}
	/* Распечатка списка лексем */
	void printlex(char *msg, Token *t){
	if(msg && *msg) printf("%s: ", msg);
	for(; t != NULL; t = t->next)
	printf("%s`%s' ", toknames[(int)t->type], t->token);
	putchar('\n');
	}

	/* Система переменных ----------------------------------------- */
	#define NEXT(v)	*v = (*v)->next
	#define TOKEN(v)	(*v)->token
	#define TYPE(v)	(*v)->type
	#define eq(str1, str2)  (!strcmp(str1, str2))
	jmp_buf breakpoint;
	#define ERR(msg,val) { printf("%s\n", msg);longjmp(breakpoint, val+1);}

	typedef struct {
	char *name;	/* Имя переменной	*/
	int value;	/* Значение переменной */
	int isset;	/* Получила ли значение ? */
	} Var;
	#define MAXV 40
	Var vars[MAXV];
	/* Получить значение переменной */
	int getVar(char *name){ Var *ptr;
	for(ptr=vars; ptr->name; ptr++)
	if(eq(name, ptr->name)){
	if(ptr->isset) return ptr->value;
	printf("%s: ", name); ERR("variable is unbound yet", 0);
	}
	printf("%s: ", name); ERR("undefined variable", 0);
	}
	/* Создать новую переменную	*/
	Var *internVar(char *name){ Var *ptr;
	for(ptr=vars; ptr->name; ptr++)
	if(eq(name, ptr->name)) return ptr;
	ptr->name = strdup(name);
	ptr->isset = 0; ptr->value = 0; return ptr;
	}
	/* Установить значение переменной */
	void setVar(Var *ptr, int val){ ptr->isset = 1; ptr->value = val; }

	/* Распечатать значения переменных */
	void printVars(){ Var *ptr;
	for(ptr=vars; ptr->name; ++ptr)
	printf("\t%s %s %d\n", ptr->isset ? "BOUND  ":"UNBOUND",
	ptr->name, ptr->value);
	}


	/* Синтаксический разбор и одновременное вычисление ----------- */
	/* Вычисление встроенных функций */
	int apply(char *name, int args[], int nargs){
	if(eq(name, "power2")){
	if(nargs != 1) ERR("power2: wrong argument count", 0);
	return (1 << args[0]);
	} else if(eq(name, "min")){
	if(nargs != 2) ERR("min: wrong argument count", 0);
	return (args[0] < args[1] ? args[0] : args[1]);
	} else if(eq(name, "max")){
	if(nargs != 2) ERR("max: wrong argument count", 0);
	return (args[0] < args[1] ? args[1] : args[0]);
	} else if(eq(name, "sum")){ register i, sum;
	for(i=0, sum=0; i < nargs; sum += args[i++]);
	return sum;
	} else if(eq(name, "rand")){
	switch(nargs){
	case 0:  return rand();
	case 1:  return rand() % args[0];
	case 2:  return args[0] + rand() % (args[1] - args[0] + 1);
	default: ERR("rand: wrong argument count", 0);
	}
	}
	ERR("Unknown function", args[0]);
	}
	/* Вычислить выражение из списка лексем.	*/
	/* Синтаксис задан праворекурсивной грамматикой */
	int expr(Token *t){ int val = 0;
	if(val = setjmp(breakpoint)) return val - 1;
	val = expression(&t);
	if(t){ printlex(NULL, t); ERR("Extra tokens", val); }
	return val;
	}
	/* <EXPRESSION> =   <EXPASS>  |
	<EXPASS>  ";" <EXPRESSION>	*/
	int expression(Token **v){ int arg = expass(v);
	if(*v && TYPE(v) == SMC ){
	NEXT(v); return expression(v);
	} else return arg;
	}
	/* <EXPASS> =	<ПЕРЕМЕННАЯ> "=" <EXPASS> |
	<EXP0>	*/
	int expass(Token **v){ int arg;
	if(*v && (*v)->next && (*v)->next->type == OP &&
	eq((*v)->next->token, "=")){ Var *ptr;
	/* присваивание (assignment) */
	if( TYPE(v) != ID ) /* слева нужна переменная */
	ERR("Lvalue needed", 0);
	ptr = internVar(TOKEN(v));
	NEXT(v); NEXT(v); setVar(ptr, arg = expass(v)); return arg;
	}
	return exp0(v);
	}

	/* <EXP0>  =  <EXP1>  |   <EXP1> "||" <EXP0>  */
	int exp0(Token **v){ int arg = exp1(v);
	if(*v && TYPE(v) == OP && eq(TOKEN(v), "||")){
	NEXT(v); return(exp0(v) || arg );
	/* помещаем arg ВТОРЫМ, чтобы второй операнд вычислялся
	* ВСЕГДА (иначе не будет исчерпан список токенов и
	* возникнет ошибка в expr(); Это не совсем по правилам Си.
	*/
	} else return arg;
	}
	/* <EXP1>  =  <EXP2>  |   <EXP2> "&&" <EXP1>	*/
	int exp1(Token **v){ int arg = exp2(v);
	if(*v && TYPE(v) == OP && eq(TOKEN(v), "&&")){
	NEXT(v); return(exp1(v) && arg);
	} else return arg;
	}
	/* <EXP2>  =  <EXP2A>  |   <EXP2A> "|" <EXP2>	*/
	int exp2(Token **v){ int arg = exp2a(v);
	if(*v && TYPE(v) == OP && eq(TOKEN(v), "|")){
	NEXT(v); return( arg | exp2(v));
	} else return arg;
	}
	/* <EXP2A>  =  <EXP2B>  |   <EXP2B> "^" <EXP2A>  */
	int exp2a(Token **v){ int arg = exp2b(v);
	if(*v && TYPE(v) == OP && eq(TOKEN(v), "^")){
	NEXT(v); return( arg ^ exp2a(v));
	} else return arg;
	}
	/* <EXP2B>  =  <EXP2C>  |   <EXP2C> "&" <EXP2B>  */
	int exp2b(Token **v){ int arg = exp2c(v);
	if(*v && TYPE(v) == OP && eq(TOKEN(v), "&")){
	NEXT(v); return( arg & exp2b(v));
	} else return arg;
	}
	/* <EXP2C>  =  <EXP3>  |   <EXP3> "==" <EXP3>
	|   <EXP3> "!=" <EXP3>	*/
	int exp2c&& TYPE(v) == OP && eq(TOKEN(v), "==")){
	NEXT(v); return( arg == exp3(v));
	} else if(*v && TYPE(v) == OP && eq(TOKEN(v), "!=")){
	NEXT(v); return( arg != exp3(v));
	} else return arg;
	}

	/* <EXP3>  =  <EXP3A>  |   <EXP3A> ">"  <EXP3>
	|   <EXP3A> "<"  <EXP3>
	|   <EXP3A> ">=" <EXP3>
	|   <EXP3A> "<=" <EXP3>	*/
	int exp3(Token **v){ int arg = exp3a(v);
	if(*v && TYPE(v) == OP && eq(TOKEN(v), ">")){
	NEXT(v); return( arg && exp3(v));
	}else if(*v && TYPE(v) == OP && eq(TOKEN(v), "<")){
	NEXT(v); return( arg && exp3(v));
	}else if(*v && TYPE(v) == OP && eq(TOKEN(v), ">=")){
	NEXT(v); return( arg && exp3(v));
	}else if(*v && TYPE(v) == OP && eq(TOKEN(v), "<=")){
	NEXT(v); return( arg && exp3(v));
	} else return arg;
	}
	/* <EXP3A>  =  <EXP4>  |   <EXP4> "<<" <EXP3A>
	|   <EXP4> ">>" <EXP3A>	*/
	int exp3a(Token **v){ int arg = exp4(v);
	if(*v && TYPE(v) == OP && eq(TOKEN(v), "<<")){
	NEXT(v); return( arg << exp3a(v));
	}else if(*v && TYPE(v) == OP && eq(TOKEN(v), ">>")){
	NEXT(v); return( arg && exp3a(v));
	} else return arg;
	}
	/* <EXP4>  =  <EXP5>  |   <EXP5> "+" <EXP4>
	|   <EXP5> "-" <EXP4>	*/
	int exp4(Token **v){ int arg = exp5(v);
	if(*v && TYPE(v) == OP && eq(TOKEN(v), "+")){
	NEXT(v); return( arg + exp4(v));
	}else if(*v && TYPE(v) == OP && eq(TOKEN(v), "-")){
	NEXT(v); return( arg - exp4(v));
	} else return arg;
	}
	/* <EXP5>  =  <EXP6>  |   <EXP6> "*" <EXP5>
	|   <EXP6> "/" <EXP5>
	|   <EXP6> "%" <EXP5>	*/
	int exp5(Token **v){ int arg = exp6(v), arg1;
	if(*v && TYPE(v) == OP && eq(TOKEN(v), "*")){
	NEXT(v); return( arg * exp5(v));
	}else if(*v && TYPE(v) == OP && eq(TOKEN(v), "/")){
	NEXT(v); if((arg1 = exp5(v)) == 0) ERR("Zero divide", arg);
	return( arg / arg1);
	}else if(*v && TYPE(v) == OP && eq(TOKEN(v), "%")){
	NEXT(v); if((arg1 = exp5(v)) == 0) ERR("Zero module", arg);
	return( arg % arg1);
	} else return arg;
	}

	/* <EXP6>  = "!"<EXP6> | "~"<EXP6> | "-"<EXP6>
	| "(" <EXPRESSION> ")"
	|  <ИМЯФУНКЦИИ> "(" [ <EXPRESSION> [ "," <EXPRESSION> ]... ] ")"
	|  <ЧИСЛО>
	|  <CH_ПЕРЕМЕННАЯ>	*/
	int exp6&& eq(TOKEN(v), "!")){
	NEXT(v); return !exp6(v);
	}
	if(TYPE(v) == OP && eq(TOKEN(v), "~")){
	NEXT(v); return ~exp6(v);
	}
	if(TYPE(v) == OP && eq(TOKEN(v), "-")){
	NEXT(v); return -exp6(v);	/* унарный минус */
	}
	if(TYPE(v) == OPEN){
	NEXT(v); arg = expression(v);
	if( !*v || TYPE(v) != CLOSE) ERR("Lost ')'", arg);
	NEXT(v); return arg;
	}
	if(TYPE(v) == NUM){  /* изображение числа */
	arg = atoi(TOKEN(v)); NEXT(v); return arg;
	}
	if(TYPE(v) == ID){
	char *name = (*v)->token; int args[20], nargs = 0;
	NEXT(v);
	if(! (*v && TYPE(v) == OPEN)){  /* Переменная */
	return expvar(v, name);
	}
	/* Функция */
	args[0] = 0;
	do{ NEXT(v);
	if( *v && TYPE(v) == CLOSE ) break; /* f() */
	args[nargs++] = expression(v);
	}   while( *v && TYPE(v) == COMMA);

	if(! (*v && TYPE(v) == CLOSE)) ERR("Error in '()'", args[0]);
	NEXT(v);
	return apply(name, args, nargs);
	}
	printlex(TOKEN(v), *v); ERR("Unknown token type", 0);
	}
	/* <CH_ПЕРЕМЕННАЯ>  =   <ПЕРЕМЕННАЯ>	|
	<ПЕРЕМЕННАЯ> "++" |
	<ПЕРЕМЕННАЯ> "--"
	Наши операции ++ и -- соответствуют ++x и --x из Си	*/
	int expvar&& TYPE(v) == OP){
	if(eq(TOKEN(v), "++")){ NEXT(v); setVar(ptr, ++arg); return arg; }
	if(eq(TOKEN(v), "--")){ NEXT(v); setVar(ptr, --arg); return arg; }
	}
	return arg;
	}

	/* Головная функция ------------------------------------------- */
	char input[256];
	Token *head, *tail;

	void main(){
	do{ printf(": "); fflush(stdout);
	if( !gets(input)) break;
	if(!*input){ printVars(); continue; }
	if(eq(input, "!!")) ; /* ничего не делать, т.е. повторить */
	else{ if(head) freelex(&head); lex(&head, &tail, input); }
	printf("Result: %d\n", expr(head));
	} while(1); putchar('\n');
	}
7.69.  Напишите программу, выделяющую n-ое поле из каждой строки файла. Поля разделя-
ются  двоеточиями. Предусмотрите задание символа-разделителя из аргументов программы.
Используйте эту программу для выделения поля "домашний каталог" из файла /etc/passwd.
Для выделения очередного поля можно использовать следующую процедуру:

	main(){
	char c, *next, *strchr(); int nfield;
	char *s = "11111:222222222:333333:444444";

	for(nfield=0;;nfield++){
	if(next = strchr(s, ':')){
	c= *next; *next= '\0';
	}
	printf( "Поле #%d: '%s'\n", nfield, s);
	/* можно сделать с полем s что-то еще */
	if(next){ *next= c; s= next+1; continue; }
	else	{ break; /* последнее поле */	}
	}
	}
7.70.  Разработайте архитектуру и систему команд учебной машины и напишите  интерпре-
татор учебного ассемблера, отрабатывающего по крайней мере такие команды:

	mov пересылка (:=)	add сложение
	sub вычитание	cmp сравнение и выработка признака
	jmp переход	jeq переход, если ==
	jlt переход, если <   jle переход, если <=
	neg изменение знака   not инвертирование признака
7.71.  Напишите программу, преобразующую определения функций Си в  "старом"  стиле  в
"новый" стиль стандарта ANSI ("прототипы" функций).

	f(x, y, s, v)
	int x;
	char *s;
	struct elem *v;
	{ ... }

преобразуется в

	int f(int x, int y, char *s, struct elem *v)
	{ ... }

(обратите внимание, что переменная y и сама функция f описаны по умолчанию как  int).

Еще пример:

	char *ff()	{ ... }
	заменяется на
	char *ff(void){ ... }

В данной задаче вам возможно придется использовать программу lex.
	В списке аргументов прототипа должны быть явно указаны типы  всех  аргументов  -
описатель int нельзя опускать.  Так

	q(x, s) char *s; { ... }  // не прототип, допустимо.
	// x - int по умолчанию.
	q(x,	char *s);	// недопустимо.
	q(int x, char *s);	// верно.

Собственно под "прототипом" понимают предварительное описание функции в новом стиле -
где вместо тела {...} сразу после заголовка стоит точка с запятой.

	long f(long x, long y);	/* прототип */
	...
	long f(long x, long y){ return x+y; }  /* реализация */

В прототипе имена аргументов можно опускать:

	long f(long, long);	/* прототип */
	char *strchr(char *, char);

Это предварительное описание помещают  где-нибудь  в  начале  программы,  до  первого
вызова функции.  В современном Си прототипы заменяют описания вида

	extern long f();

о которых мы говорили раньше.   Прототипы  предоставляют  программисту  механизм  для
автоматического контроля формата вызова функции.  Так, если функция имеет прототип

	double f( double );

и вызывается как

	double x = f( 12 );

то компилятор автоматически превратит это в

	double x = f( (double) 12 );

(поскольку существует приведение типа от int к double); если же написано

	f( "привет" );

то компилятор сообщит об ошибке (так как нет преобразования типа (char *) в double параметров;
c)   тип возвращаемого функцией значения.

Прототипы обычно помещают в include-файлы. Так в ANSI стандарте Си предусмотрен файл,
подключаемый

	#include <stdlib.h>
в котором определены прототипы функций из стандартной библиотеки языка  Си.  Черезвы-
чайно  полезно  писать  эту директиву include, чтобы компилятор проверял, верно ли вы
вызываете стандартные функции.
	Заметим, что если вы определили прототипы каких-то функций, но в своей программе
используете  не все из этих функций, то функции, соответствующие "лишним" прототипам,
НЕ будут добавляться к вашей программе из библиотеки. Т.е.  прототипы - это  указание
компилятору;  ни  в какие машинные команды они не транслируются. То же самое касается
описаний внешних переменных и функций в виде

	extern int x;
	extern char *func();

Если вы не используете переменную или функцию с таким именем, то эти строки не  имеют
никакого эффекта (как бы вообще отсутствуют).

7.72.  Обратная задача: напишите преобразователь из нового стиля в старый.

	int f( int x, char *y ){ ... }

переводить в

	int f( x, y ) int x; char *y; { ... }
7.73. для одной и той же функции.  Так бывает,  когда  вы
поменяли  функцию  и  прототип в одном файле, быть может во втором, но забыли сделать
это в остальных.

	--------
	файл a.c
	--------
	void g(void);
	void h(void);

	int x = 0, y = 13;

	void f(int arg){
	printf("f(%d)\n", arg);
	x = arg;
	x++;
	}

	int main(int ac, char *av[]){
	h();
	f(1);
	g();
	printf("x=%d y=%d\n", x, y);
	return 0;
	}

	--------
	файл b.c
	--------
	extern int x, y;

	int f(int);

	void g(){
	y = f(5);
	}
	--------
	файл c.c
	--------
	void f();

	void h(){
	f();
	}

Выдача программы:

	abs@wizard$ cc a.c b.c c.c -o aaa
	a.c:
	b.c:
	c.c:
	abs@wizard$ aaa
	f(-277792360)
	f(1)
	f(5)
	x=6 y=5
	abs@wizard$

  чтобы  все  файлы
программы  включали одни и те же прототипы.  Стараться, чтобы этот include-файл вклю-
чался также в файл с самим определением функции.  В  таком  случае  изменение  только
заголовка  функции  или только прототипа вызовет ругань компилятора о несоответствии.
Вот как должен выглядеть наш проект:

	-------------
	файл header.h
	-------------
	extern int x, y;
	void f(int arg);
	int main(int ac, char *av[]);
	void g(void);
	void h(void);


	--------
	файл a.c
	--------
	#include "header.h"

	int x = 0, y = 13;

	void f(int arg){
	printf("f(%d)\n", arg);
	x = arg;
	x++;
	}

	int main(int ac, char *av[]){
	h();
	f(1);
	g();
	printf("x=%d y=%d\n", x, y);
	return 0;
	}
	--------
	файл b.c
	--------
	#include "header.h"

	void g(){
	y = f(5);
	}
	--------
	файл c.c
	--------
	#include "header.h"

	void h(){
	f();
	}

Попытка компиляции:

	 line 4: prototype mismatch: 0 args passed, 1 expected
	cc: acomp failed for c.c

[Назад] [Содержание] [Вперед]

Главная