Некоторые примеры из семинарских занятий по Си (1114741), страница 2
Текст из файла (страница 2)
Один из возможных вариантов ее реализации имеетследующий вид:for (i = 0; i < n; i++)for (j = 0;j < m; j++)if (а[i] == b[j])goto found ; /* break здесь не подойдет, так как онзавершит только внутренний цикл for *//* нет одинаковых элементов */…found:/* обнаружено совпадение: а[i] == b[j] */Программу нахождения совпадающих элементов можно написать и без goto, введяпеременную found.
Она имеет значение «ложь», пока совпадения не найдено, и«истина», как только обнаружены совпадающие элементы. Проверка значения foundвходит в условия продолжения каждого из двух вложенных циклов for:found =0;for (i=0; i < n && !found; i++)for (j = 0; j <m && !found j++)if (а[i] == b[j])found = 1;if (found)/* обнаружено совпадение: а[i-1] == b[j-1] */еlsе/* нет одинаковых элементов */…Еще один разумный вариант, где можно использовать goto, это тело функции,эмулирующей поведение некоторого конечного автомата (про конечные автоматыматериал будет в дальнейших лекциях, посвященных теории трансляции).Приведение типаВ языке Си есть префиксная операция явного приведения типа (ее уровень в таблицеприоритетов – 14):(тип) выражениевыражение должно быть первичным (см.
материал про выражения на сайте) илисостоящим из операции не ниже четырнадцатого уровня. Результатом будет значениеуказанного типа. Способ преобразования к новому типу – см. таблицы в приложении кзадачнику Руденко. Результат не является l-обозначением, то есть не может стоять в левойчасти присваивания.Квалификатор constconst int ci=5; /* ci – неизменяемое целое, проинициализированное значением 5 */ci=6; /* ошибка: попытка изменить неизменяемое целое*/int * pi; /* указатель на целое */pi = (int *) &ci; /* явное приведение типа, к указателю на изменяемое целое */*pi=6; /* может быть ошибка во время выполнения из-за попытки записи в память,доступной только для чтения; в некоторых реализациях ошибки не будет */const int * pci = & ci; /* указатель на неизменяемое целое */*pci = 6; /* ошибка: попытка изменить неизменяемое целое*/const int b =7;pci = &b; /* правильно: сам указатель изменять можно */int a,b;int * const cpi = & a; /* cpi -- неизменяемый указатель на целое */cpi = & b ; /* ошибка: попытка изменить неизменяемы указатель */*cpi =10; /* правильно: содержимое, на которое указывает cpi изменять можно */const int * const cpci = & ci; /* неизменяемый указатель на неизменяемоецелое */Примеры выраженийВыражение(unsigned) (unsigned short)0xFFFFFFдает значение 0xFFFF при условии, что в реализации под значения типа short отводится 16бит, под значения типа int – 32 бита.Выражение(double) (float) 3.1415926535897932384может дать значение меньшей точности, чем исходная константа.
Операции явногоприведения типа относятся к префиксным и группируются справа налево:(double)( (float)3.1415926535897932384 )Еще примеры:unsigned i= -1; /* переменная i получит значение 65535, если под тип intотводится 16 бит */(1.0 3)+(unsigned)1; /* результат равен –1.0 ; */1.0 +(3 +(unsigned)1); /* результат – большое число; если в данной реализациипод тип unsigned отводится 16 бит, то результат равен 1.0 + (216 3) + 1 = 65535.0; типрезультата double */Применение операции «запятая»Операция «запятая» обычно применяется в тех местах программы, где по синтаксисуязыка Си требуется одно выражение, но по смыслу нужно выполнить нескольковыражений с побочными эффектами, например – начальная инициализация в for-цикле.for (x=0,y=N; x< N && y>0; x++,y--) { … }Запятая в вызове функции всегда трактуется как разделитель в списке аргументов, а неоперация-запятая:f(a,b=2,5*b,c); /* вызов функции с четырьмя аргументами */f(a,(b=2,5*b),c); /* это вызов функции с тремя аргументами, второй аргумент –это выражение с операцией запятая */В процедуре сортировки методом пузырька bubble_sort( ) вместо трех операторовдля обмена значениями элементов массиваtemp=mas[j+1];mas[j+1]=mas[j];mas[j]=temp;можно использовать один оператор-выражение c операцией «запятая»:temp=mas[j+1],mas[j+1]=mas[j],mas[j]=temp;Условные выраженияСледующий оператор-выражение, содержащий условное выражение,r=a?b:c;эквивалентен условному операторуif (a!= 0) r=b;else r=c;Выражение с тремя последовательными условными операциямиa?b:c?d:e?f:gв силу правой ассоциативности операции ?: интерпретируется какa?b:(c?d:(e?f:g))Тип результата для выраженияa==b ? 1.0 : 2всегда вещественный, так как выполняется балансировка 2-го и 3-го операндов.Логические операторные выраженияВ языке Си логические операции И (&&) и ИЛИ (||) возвращают значения 0 («ложь») или1 («истина») и вычисляются по «ленивой» (сокращенной) схеме.
Сначала вычисляетсялевый операнд, и, если по его значению можно определить результат операции (0, т.е.«ложь» в случае И при нулевом значении левого операнда; 1, т.е. «истина» в случае ИЛИпри ненулевом значении левого операнда), то правый операнд не вычисляется. Впротивном случае вычисления продолжаются: вычисляется значение правого операнда –если он ненулевой, то значением операции будет 1 («истина»), иначе 0 («ложь»).Оператор-выражениеr=a&&b;эквивалентен условному операторуif (a==0) r=0;else{if (b==0) r=0;else r=1;}Операторr=a||b;эквивалентен операторуif (a!=0) r=1;else {if (b!=0) r=1;else r=0;}«Ленивая» семантика логических операций позволяет, например, в задачах обработкимассивов совмещать проверку выхода за границу массива и значения текущего элементамассива в одном логическом выражении.
В Паскале такое было невозможно. В самомделе, операторifi<=N then if mas[i]<>0 then s:=s+1нельзя заменить наif (i<=N)and (mas[i]<>0) then s:=s+1, так как правый операнд операцииand может быть вычислен независимо от левого, и при i>N произойдет ошибка —выход за границу массива. Поскольку в Си вычисление логических операций идет слеванаправо с досрочным прекращением вычислений, аналогичные операторы на Сиif (i<=N) if (mas[i]<>0) s=s+1; иif(i<=N && mas[i]<>0) s=s+1; эквивалентны: второй можно считатьсокращенной записью первого.В языке Си приоритет логических операций ниже, чем у отношений, поэтому скобкивокруг отношений в выражении i<=N && mas[i]<>0 не нужны.Покажем на примере сортировки массива простыми вставками возможность совмещенияв заголовке цикла проверки границы массива и текущего элемента.Сортировка простыми вставкамиvoid insert_sort(int mas[], int N) {register int i,j, temp;for (i=1; i<N; i++){temp=v[i];for (j=i-1; j>=0 && v[j]>temp; j--)mas[j+1]=mas[j];mas[j+1]=temp;}}В условии цикла j>=0 && v[j]>temp вычислениевыражения v[j]>tempпроизводится, только если истинно j>=0, поэтому выхода за границу массива не будет.Переменная i во внешнем цикле for пробегает значения от 1 до N-1 включительно.
Нашаге i все элементы от mas[i] до mas[i-1] уже отсортированы, и остаетсясортировать только элементы от mas[i] до mas[N]. Переменная j во внутреннемцикле уменьшается от i-1, при этом элементы массива перемещаются по одному вверх,пока не будет найдено место для помещения mas[i] – почему данный метод иназывается методом вставок.
Максимальное число выполняемых операцийпропорционально N*N.Об особенности операции отрицанияВыражениеx==!!x (двойное логическое отрицание) может быть и истинным иложным, т.е. не является тождеством в языке Си. (При x, равном 0 или 1 оно истинно, приостальных x ложно.)Однако в условных конструкциях, например, if(выражение ) или while(выражение )выражения x и !!x взаимозаменяемы, т.е. в этих конструкциях важно равно или неравно значение выражения нулю.
Оба этих выражения принимают значение ноль толькопри x равном нулю. При других значениях x их значения могут отличаться друг отдруга, но оба не равны нулю.Операции отношения и равенстваВыражениеx==y==2ложно в языке Си при любых x, y. Ему эквивалентно выражение (x==y)==2 в силулевой ассоциативности операции ==. Подвыражение (x==y) может принимать значения0 или 1, ни одно из которых не равно 2.
Чтобы сохранить на Си математический смыслдвойного равенства, нужно привлечь логическую операцию И.x==y && y==7Смысл выражения3<x<7в языке Си также отличается от математического. Это выражение всегда истинно, так каконо эквивалентно в силу левой ассоциативности операции ”<” выражению (3<x)<7, азначениями подвыражения (3<x) могут быть только 0 или 1. Чтобы соблюстиматематическую интерпретацию двойного неравенства, следует написать так:3<x && x<7Упражнение:Эквивалентны ли выраженияa<x == x< b иa<x && x< b || b<=x && x<=a?Побитовые операцииВыражениеb << 4 >> 8хотя и выглядит необычно, допустимо в языке Си.
Операции сдвига левоассоциативны,поэтому выражение эквивалентно выражению(b << 4) >> 8В операциях сдвига происходит повышение целочисленности каждого операнда вотдельности, но нет балансировки. Если правый операнд отрицательный, результатнеопределенный. Результат также не определен, если значение правого операндапревышает размер (в битах) значения левого операнда. При сдиге влевоосвобождающиеся битовые позиции заполняются нулями. При сдвиге вправо заполнениенулями гарантируется только для беззнаковых целых или неотрицательных знаковых, дляотрицательных знаковых результат определяется реализацией.Унарные операции + и –Эти операции являются сокращенной записью выражений (0+x) и (0–x).Операция sizeofРезультатом первичного выражения sizeof(тип) над заключенным в скобки именемтипа будет размер объекта этого типа (в основной единице измерения, т.е.