Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Прикладное программирование на CC++ с нуля до мультимедийных и сетевых приложений

.pdf
Скачиваний:
0
Добавлен:
07.09.2026
Размер:
2 Мб
Скачать
☆
Массивы 31
Массивы
Если вам приходится работать с большими объемами данных, то неизбеж­но возникает проблема задания большого количества имен переменных, с ко­торыми обращается программа. Однако это еще не проблема по сравнению с тем, что нужно найти способ компактного описания однотипных действий с большим количеством однотипных данных. В математике эти проблемы давно решены с помощью введения переменных с индексами (номерами) — векто­ров, тензоров. Этим же путем пошли и разработчики многих языков програм­мирования, включая С.
Итак, массивом называется совокупность переменных с одинаковым име­нем и снабженных индексами. Сразу уточним, что все компоненты массива имеют один и тот же тип. Сами же массивы могут быть любых типов, как и простые переменные. Проиллюстрируем способ описания массивов и методы работы с массивами в языке С на программе, вычисляющей скалярное произ­ведение двух векторов. Вектор является типичным представителем массивов. В трехмерном пространстве, например декартовом, вектор R есть совокуп­ность трех компонентов: R
x,Ry,Rz
ные обозначения индексов на числовые. Для того чтобы ближе подойти к правилам описания и использования массивов в С, обозначим компоненты вектора как R
0,R1,R2
.
Как известно читателю, скалярное произведение двух векторов c=abåñòü сумма произведений соответствующих компонентов:
. Ничто не мешает нам заменить буквен-
a
0b0+a1b1+a2b2
.
Тогда программа, выполняющая поставленную задачу, может выглядеть следующим образом:
void main() { int i; double c=0.,a[3],b[3]; for(i=0; i<3; i++) { printf("a[%d]="); scanf("lf",&a[i]); printf("b[%d]="); scanf("&lf".&b[i]); } for(i=0; i<3; i++) c+=a[i]*b[i]; printf("c=%lf",c); }
Описание массива аналогично описанию переменной, с той лишь разни­цей, что после имени массива следуют прямые скобки, в которых указывается число элементов в массиве. В данном случае описаны два массива: à è b, эле­менты которых есть вещественные числа (типа double). Каждый массив состо­ит из трех чисел. Теперь подчеркнем важнейший момент, который начинаю­щие, как правило, упускают из вида, что приводит к серьезным неприятно­стям при выполнении программы. Автор настоятельно советует читателю несколько раз повторить про себя, что ЭЛЕМЕНТЫ МАССИВОВ НУМЕРУ ЮТСЯ, НАЧИНАЯ С НУЛЯ, а не с единицы, хотя последнее, возможно, бо
-
-
32 Глава 1. Язык программирования С
лее привычно. В нашем примере массив à состоит из трех элементов: à[0], à[1], à[2]. Уясните себе, что если массив описан, например, как Ð[5], òî â
нем последним элементом является Ð[4] и вовсе нет элемента Ð[5], зато есть элемент Ð[0]. Автору не хотелось бы показаться занудой, но он вынужден вновь и вновь возвращать читателя на несколько строчек назад, к выделенно­му заглавными буквами тексту. Основная беда здесь заключается в том, что компилятор (по крайней мере, в TURBO Ñ) спокойно пропускает ситуацию, когда программист допускает «выход за границы массива» — система ошибки не обнаруживает. При этом во время выполнения программы возникает кон­фликт в оперативной памяти компьютера, который довольно трудно распо­знать. Очень часто именно по этой причине возникает фатальное «зависание» системы, и единственным выходом из такой ситуации является перезагрузка компьютера.
Последняя программа показывает, как следует обращаться к отдельным элементам массива: пишется имя массива, а в прямых скобках его индекс. Очевидно, что использование массивов в сочетании с циклами позволяет реа­лизовывать однотипные операции над большими объемами данных.
В предыдущей программе заполнение массивов конкретными значения­ми — инициализация массивов делалась через ввод значений каждого из эле­ментов с консоли с использованием приглашения и функции scanf. Однако аналогично тому, как начальная инициализация простых переменных может быть выполнена непосредственно в описаниях, точно так и массивы можно инициализировать в описаниях. Следующая программа, также выполняющая скалярное произведение векторов, демонстрирует начальную инициализацию массивов:
void main()
{ int i=2;
double a[]={0.23,-1.45,2.04},
b [] =={-3.12.0.03,4.66}, c=0.0;
for(;i>=0;) c+=a[i]*b[i- -] ;
printf("c=%lf",c); }
При задании компонентов массивов в описаниях присваиваемые значе­ния перечисляются в фигурных скобках через запятую. Как видно из текста программы, в этом случае количество элементов в массиве — длину массива можно не указывать и в описании после имени массива оставить пустые пря­мые скобки. Действительно, количество инициализирующих значений внутри скобок {} автоматически задает длину массива. Если же вы укажете длину массива явно, а в инициализирующем списке напишете значений меньше, чем длина, то будут заданы первые элементы массива, а остальные останутся неопределенными.
Теперь рассмотрим следующую задачу. Сформируем два массива целых чисел по 100 элементов в каждом. Заполним массивы случайными целыми числами в диапазоне от 0 до 500. Выведем на экран номера (индексы) и зна чения попарно совпадающих элементов массивов, продвигаясь по массивам
-
Массивы 33
«сверху вниз», то есть начиная с последних элементов массивов. Задача может быть реализована следующей программой:
void main() {const double f=l./32767*500;
int I,a[100],b[100]; for(i=0; i<100; i++) { a[i]=rand()*f; b[i]=rand()*f; } for(; - -i>==0;) if(a[i]==b[i]) printf("a[%d]=b[%d]=%d \n",i,i,a[i]); }
Сначала в программе задается глобальная вещественная константа f, ко­торая, будучи умноженной на функцию rand(), дает вещественное случайное число в нужном диапазоне. В первом цикле for элементам массивов à è b при­даются случайные значения. Поскольку массивы описаны как целые, правые части выражений присваивания автоматически будут приведены к целому типу. Если же вы (как и ваш покорный слуга) с трудом переносите такое вольное обращение с типами данных, то пишите a[i]=(int) (rand()*f). Про­грамма от этого ничего не потеряет, хотя ровным счетом ничего и не приоб­ретет. Из первого цикла программа выходит при значении r, равном 100. Сле­дующий цикл должен просматривать массивы, начиная с 99-х элементов. В этой связи в проверке условия в цикле используется предварительная дек­рементация переменной i. Напомним, что выражение --i>==0 сначала умень­шает i на единицу, а затем производит сравнение уменьшенного значения с нулем.
До сих пор мы говорили о числовых массивах. Массивы, однако, как и простые переменные, могут быть произвольного типа. Определенные особен­ности имеют место для символьных массивов. В этой связи остановимся на массивах типа char более детально.
В принципе обращение с символьными массивами может ничем не отли­чаться от обращения с числовыми массивами. Вот пример инициализации массива символов путем ввода значения каждого элемента с клавиатуры:
viod main() {char a[5],z; int i; for(i=0; i< 5; i++) scanf("%c".&z); a[i]=z;}
Инициализация при описании тоже может быть выполнена обычным об­разом:
void main() { char s[]={'a','b','c'}; printf("%c",s[2]); }
Напомним, что символьная константа — это символ, заключенный в апострофы. В результате работы этой программы на экран будет выведен символ ñ.
Теперь обратим внимание на смысл понятия «массив символов». Это ведь не что иное, как строка в самом житейском понимании. Не правда ли? Такая
34 Глава 1. Язык программирования С
интерпретация символьных массивов породила в С некоторые особые момен­ты. Начальная инициализация строки при описании, наряду со стандартным способом, может быть выполнена несколько проще:
void main()
{ int i=0; char S[]="This is string";
while(S[i++]!='\0');
printf("%d",- -i); }
Массив S инициализирован в виде строки, заключенной в кавычки. Оп­ределенные таким образом символьные массивы, которые будем называть строками, несколько отличаются от массивов, определенных стандартным способом. Во-первых, для них существует целый набор функций, описанных в заголовочном файле string.h, о которых мы поговорим позже. Во-вторых, в конец строки всегда добавляется специальный символ '\0' — признак конца строки. Программисту не нужно заботиться об этом символе, поскольку он вставляется системой автоматически, но помнить о его существовании в кон­це массива следует. Его присутствие может оказаться полезным, как это ис­пользовано в нашей программе. Цикл while здесь каждый раз проверяет, дос­тигли ли мы конца строки, и увеличивает счетчик i на единицу. После выхода из цикла счетчик имеет значение, равное количеству символов в строке, с учетом символа конца строки. Таким образом, в результате работы программы будет напечатана длина строки — в данном случае 14, число символов, поме­щенных между кавычками (включая два символа пробела).
Укажем, что числовой эквивалент символа '\0' есть 0. По этой причине понятно, что условие в цикле можно упростить до вида S[i++]! = 0. Автор был бы безмерно счастлив, если бы в этот момент внимательный читатель воскликнул: «А почему не просто while(S[i++])?». Он прав! Так и сделайте.
В упомянутом заголовочном файле string.h имеется довольно много стро­ковых функций. Рассмотрим некоторые из них. Функция gets(Èìÿ) осуществ­ляет ввод строки с консоли. Здесь Èìÿ есть имя символьного массива, в кото­рый будет записана вводимая с клавиатуры строка. Сам ввод осуществляется следующим образом. Набирается строка целиком так, как она должна выгля­деть. После набора нажимается клавиша Enter. Простейшая программа, ини­циализирующая строку вводом с клавиатуры, выглядит так:
#include< string.h >
void main()
{ char S[100]; gets(S); }
Здесь максимальная длина вводимой строки — 100 символов. При попыт­ке ввода более длинной строки никакой ошибки система не обнаружит, но возникнет уже описанная ситуация с выходом за границы массива с непред­сказуемыми последствиями.
Функция puts(Èìÿ) выводит на экран строку, описанную символьным массивом, названным как Имя. Независимо от объявленной длины массива,
Массивы 35
строка выводится до символа конца строки. Следующая программа произво­дит вывод на экран строки, инициализированной при описании:
#include< string.h >
void main()
{ char SYM[]="QWER'\n'qwer";
puts(SYM); }
В результате работы программы сначала печатается часть строки QWER, затем печатается символ перевода строки, то есть просто происходит переход на новую строчку, где печатается остальная часть qwer.
Еще одна полезная функция из заголовочного файла string.h позволит оп­ределить длину строки без «самодельных» ухищрений, которые мы предпри­нимали ранее. Это функция strlen(Èìÿ), возвращающая целое число, равное длине строки, опять же от начала до признака конца. Теперь вместо состав­ленной ранее программы можно предложить более простую:
void main()
{ char S[]="This is string";
printf("%d",strlen(S)); }
Для операций с матрицами в математике используются переменные со многими индексами. В С для этой цели используются так называемые много­мерные массивы, или массивы массивов. В принципе ограничений на количе­ство индексов нет, но мы ограничимся рассмотрением двухмерных массивов. Описание int x[2][3]; можно рассматривать как два массива, каждый из кото­рых есть набор трех целых чисел. Нам более привычно рассматривать такую конструкцию просто как матрицу — набор компонентов с двумя индексами:
xxx
00 01 02
xxx
10 11 12
В памяти компьютера многомерные массивы располагаются последова­тельно так, что в первую очередь меняется последний индекс, а в послед­нюю — первый. Таким образом, элементы указанного массива расположены в памяти последовательно в следующем порядке:
x[0][0], x[0][1], x[0][2], x[1][0], x[1][1], õ[1][2]
Работу с двухмерными массивами проиллюстрируем простой программой, которая формирует единичную матрицу и выводит ее на экран:
void main()
{ int i.j, Ì[5][5];
for(i=0; i<5; i++)
for(j=0; j<5; j++) if(i==j) M[i][j]=l; else M[i][j]=0;
for(i=0; i<5; i++)
{for(j=0; j<5; j++) printf("%d ",M[i][j]);
printf("\n"); } }
36 Глава 1. Язык программирования С
Как известно, в единичной матрице по главной диагонали стоят единицы, а все остальные элементы равны нулю. Два первых вложенных друг в друга цикла for позволяют перебрать все возможные комбинации индексов, а услов­ное выражение во внутреннем цикле выполняет соответствующее присваива­ние. Можно предложить более элегантное, хотя и менее наглядное выражение вместо конструкции if — else. Необходимый результат дает присваивание M[i] [j]=(i==j);. Вторая пара вложенных циклов обеспечивает печать матрицы на экран именно в виде таблицы из пяти строк и пяти столбцов. Внешний цикл работает пять раз. Пять раз выполняется составное выражение, в кото­ром, в свою очередь, два выражения. Первое выражение — печать значений элементов матрицы. Печать выполняется в одну строчку — печатаются пять чисел, разделенных пробелом. Затем выполняется второе выражение, а это есть переход на новую строку. Далее снова печатается пять чисел и снова про­изводится перевод на новую строку, где, наконец, печатаются последние чис­ла. Заметим, что в данной программе можно было бы обойтись только одной парой циклов, совместив инициализацию матрицы с ее печатью. Разделение на два этапа сделано исключительно для большей наглядности.
Адреса и указатели
Этот материал выделен автором в самостоятельный раздел, несмотря на его небольшой объем, поскольку является крайне важным для дальнейшего, теперь уже достаточно профессионального изучения языка С.
Начать следует с напоминания того факта, что все данные, необходимые для выполнения программы, такие как переменные, константы, массивы и многое другое, включая и сам код программы, размещаются в оперативной памяти компьютера. Память представляет собой совокупность большего числа элементарных ячеек, каждая из которых имеет свой номер или, выражаясь профессионально, адрес. Такая минимальная адресуемая ячейка называется байтом. Байт, в свою очередь, представляет собой совокупность восьми дво­ичных разрядов (битов), в которых записываются 0 или 1. Мы уже говорили о том, что различные типы данных занимают различное число байтов. Так, один символ размещается в одном байте, целое типа int занимает уже два бай­та и так далее. После загрузки исполняемого кода программы в память каждая переменная обзаводится своим адресом, номером первого из группы занимае­мых ею байтов. Адреса принято представлять в шестнадцатеричной системе счисления, и для операций с адресами в С существует специальный формат ð (от английского слова pointer — указатель), а для размещения адресов сущест­вует специальный тип переменных — указатели. В зависимости от того, адре­са каких данных помещаются в указатели, изменяются и сами типы указате­лей. Могут существовать указатели на символы, на целые типа int, на вещест­венные типа double и многие другие. Указатели в программе описываются так: Òèï *Èìÿ. При этом указатели можно объединять с описаниями других дан­ных под общим названием типа. Например, возможно такое описание: double u=7.65,*a;. Здесь описана и инициализирована вещественная перемен-
Адреса и указатели 37
ная двойной точности u и указатель a на объект типа double, пока не инициа­лизированный, то есть не имеющий значения. Для получения адреса объекта в С существует специальная операция &, знак которой помещается перед именем объекта. Таким образом, &u есть адрес переменной u. Понятно, что инициализация указателя (придание ему конкретного значения) может быть сделана простым присваиванием. После выполнения выражения à=&u указа­тель à имеет значение адреса переменной u. Существует операция, обратная операции получения адреса. Такая операция называется разадресацией и име­ет вид *Указатель. Применение такой операции позволяет получить значение, находящееся по адресу, на который указывает указатель. В нашем примере *à есть число, находящееся по адресу à. В свою очередь, à есть адрес перемен­ной u, значение которой равно 7.65. В итоге *à — это просто число 7.65. По­скольку & è * — взаимно обратные операции, очевидно, что &*M åñòü Ì è *&N åñòü N,
что бы ни представляли собой переменные Ì è N.
Может возникнуть вопрос, ответ на который не столь тривиален, как это может показаться. Вопрос простой: в каких единицах измеряется значение указателя и какова величина этих единиц. Сказать, что указатели выражаются в байтах, будет правильно, но мало. Сначала рассмотрим программу:
void main()
{int c=4,*a,*b;
a=&c; b=a+l;
printf("%p %d",a,(int)b-(int)a); }
Здесь описана и инициализирована целая переменная и два указателя на целые (int) переменные. Указатель à получает значение адреса переменной, а указатель b — значение на единицу большее. Затем печатаются адрес перемен­ной и разность указателей, каждый из которых приведен к целому числовому типу. Казалось бы, раз b на единицу больше à, то разность между ними есть 1. При печати вы, однако, увидели бы не 1, а 2. Если заменить в описаниях int íà long, то напечаталось бы 4. Дело в том, что здесь печатается разность номе­ров байтов, начиная с которых размещаются переменные соответствующего типа, и если переменная à размещена в байтах номер N и номер N+1 (òèï int занимает два байта), то следующая переменная, указатель на которую увели­чен на единицу, будет размещена в байтах N+2 è N+3. Поэтому разность ука­зателей и составляет 2 байта, а для типа long — 4 байта.
Принципиальный момент для языка С: условились считать, что имя мас­сива (без индексов) само уже является указателем на начало этого массива, то есть если описан массив t[15], òî t åñòü &t[0]. Тогда понятно, что *t есть зна­чение начального элемента массива и вообще *(t+i) åñòü íå ÷òî èíîå, êàê t[i]. Более того, еще до начала компиляции программы препроцессор, о котором мы вскользь упоминали раньше, производит замену в тексте всех конструк­ций типа u[i] на конструкции *(u+i). Из этого, в частности, следует, что со­вершенно чудовищная, на первый взгляд, конструкция 3[ð] будет совершенно безболезненно пропущена компилятором, и если вы имели в виду ð[3], то все в программе будет в порядке. Тогда, если вы хотите слегка облегчить работу
38 Глава 1. Язык программирования С
системы и уменьшить время обработки (но не исполнения) программы, пи­шите всегда *(ð+4) вместо ð[4].
В заключение параграфа приведем еще одну программу:
viod main() {double m[]={0.6,0.2,1.4,3.3,5.2,7.1,9.2},*p; for(p=m+2; p<m+5; p++) printf("lf\n" ,*p) ; }
Автор тешит себя надеждой, что читатель уже достаточно опытен для того, чтобы понять, что эта программа выведет на экран три числа 1.4, 3.3 и 5.2.
Функции
До сих пор в наших программах мы использовали одну единственную «са­модельную» функцию с именем main — главная. При необходимости програм­ма может содержать достаточно большое количество функций, среди которых, однако, присутствие функции main обязательно, поскольку именно с нее на­чинается выполнение любой программы. Описание и использование «само­дельных» функций демонстрирует следующая программа:
#include<stdio.h> /*Это - первая функция*/ double sqr(double х) { return х*х; }
/*Это - вторая функция*/ double cub(double x) { return sqr(x)*x; }
/*Это - главная функция*/ void main(void) { double у;
for(y=0.; y<10.5; y++)
printf("y=%3.1lf y^2=%3.11f y^3=%3.11f\n", y.sqr(y),cub(y)); }
Начнем с того, что не имеет отношения к описаниям функций, но впер­вые появилось в нашей программе. Речь идет о так называемых комментари­ях. Комментарием является все, что стоит между открывающей конструкцией /* и закрывающей конструкцией */. Присутствие в программе комментариев никакого влияния ни на что не оказывает. Система вообще не видит коммен­тариев, и они предназначены только для человека, читающего программу. Зато этому человеку комментарии могут сообщить полезную информацию, как это и сделано в последней программе. Комментарий в одной строке мож­но организовать и по-другому: напишите конструкцию //. Все, что находится после этой конструкции до конца строки, будет комментарием.
Теперь в нашей программе описана функция с именем sqr. Общая форма описания функции имеет вид:
ТипФункции ИмяФункции(ТипАргумента1 Аргумент1, ТипАргумента2 Аргумент2,...) {Тело функции};
Функции 39
Если назначение функции заключается в вычислении одного значения, то есть, как говорят гуру от программирования, если функция возвращает значе­ние, то тип функции должен быть указан, и таким типом является тип возвра­щаемого значения. В нашей программе функция имеет тип double, стало быть, функция возвращает вещественное число с двойной точностью. Если тип функции не указан, то, по умолчанию, она считается целочисленной функци­ей (типа int). Если функция не возвращает никакого значения, например зна­комая нам функция clrscr, то она имеет тип void — никакой. Несмотря на от­сутствие типа, слово void в описании должно присутствовать. Во всех наших программах функции main не возвращали значений и описывались как void. Функции, возвращающие значение, обязаны содержать в своем теле хотя бы одно так называемое выражение возврата:
return ВозвращаемоеЗначение;
Слово return как раз и означает ВЕРНУТЬ, и после него следует выраже­ние, вычисляющее возвращаемое значение. В функциях сложной структуры, имеющих, например, несколько альтернативных путей выполнения, возмож­но присутствие нескольких выражений return. Нужно помнить, что выполне­ние любого из выражений возврата приводит к немедленному завершению выполнения функции, передаче возвращаемого значения «наружу» и переходу к участку программы, непосредственно следующему за обращением к этой функции. Нетрудно видеть, что функция sqr возвращает квадрат своего аргу­мента. В некоторых языках программирования именно под таким именем су­ществует именно такая функция, вычисляющая квадрат своего аргумента, но не «самодельная», а стандартная. Там она ничем не отличается от других стан­дартных функций: sin, åxð, sqrt и так далее. В С такой стандартной функции нет. Так что если вам непременно хочется ее иметь, то придется изготовить ее своими руками, что в последней программе и сделано.
В скобках после имени функции находится список аргументов, разделен­ных запятыми. Для каждого аргумента указывается его тип. Даже если в спи­ске есть несколько аргументов одного типа, их нельзя объединять под одним словом описания типа, как это, например, можно было делать при объявле­нии переменных. Такая «причуда» имеет своей целью, в частности, уберечь программиста от злоупотребления большим числом аргументов функций. В нашем примере список аргументов состоит из одного параметра, но это только частный случай. Необходимо обратить внимание на одно крайне важ­ное обстоятельство. Принимая свой аргумент, функция делает с него копию и работает уже с этой копией, а не с самим значением. По этой причине, если вы в теле функции измените, как вам кажется, значение аргумента, то после выхода из функции в вызывающую ее часть программы вы с удивлением мо­жете обнаружить, что значение аргумента не изменилось. У такой особенно­сти построения функций в С есть свои преимущества и свои недостатки, ко­торые мы обсудим несколько позднее.
Функция может и не иметь ни одного аргумента. Ну, действительно, за чем параметры могут понадобиться функции clrscr, если ее назначение —
-
40 Глава 1. Язык программирования С
очистка экрана? Она ведь даже не возвращает значения, не имеет типа. В слу­чае отсутствия у функции аргументов вы можете при описании воспользо­ваться пустыми скобками (). Для тех, кто любит во всем порядок, как автор этих строк, посоветуем избавить себя от дискомфорта при взгляде на эти пус­тые скобки и писать (void). Вот мы и полностью разобрались с непонятной поначалу строкой из самой первой нашей программы void main(void). Как ви­дите, автор выполняет свои обещания.
Тело функции — это запрограммированный рецепт выполнения некото­рых действий. Говорить особо о построении тела функции нет смысла, по­скольку до сих пор мы только этим и занимались. Разве что еще раз напом­ним, что функция, имеющая любой тип, отличный от void, должна возвра­щать значение с помощью выражения возврата. В функции sqr тело до крайности простое. Оно даже не содержит ни одного выражения, кроме выра­жения возврата return x*x. С последним все ясно. Вы, конечно, догадываетесь, что такая простая ситуация скорее исключение, чем правило.
Уже следующая функция в нашей программе выглядит чуть сложнее. За­метьте, что вторая функция cub находится в программе после функции sqr и поэтому знает, что функция sqr собой представляет. Аналогично функция main знает обе функции: и sqr,ècub. Такую естественную последовательность рекомендуется соблюдать в программе, хотя несложные средства, демонстри­руемые далее, позволят вам изменить этот порядок.
Функция cub, как это следует из ее имени, вычисляет куб своего аргу­мента. Если бы выражение возврата в ней мы записали в виде return x*x*x; (что было бы не хуже представленного выше варианта), то нам было бы не­чего добавить к тому, что говорилось о функции sqr. Однако здесь функция возведения в куб использует функцию возведения в квадрат, то есть при сво­ей работе функция cub обращается к уже известной ей функции sqr. В свою очередь, главная функция обращается к описанным ранее функциям и ус­пешно распечатывает таблицу квадратов и кубов чисел от 0 до 10 с шагом 1. Мы вполне сознаем, что такую таблицу можно было бы изготовить значи­тельно проще. Программу следует рассматривать только как учебный пример и не более того.
В приведенном примере внутри «самодельных» функций sqr è cub, в отли­чие от функции main, нам не пришлось описывать никаких дополнительных переменных. Однако и это скорее исключение, чем правило. Следующая про­грамма демонстрирует более реалистичную ситуацию:
#include<stdio.h> void main(void) { int i; double x,STEP(); printf("x="); scanf("%lf'.&x); printf("ï="); scanf("%d",&i); printf ("%lf %d=%lf",x,i.STEP(x,i)); } double STEP(double x, int n) { int i; double s=1.0; for(i=l; I<=n; i++) s*=x; return s; }