А.А. Белеванцев, С.С. Гайсарян, Л.С. Корухова, Е.А. Кузьменкова, В.С. Махнычев. Семинары по курсу Алгоритмы и алгоритмические языки (1108027), страница 6
Текст из файла (страница 6)
В стандартном потоке ввода задается последовательностьненулевых целых чисел, за которой следует 0. Вывести сначала все отрицательные числаэтой последовательности, а затем – все положительные (в любом порядке).5.3.12. На стандартном потоке ввода задана формула следующего вида:<формула> ::= <цифра> | (<формула> <знак> <формула>)<знак> ::= + | - | *<цифра> ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9Считая, что в записи формулы нет ошибок, описать функцию, вычисляющуюзначение этой формулы.246.
Массивы6.1. Одномерные массивы, адресная арифметикаОдномерные массивы. Определение и инициализация массива, доступ к элементаммассива. Указатели и массивы, адресная арифметика. Передача массива в качествепараметра в функцию. Задачи.6.1.1. Одномерные массивыОпределение одномерного массива имеет вид:<тип> <имя> [<количество элементов массива>], где <тип> задает тип элементов массива, при этом важно помнить, что все элементымассива имеют один и тот же тип. При размещении в памяти все элементы массиварасполагаются подряд. Доступ к элементу массива осуществляется по его индексу, вкачестве индекса используются целые неотрицательные числа, индексация элементовмассива начинается с 0.
Таким образом, в массиве из n элементов индексация элементовизменяется в диапазоне от 0 до n-1.Например, определение целочисленного массива x из трех элементов:int x[3]; // элементы x[0], x[1], x[2]При определении массива возможна его полная или частичная инициализацияначальными значениями:intintint//x[3] = {1, 2, 3}; // x[0] = 1, x[1] = 2, x[2] = 3a[10] = {1, 2, 3}; // a[0] = 1, a[1] = 2, a[2] = 3b[10] = {[0] = 1, [3] = 2, [5] = -3};b[0] = 1, b[3] = 2, b[5] = -3Не указанные в инициализации элементы массива в общем случае не получаютникакого начального значения. Исключение составляют глобальные массивы, всеэлементы которых по умолчанию инициализируются нулевыми значениями приопределении массива.Количество элементов массива может объявляться с помощью константы, в этомслучае рекомендуется использовать конструкцию enum.
Например:enum {BUF_SIZE = 100}; // объявление константы BUF_SIZEint buffer[BUF_SIZE];Выход за границу массива обычно не контролируется компилятором, аобнаруживается только на этапе выполнения программы. Во избежание подобной ошибкипрограммист должен контролировать данную ситуацию самостоятельно.Во всех приведенных выше примерах размер массива задается константой, однакостандарт Си-99 допускает также использование массивов переменной длины в качествелокальных массивов. Например, ввод целочисленного массива из n элементов в стандартеСи-99 реализуется следующим фрагментом:int n;25scanf ("%d", &n);// ввод количества элементов массиваint x[n];for (int i =0; i < n; i++) {// ввод массиваscanf ("%d", &x[i]);}Задача 1. Укажите, какому элементу массива будет присвоено значение 12, а такжезначение переменной i после выполнения следующего фрагмента программы:int i = 8;a[i++] = 12;Решение: постфиксная операция ++ увеличит значение переменной i после ееиспользования в выражении.
Следовательно, ответ: a[8] = 12, i = 9.Задача 2. На стандартном потоке ввода задан текст, состоящий из латинских букв ицифр и оканчивающийся точкой. На стандартный поток вывода вывести цифру, наиболеечасто встречающуюся в тексте (если таких цифр несколько, вывести любую из них).Для решения задачи сформируем массив счетчиков вхождений цифр в заданныйтекст, где в i-ом элементе массива будем накапливать количество вхождений в текстцифры со значением i. Если символьная переменная c содержит цифру, тогдавыражение c – '0' вычисляет целочисленное значение этой цифры и, следовательно,индекс соответствующего счетчика в массиве.
Сформировав массив счетчиков, найдеминдекс (ind_max) максимального элемента в этом массиве. Тогда выражениеind_max + '0' и будет определять искомую цифру.#include <stdio.h>intmain (void){int digits[10]; // массив счетчиков// инициализация массива счетчиковfor (int i =0; i < 10; i++) {digits[i] = 0;}// ввод текста и формирование массива счетчиковchar c; // очередной символwhile ((c = getchar ()) != '.') {if (c >= '0' && c <= '9') {++digits[c – '0'];}}// поиск индекса максимального элементаint max = digits[0], ind_max = 0;for (int i =1; i < 10; i++) {if (digits[i] > max) {max = digits[i];ind_max = i;}}printf ("%c\n", ind_max + '0');return 0;}266.1.2.
Указатели и массивыИмя массива является константным указателем на нулевой элемент массива, т.е.имеет тип:int * const;Изменять значение такого указателя нельзя, например, ошибкой было бы написатьследующее присваивание:int a[10];int b[10];a = b; // ЗАПРЕЩЕНО!- попытка изменить значение константы aОднако можно установить обычный (неконстантный) указатель на начало массива изатем изменять его значение по своему усмотрению, например:int a[10];int *p;p = a;В результате указатель p будет указывать на начало (нулевой элемент) массива a.Для вычисления размера памяти (в байтах), необходимой для хранения объекта,используется операция sizeof.
Так, значением выражения sizeof(<тип>) являетсяколичество байтов, необходимых для размещения в памяти значения указанного типа,выражение sizeof(a), где a – имя некоторой переменной, возвращает количествобайтов памяти, отводимых компилятором для хранения переменной a. Для массива a из nэлементов sizeof(a) = sizeof(<тип>) * n, где <тип> задает тип элементовмассива. Например, при условии, что для хранения значения типа int отводится 4 байтапамяти, для массива a из приведенного выше примераsizeof(a) = sizeof(int) * 10 = 4 * 10 = 40,Обратите внимание, чтоsizeof(a) ≠ sizeof(p), так как результатомвычисления выражения sizeof(p) является размер памяти, отводимой компиляторомпод указатель p (под объект int *), обычно это 4 байта.6.1.3. Адресная арифметикаНад указателями определены следующие основные операции:добавление и вычитание целочисленной константы,сравнение (==, !=, <, >, <=, >=),вычитание указателей одного типа.Например:intintp =x =a[10];*p, x;a;*(p + 8); // x = a[8]27При вычислении выражения p + 8 адрес, хранящийся в указателе p, складывается созначением8*sizeof(int), в результате получаем адрес восьмого элемента массива(&a[8]).
После выполнения операции разыменования *(p + 8) в переменной x будетзначение восьмого элемента массива (a[8]). Обратите внимание на необходимостьиспользования скобок в выражении *(p + 8). Сравните:x = *p + 8; // x = a[0] + 8x = *(p + 8); // x = a[8]Аналогично выражениеp - i, где i – некоторая целочисленная константа,задает указатель на i -ый перед p элемент массива.При выполнении операций +=, -=, ++, -- указатель передвигается насоответствующее количество элементов массива. Например,p += 8; // p = &a[8]при условии, что перед выполнением операции p указывал на начало массива a.Указатели одинакового типа можно сравнивать и вычитать. Эти операции имеетсмысл выполнять при условии, что оба указателя указывают на элементы одного и того жемассива. Так, если указатели p и q указывают на элементы одного и того же массива ипри этом номер элемента, на который указывает p, меньше номера элемента с указателемq, то справедливо соотношение:p < q.При вычитании указателей вычисляется разность адресов, хранящихся в этихуказателях, которая затем делится на размер (в байтах) элемента массива.
Результатомявляется количество элементов массива, расположенных между данными указателями.Например:int a[10];int *p, *q;p = a;q = &a[8];// q – p = (&a[8] - &a[0]) / sizeof(int) = 8Задача 3. Что будет напечатано в результате выполнения следующего фрагмента:int a[3] = {1, 2, 3};int *px, x = 5;px = a + 1;*px-- = x – 10;printf("%d\n", *px);printf("%d %d %d\n", a[0], a[1], a[2]);Рассмотрим выполнение присваиваний из данного фрагмента.px = a + 1; // px = &a[1]*px-- = x – 10; // a[1] = -5, px = &a[0]Особый интерес представляет выполнение второго присваивания, где сначалавычисляется значение выражения в правой части присваивания (x – 10 = -5), затемэто значение присваивается по указателю px (a[1] = -5), и указатель передвигается28на предыдущий элемент массива (px = &a[0]).