Какая запись является объявлением статического массива ответ
Массив представляет набор однотипных значений. Объявление массива похоже на объявление обычной переменной, которая хранит одиночное значение, причем есть два способа объявления массива:
тип_данных название_массива[]; // либо тип_данных[] название_массива;
Например, определим массив чисел:
int nums[]; int[] nums2;
После объявления массива мы можем инициализовать его:
int nums[]; nums = new int[4]; // массив из 4 чисел
Создание массива производится с помощью следующей конструкции: new тип_данных[количество_элементов] , где new — ключевое слово, выделяющее память для указанного в скобках количества элементов. Например, nums = new int[4]; — в этом выражении создается массив из четырех элементов int, и каждый элемент будет иметь значение по умолчанию — число 0.
Также можно сразу при объявлении массива инициализировать его:
int nums[] = new int[4]; // массив из 4 чисел int[] nums2 = new int[5]; // массив из 5 чисел
При подобной инициализации все элементы массива имеют значение по умолчанию. Для числовых типов (в том числе для типа char) это число 0, для типа boolean это значение false , а для остальных объектов это значение null . Например, для типа int значением по умолчанию является число 0, поэтому выше определенный массив nums будет состоять из четырех нулей.
Однако также можно задать конкретные значения для элементов массива при его создании:
// эти два способа равноценны int[] nums = new int[] < 1, 2, 3, 5 >; int[] nums2 = < 1, 2, 3, 5 >;
Стоит отметить, что в этом случае в квадратных скобках не указывается размер массива, так как он вычисляется по количеству элементов в фигурных скобках.
После создания массива мы можем обратиться к любому его элементу по индексу, который передается в квадратных скобках после названия переменной массива:
int[] nums = new int[4]; // устанавливаем значения элементов массива nums[0] = 1; nums[1] = 2; nums[2] = 4; nums[3] = 100; // получаем значение третьего элемента массива System.out.println(nums[2]); // 4
Индексация элементов массива начинается с 0, поэтому в данном случае, чтобы обратиться к четвертому элементу в массиве, нам надо использовать выражение nums[3] .
И так как у нас массив определен только для 4 элементов, то мы не можем обратиться, например, к шестому элементу: nums[5] = 5; . Если мы так попытаемся сделать, то мы получим ошибку.
Длина массива
Важнейшее свойство, которым обладают массивы, является свойство length , возвращающее длину массива, то есть количество его элементов:
int[] nums = ; int length = nums.length; // 5
Нередко бывает неизвестным последний индекс, и чтобы получить последний элемент массива, мы можем использовать это свойство:
int last = nums[nums.length-1];
Многомерные массивы
Ранее мы рассматривали одномерные массивы, которые можно представить как цепочку или строку однотипных значений. Но кроме одномерных массивов также бывают и многомерными. Наиболее известный многомерный массив — таблица, представляющая двухмерный массив:
int[] nums1 = new int[] < 0, 1, 2, 3, 4, 5 >; int[][] nums2 = < < 0, 1, 2 >, < 3, 4, 5 >>;
Визуально оба массива можно представить следующим образом:
Какая запись является объявлением статического массива ответ
Массив представляет собой агрегат из нескольких переменных одного и того же типа. Массив с именем a из LENGTH элементов типа TYPE объявляется так:
TYPE a[LENGTH];
Это соответствует тому, что объявляются переменные типа TYPE со специальными именами a[0], a[1], . a[LENGTH—1]. Каждый элемент массива имеет свой номер — индекс. Доступ к x-ому элементу массива осуществляется при помощи операции индексации:
int x = . ; /* целочисленный индекс */ TYPE value = a[x]; /* чтение x-ого элемента */ a[x] = value; /* запись в x-тый элемент */
В качестве индекса может использоваться любое выражение, выдающее значение целого типа: char, short, int, long. Индексы элементов массива в Си начинаются с 0 (а не с 1), и индекс последнего элемента массива из LENGTH элементов — это LENGTH-1 (а не LENGTH). Поэтому цикл по всем элементам массива — это
TYPE a[LENGTH]; int indx; for(indx=0; indx LENGTH; indx++) . a[indx]. ;
Статические массивы можно объявлять с инициализацией, перечисляя значения их элементов в <> через запятую. Если задано меньше элементов, чем длина массива остальные элементы считаются нулями:
int a10[10] = < 1, 2, 3, 4 >; /* и 6 нулей */
Если при описании массива с инициализацией не указать его размер, он будет подсчитан компилятором:
int a3[] = < 1, 2, 3 >; /* как бы a3[3] */
В большинстве современных компьютеров (с фон-Неймановской архитектурой) память представляет собой массив байт. Когда мы описываем некоторую переменную или массив, в памяти выделяется непрерывная область для хранения этой переменной. Все байты памяти компьютера пронумерованы. Номер байта, с которого начинается в памяти наша переменная, называется адресом этой переменной (адрес может иметь и более сложную структуру, чем просто целое число — например состоять из номера сегмента памяти и номера байта в этом сегменте). В Си адрес переменной можно получить с помощью операции взятия адреса &. Пусть у нас есть переменная var, тогда &var — ее адрес. Адрес нельзя присваивать целой переменной; для хранения адресов используются указатели (смотри ниже).
Данное может занимать несколько подряд идущих байт. Размер в байтах участка памяти, требуемого для хранения значения типа TYPE, можно узнать при помощи операции sizeof(TYPE), а размер переменной — при помощи sizeof(var). Всегда выполняется sizeof(char)==1. В некоторых машинах адреса переменных (а также агрегатов данных массивов и структур) кратны sizeof(int) или sizeof(double) — это так называемое «выравнивание (alignment) данных на границу типа int«. Это позволяет делать доступ к данным более быстрым (аппаратура работает эффективнее).
Язык Си предоставляет нам средство для работы с адресами данных — указатели (pointer)| -. Указатель физически — это адрес некоторой переменной («указуемой» переменной). Отличие указателей от машинных адресов состоит в том, что указатель может содержать адреса данных только определенного типа. Указатель ptr, который может указывать на данные типа TYPE, описывается так:
TYPE var; /* переменная */ TYPE *ptr; /* объявление ук-ля */ ptr = & var;
В данном случае мы занесли в указательную переменную ptr адрес переменной var. Будем говорить, что указатель ptr указывает на переменную var (или, что ptr установлен на var). Пусть TYPE равно int, и у нас есть массив и указатели:
int array[LENGTH], value; int *ptr, *ptr1;
Установим указатель на x-ый элемент массива
ptr = & array[x];
Указателю можно присвоить значение другого указателя на такой же тип. В результате оба указателя будут указывать на одно и то же место в памяти: ptr1 = ptr;
Мы можем изменять указуемую переменную при помощи операции *
*ptr = 128; /* занести 128 в указуемую перем. */ value = *ptr; /* прочесть указуемую переменную */
В данном случае мы заносим и затем читаем значение переменной array[x], на которую поставлен указатель, то есть
*ptr означает сейчас array[x]
Таким образом, операция * (значение по адресу) оказывается обратной к операции & (взятие адреса):
& (*ptr) == ptr и * (&value) == value
Операция * объясняет смысл описания TYPE *ptr; оно означает, что значение выражения *ptr будет иметь тип TYPE. Название же типа самого указателя — это (TYPE *). В частности, TYPE может сам быть указательным типом — можно объявить указатель на указатель, вроде char **ptrptr;
Имя массива — это константа, представляющая собой указатель на 0-ой элемент массива. Этот указатель отличается от обычных тем, что его нельзя изменить (установить на другую переменную), поскольку он сам хранится не в переменной, а является просто некоторым постоянным адресом.
массив указатель ____________ _____ array: | array[0] | ptr:| * | | array[1] | | | array[2] |Следствием такой интерпретации имен массивов является то, что для того чтобы поставить указатель на начало массива, надо писать
ptr = array; или ptr = &array[0]; но не ptr = &array;Операция & перед одиноким именем массива не нужна и недопустима!
Такое родство указателей и массивов позволяет нам применять операцию * к имени массива: value = *array; означает то же самое, что и value = array[0];
Указатели - не целые числа! Хотя физически это и номера байтов, адресная арифметика отличается от обычной. Так, если дан указатель TYPE *ptr; и номер байта (адрес), на который указывает ptr, равен byteaddr, то
ptr = ptr + n; /* n - целое, может быть и < 0 */заставит ptr указывать не на байт номер byteaddr + n, а на байт номер
byteaddr + (n * sizeof(TYPE))то есть прибавление единицы к указателю продвигает адрес не на 1 байт, а на размер указываемого указателем типа данных! Пусть указатель ptr указывает на x-ый элемент массива array. Тогда после
TYPE *ptr2 = array + L; /* L - целое */ TYPE *ptr1 = ptr + N; /* N - целое */ ptr += M; /* M - целое */указатели указывают на
ptr1 == &array[x+N] и ptr == &array[x+M] ptr2 == &array[L]Если мы теперь рассмотрим цепочку равенств
*ptr2 = *(array + L) = *(&array[L]) = array[L]то получим ОСНОВНОЕ ПРАВИЛО: пусть ptr - указатель или имя массива. Тогда операции индексации, взятия значения по адресу, взятия адреса и прибавления целого к указателю связаны соотношениями:
ptr[x] тождественно *(ptr+x) &ptr[x] тождественно ptr+x(тождества верны в обе стороны), в том числе при x==0 и x < 0. Так что, например,
ptr[-1] означает *(ptr-1) ptr[0] означает *ptrУказатели можно индексировать подобно массивам. Рассмотрим пример:
/* индекс: 0 1 2 3 4 */ double numbers[5] = < 0.0, 1.0, 2.0, 3.0, 4.0 >; double *dptr = &numbers[2]; double number = dptr[2]; /* равно 4.0 */ numbers: [0] [1] [2] [3] [4] | [-2] [-1] [0] [1] [2] dptrесли dptr = &numbers[x] = numbers + x то dptr[i] = *(dptr + i) = = *(numbers + x + i) = numbers[x + i]Указатель на один тип можно преобразовать в указатель на другой тип: такое преобразование не вызывает генерации каких-либо машинных команд, но заставляет компилятор изменить параметры адресной арифметики, а также операции выборки данного по указателю (собственно, разница в указателях на данные разных типов состоит только в размерах указуемых типов; а также в генерации команд `->' для выборки полей структур, если указатель - на структурный тип).
Целые (int или long) числа иногда можно преобразовывать в указатели. Этим пользуются при написании драйверов устройств для доступа к регистрам по физическим адресам, например:
unsigned short *KISA5 = (unsigned short *) 0172352;
- Как уже было сказано, адреса данных часто выравниваются на границу некоторого типа. Мы же можем задать невыровненное целое значение. Такой адрес будет некорректен.
- Структура адреса, поддерживаемая процессором, может не соответствовать формату целых (или длинных целых) чисел. Так обстоит дело с IBM PC 8086/80286, где адрес состоит из пары short int чисел, хранящихся в памяти подряд. Однако весь адрес (если рассматривать эти два числа как одно длинное целое) не является обычным long-числом, а вычисляется более сложным способом: адресная пара SEGMENT:OFFSET преобразуется так
unsigned short SEGMENT, OFFSET; /*16 бит: [0..65535]*/ unsigned long ADDRESS = (SEGMENT OFFSET; получается 20-и битный физический адрес ADDRESSВ Си принято соглашение, что указатель (TYPE *)0 означает "указатель ни на что". Он является просто признаком, используемым для обозначения несуществующего адреса или конца цепочки указателей, и имеет специальное обозначение NULL. Обращение (выборка или запись данных) по этому указателю считается некорректным (кроме случая, когда вы пишете машинно-зависимую программу и работаете с физическими адресами).
Отметим, что указатель можно направить в неправильное место - на участок памяти, содержащий данные не того типа, который задан в описании указателя; либо вообще содержащий неизвестно что:
int i = 2, *iptr = &i; double x = 12.76; iptr += 7; /* куда же он указал ?! */ iptr = (int *) &x; i = *iptr;Само присваивание указателю некорректного значения еще не является ошибкой. Ошибка возникнет лишь при обращении к данным по этому указателю (такие ошибки довольно тяжело искать!).
При передаче имени массива в качестве параметра функции, как аргумент передается не копия САМОГО МАССИВА (это заняло бы слишком много места), а копия АДРЕСА 0-ого элемента этого массива (т.е. указатель на начало массива).
f(int x )< x++; > g(int xa[])< xa[0]++; > int a[2] = < 1, 1 >; /* объявление с инициализацией */ main()< f(a[0]); printf("%d\n",a[0]); /* a[0] осталось равно 1*/ g(a ); printf("%d\n",a[0]); /* a[0] стало равно 2 */ >В f() в качестве аргумента передается копия элемента a[0] (и изменение этой копии не приводит к изменению самого массива - аргумент x является локальной переменной в f()), а в g() таким локалом является АДРЕС массива a - но не сам массив, поэтому xa[0]++ изменяет сам массив a (зато, например, xa++ внутри g() изменило бы лишь локальную указательную переменную xa, но не адрес массива a).
Заметьте, что поскольку массив передается как указатель на его начало, то размер массива в объявлении аргумента можно не указывать. Это позволяет одной функцией обрабатывать массивы разной длины:
вместо Fun(int xa[5]) < . >можно Fun(int xa[] ) < . >или даже Fun(int *xa )
Если функция должна знать длину массива - передавайте ее как дополнительный аргумент:int sum( int a[], int len ) < int s=0, i; for(i=0; i < len; i++) s += a[i]; return( s ); >. int arr[10] = < . >; . int sum10 = sum(arr, 10); .Количество элементов в массиве TYPE arr[N]; можно вычислить специальным образом, как
#define LENGTH (sizeof(arr) / sizeof(arr[0]))#define LENGTH (sizeof(arr) / sizeof(TYPE))Оба способа выдадут число, равное N. Эти конструкции обычно употребляются для вычисления длины массивов, задаваемых в виде
TYPE arr[] = < . >;без явного указания размера. sizeof(arr) выдает размер всего массива в байтах.
sizeof(arr[0]) выдает размер одного элемента. И все это не зависит от типа элемента (просто потому, что все элементы массивов имеют одинаковый размер).
Строка в Си - это последовательность байт (букв, символов, литер, character), завершающаяся в конце специальным признаком - байтом '\0'. Этот признак добавляется компилятором автоматически, когда мы задаем строку в виде "строка". Длина строки (т.е. число литер, предшествующих '\0') нигде явно не хранится. Длина строки ограничена лишь размером массива, в котором сохранена строка, и может изменяться в процессе работы программы в пределах от 0 до длины массива-1. При передаче строки в качестве аргумента в функцию, функции не требуется знать длину строки, т.к. передается указатель на начало массива, а наличие ограничителя '\0' позволяет обнаружить конец строки при ее просмотре.
С массивами байт можно использовать следующую конструкцию, задающую массивы (строки) одинакового размера:
char stringA [ITSSIZE]; char stringB [sizeof stringA];В данном разделе мы в основном будем рассматривать строки и указатели на символы.
2.1.
Операции взятия адреса объекта и разыменования указателя - взаимно обратны.
TYPE objx; TYPE *ptrx = &objx; /* инициализируем адресом objx */ *(&objx) = objx; &(*ptrx) = ptrx;Вот пример того, как можно заменить условный оператор условным выражением (это удастся не всегда):
if(c) a = 1; else b = 1;Предупреждение: такой стиль не способствует понятности программы и даже компактности ее кода.
#include int main(int ac, char *av[]) < int a, b, c; a = b = c = 0; if(av[1]) c = atoi(av[1]); *(c ? &a : &b) = 1; /* . */ printf("cond=%d a=%d b=%d\n", c, a, b); return 0; >2.2.
Каким образом инициализируются по умолчанию внешние и статические массивы? Инициализируются ли по умолчанию автоматические массивы? Каким образом можно присваивать значения элементам массива, относящегося к любому классу памяти?
2.3.
Пусть задан массив int arr[10]; что тогда означают выражения:
arr[0] *arr *arr + 2 arr[2] *(arr + 2) arr &arr[2] arr+22.4.
Правильно ли написано увеличение величины, на которую указывает указатель a, на единицу?
*a++;Ответ: нет, надо:
(*a)++; или *a += 1;2.5.
Дан фрагмент текста:
char a[] = "xyz"; char *b = a + 1;Чему равны
b[-1] b[2] "abcd"[3](Ответ: 'x', '\0', 'd' )
Можно ли написать a++ ? То же про b++ ? Можно ли написать b=a ? a=b ? (нет, да, да, нет)
2.6.
Ниже приведена программа, вычисляющая среднее значение элементов массива
int arr [] = ; main () < int i; long sum; for ( i = 0, sum = 0L; i < (sizeof(arr)/sizeof(int)); i++ ) sum += arr[i]; printf ("Среднее значение = %ld\n", sum/8) >Перепишите указанную программу с применением указателей.
2.7.
Что напечатается в результате работы программы?
char arr[] = ; main ()Почему массив arr[] описан вне функции main()? Как внести его в функцию main() ?
Ответ: написать внутри main
static char arr[]=.2.8.
Можно ли писать на Си так:
f( n, m )Ответ: к сожалению нельзя (Си - это не Algol). При отведении памяти для массива в качестве размера должна быть указана константа или выражение, которое может быть еще во время компиляции вычислено до целочисленной константы, т.е. массивы имеют фиксированную длину.
2.9.
Предположим, что у нас есть описание массива
static int mas[30][100];
- выразите адрес mas[22][56] иначе
- выразите адрес mas[22][0] двумя способами
- выразите адрес mas[0][0] тремя способами
2.10.
Составьте программу инициализации двумерного массива a[10][10], выборки элементов с a[5][5] до a[9][9] и их распечатки. Используйте доступ к элементам по указателю.
2.11.
Составьте функцию вычисления скалярного произведения двух векторов. Длина векторов задается в качестве одного из аргументов.
2.12.
Составьте функцию умножения двумерных матриц a[][] * b[][].
2.13.
Составьте функцию умножения трехмерных матриц a[][][] * b[][][].
2.14.
Для тех, кто программировал на языке Pascal: какая допущена ошибка?
char a[10][20]; char c; int x,y; . c = a[x,y];Ответ: многомерные массивы в Си надо индексировать так:
c = a[x][y];В написанном же примере мы имеем в качестве индекса выражение x,y (оператор "запятая") со значением y, т.е.
c = a[y];Синтаксической ошибки нет, но смысл совершенно изменился!
2.15.
Двумерные массивы в памяти представляются как одномерные. Например, если
int a[N][M];то конструкция a[y][x] превращается при компиляции в одномерную конструкцию, подобную такой:
int a[N * M]; /* массив развернут построчно */ #define a_yx(y, x) a[(x) + (y) * M]a[y][x] есть *(&a[0][0] + y * M + x)Следствием этого является то, что компилятор для генерации индексации двумерных (и более) массовов должен знать M - размер массива по 2-ому измерению (а также 3-ему, 4-ому, и.т.д.). В частности, при передаче многомерного массива в функцию
f(arr) int arr[N][M]; < . >/* годится */ f(arr) int arr[] [M]; < . >/* годится */ f(arr) int arr[] []; < . >/* не годится */ f(arr) int (*arr)[M]; < . >/* годится */ f(arr) int *arr [M]; < . >/* не годится: это уже не двумерный массив, а одномерный массив указателей */А также при описании внешних массивов:
extern int a[N][M]; /* годится */ extern int a[ ][M]; /* годится */ extern int a[ ][ ]; /* не годится: компилятор не сможет сгенерить операцию индексации */Вот как, к примеру, должна выглядеть работа с двумерным массивом arr[ROWS][COLS], отведенным при помощи malloc();
void f(int array[][COLS]) < int x, y; for(y=0; y < ROWS; y++) for(x=0; x < COLS; x++) array[y][x] = 1; >void main()
2.16.
Как описывать ссылки (указатели) на двумерные массивы? Рассмотрим такую программу:
#include #define First 3 #define Second 5 char arr[First][Second] = < "ABC.", < 'D', 'E', 'F', '?', '\0' >, < 'G', 'H', 'Z', '!', '\0' >>; char (*ptr)[Second]; main()< int i; ptr = arr; /* arr и ptr теперь взаимозаменимы */ for(i=0; iУказателем здесь является ptr. Отметим, что у него задана размерность по второму измерению: Second, именно для того, чтобы компилятор мог правильно вычислить двумерные индексы.
Попробуйте сами объявить
char (*ptr)[4]; char (*ptr)[6]; char **ptr;и увидеть, к каким невеселым эффектам это приведет (компилятор, кстати, будет ругаться; но есть вероятность, что он все же странслирует это для вас. Но работать оно будет плачевно). Попробуйте также использовать ptr[x][y].
Обратите также внимание на инициализацию строк в нашем примере. Строка "ABC." равносильна объявлению
2.17.
Массив s моделирует двумерный массив char s[H][W]; Перепишите пример при помощи указателей, избавьтесь от операции умножения. Прямоугольник (x0,y0,width,height) лежит целиком внутри (0,0,W,H).
char s[W*H]; int x,y; int x0,y0,width,height; for(x=0; x < W*H; x++) s[x] = '.'; . for(y=y0; y < y0+height; y++) for(x=x0; x < x0+width; x++) s[x + W*y] = '*';char s[W*H]; int i,j; int x0,y0,width,height; char *curs; . for(curs = s + x0 + W*y0, i=0; i < height; i++, curs += W-width) for(j=0; j < width; j++) *curs++ = '*';Такая оптимизация возможна в некоторых функциях из главы "Работа с видеопамятью".
2.18.
Что означают описания?
int i; // целое. int *pi; // указатель на целое. int *api[3]; // массив из 3х ук-лей на целые. int (*pai)[3]; // указатель на массив из 3х целых. // можно описать как int **pai; int fi(); // функция, возвращающая целое. int *fpi(); // ф-ция, возвр. ук-ль на целое. int (*pfi)(); // ук-ль на ф-цию, возвращающую целое. int *(*pfpi)(); // ук-ль на ф-цию, возвр. ук-ль на int. int (*pfpfi())(); // ф-ция, возвращающая указатель на // "функцию, возвращающую целое". int (*fai())[3]; // ф-ция, возвр. ук-ль на массив // из 3х целых. иначе ее // можно описать как int **fai(); int (*apfi[3])(); // массив из 3х ук-лей на функции, // возвращающие целые.Переменные в Си описываются в формате их использования. Так описание
int (*f)();означает, что f можно использовать в виде
int value; value = (*f)(1, 2, 3 /* список аргументов */);Однако из такого способа описания тип самой описываемой переменной и его смысл довольно неочевидны. Приведем прием (позаимствованный из журнала "Communications of the ACM"), позволяющий прояснить смысл описания. Описание на Си переводится в описание в стиле языка Algol-68. Далее
ref ТИП означает "указатель на ТИП" proc() ТИП "функция, возвращающая ТИП" array of ТИП "массив из элементов ТИПа" x: ТИП "x имеет тип ТИП"Приведем несколько примеров, из которых ясен и способ преобразования:
int (*f())(); означает (*f())() : int *f() : proc() int f() : ref proc() int f : proc() ref proc() intто есть f - функция, возвращающая указатель на функцию, возвращающую целое.
int (*f[3])(); означает (*f[])() : int *f[] : proc() int f[] : ref proc() int f : array of ref proc() intf - массив указателей на функции, возвращающие целые. Обратно: опишем
g как указатель на функцию, возвращающую указатель на массив из 5и указателей на функции, возвращающие указатели на целые.g : ref p() ref array of ref p() ref int *g : p() ref array of ref p() ref int (*g)() : ref array of ref p() ref int *(*g)() : array of ref p() ref int (*(*g)())[5] : ref p() ref int *(*(*g)())[5] : p() ref int (*(*(*g)())[5])(): ref int *(*(*(*g)())[5])(): int int *(*(*(*g)())[5])();В Си невозможны функции, возвращающие массив:
proc() array of . а только proc() ref array of .Само название типа (например, для использования в операции приведения типа) получается вычеркиванием имени переменной (а также можно опустить размер массива):
g = ( int *(*(*(*)())[])() ) 0;2.19.
Напишите функцию strcat(d,s), приписывающую строку s к концу строки d.
char *strcat(d,s) register char *d, *s; < while( *d ) d++; /* ищем конец строки d */ while( *d++ = *s++ ); /* strcpy(d, s) */ return (d-1); /* конец строки */ >Цикл, помеченный "strcpy" - это наиболее краткая запись операторов
do < char c; c = (*d = *s); s++; d++; >while(c != '\0');На самом деле strcat должен по стандарту возвращать свой первый аргумент, как и функция strcpy:
char *strcat(d,s) register char *d, *s;Эти два варианта демонстрируют, что функция может быть реализована разными способами. Кроме того видно, что вместо стандартной библиотечной функции мы можем определить свою одноименную функцию, несколько отличающуюся поведением от стандартной (как возвращаемое значение в 1-ом варианте).
© Copyright А. Богатырев, 1992-95
Си в UNIXРабота с массивами в языке Си
На этой странице относительно подробно рассказывается о статических и динамических массивах. Краткое изложение основных моментов и описание методов поиска ошибок доступны при нажатии на кнопки выше. Двумерные массивы описаны на этой странице.
Массив – это линейно упорядоченная совокупность однотипных элементов. Массив определяется типом элементов (int, double, . ) и длиной. Доступ к элементам осуществляется по индексу – порядковому номеру элемента массива. Логически первый элемент массива имеет индекс ноль. В языке Си существуют статические массивы, число элементов в которых должно быть известно в момент компиляции программы, и динамические массивы, размер которых задается в процессе выполнения программы, то есть может зависеть от входных данных. Эти два типа отличаются только методом создания массива, поэтому сначала рассмотрим статические массивы.
Статические массивы
Способы объявления статических массивов
Объявление статического массива отличается от объявления обычной переменной только указанием количества элементов массива. Например, следующее объявление означает, что именем points называется массив из 100 действительных чисел.
double points[100];В некотором смысле можно считать, что такое объявление переменной points создает 100 переменных, которые называются points[0], points[1], . points[99]. Плюс к этому, "имена" этих переменных можно вычислять: points[1], points[0+1] или points[k-1] имеют одно значение (если k=2).
В реальных программах следует избегать явного использования числовых констант в объявлениях массива (и других частях программы). Если нам нужно объявить два массива, которые теоретически могут иметь разный размер, например,
double points[100]; int students[100];то в дальнейшем, если возникнет необходимость увеличить один из массивов, будет сложно отличить одну константу от другой. Особенно это верно при обработке элементов массива (см. ниже). Правильным считается использование директив препроцессора для присвоения константам "говорящих" имен. Например:
#define NPOINTS 100 #define NSTUDENTS 100 . double points[NPOINTS]; int students[NSTUDENTS];Объявление массива может быть совмещено с присвоением значений его элементам. Например,
double points[] = ;создает массив из четырех действительных чисел с указанными значениями. Заметим, что в данном случае число элементов массива в квадратных скобках не указывается. Компилятор самостоятельно вычисляет длину по списку начальных значений. В программе можно вычислить длину такого массива, разделив его размер на размер одного элемента (пример ниже).
Работа с элементами массива
Для доступа к элементу массива достаточно знать его имя и порядковый номер элемента. В языке Си элементы массива индексируются начиная с нуля, то есть в массиве из двух элементов корректными являются индексы 0 и 1. Если массив имеет имя array, то его k -й элемент записывается как array[k] . Это выражение может использоваться как для получения значения элемента массива, так и для его изменения, если оно стоит в левой части оператора присваивания. Рассмотрим для примера следующую программу.
#define NPOINTS 100 int main() < double points[NPOINTS]; int k; points[0] = 0.1; for(k=1; k < NPOINTS; k++) < points[k] = 0.1 + points[k-1]; >return 0; >Эта программа заполняет массив действительных чисел значениями 0, 0.1, 0.2 и так далее. Отметим, что макропеременная NPOINTS используется как при объявлении массива, так и в качестве верхней границы цикла по всем его элементам. Если размер массива нужно будет изменить, то достаточно исправить одну строчку в программе (#define).
Пример работы с массивом, который задан с начальными значениями:
int main() < double points[] = ; int k; int npoints = sizeof(points)/sizeof(points[0]); for(k=0; k < npoints; k++) < printf("points[%d] = %lf\n", k, points[k]); >return 0; >Типичная ошибка при работе с массивами состоит в указании неправильного индекса. Если в приведенной выше программе переменная цикла k будет пробегать значения от 0 до npoints включительно, то поведение программы, вообще говоря, может быть любым. Наиболее вероятным поведением является вывод на экран какого-то значения, но может возникнуть и критическая ошибка, которая приведет к аварийной остановке программы.
Представление массива в памяти и адресная арифметика
В памяти ЭВМ элементы массива записаны последовательно без пропусков. Имя массива является указателем на его начальный элемент (с индексом 0). Поскольку в массиве все элементы имеют одинаковый тип, то зная адрес начала массива (A), размер одного элемента (size) и индекс k можно вычислить адрес размещения k-ого элемента: A + k*size. Если требуется получить значение k-ого элемента массива, то достаточно выполнить одно умножение (k*size), одно сложение (A + k*size) и загрузить значение из памяти по только что вычисленному адресу. Таким образом, обращение к элементу массива очень эффективно и сложность этой операции не зависит от величины индекса k: получение (или изменение) значения нулевого элемента столь же эффективно, как и миллионного.
Хорошо, адрес начала массива мы знаем — это его имя, индекс нам известен, но как узнать size (размер одного элемента)? Чуть ниже мы узнаем как это сделать, но для работы с указателями на элементы массива это не требуется! В языке Си к указателям можно прибавлять целые числа. Например, если есть указатель double *a; , то значением выражения a+9 будет адрес десятого (еще раз вспомним, что массивы индексируются с нуля!) элемента массива, который начинается с адреса a . Компилятор сам понимает, что a является указателем на double и прибавляет нужное значение.
Обратной стороной последовательно хранения элементов в памяти является сложность вставки нового значения с сохранением порядка следования элементов. Например, если в массив нужно добавить новое значение по индексу 0, то чтобы "освободить" место все элементы массива придется сдвинуть на одну позицию. Ясно, что сложность этой операции зависит от длины массива. Чем больше длина, тем дольше выполняется это действие.
Передача массива в функцию
Функция может получать на вход массив. В действительности в функцию передается адрес начала массива и его длина. Прототип функции может быть оформлен либо так:
int print_array(double x[], int len);int print_array(double *x, int len);Эти варианты являются эквивалентными. Некоторые программисты предпочитают первый (квадратные скобки показывают, что формальный параметр функции является массивом), другие — второй (имя массива является указателем на нулевой элемент). Естественно, что функция может иметь и другие параметры, в том числе, другие массивы. Это только пример.
Рассмотрим возможную реализацию функции распечатывания массива.
#include int print_array(double x[], int len) < int k; for(k = 0; k < len; k++) < printf("x[%d] = %lf\n", k, x[k]); >return 0; >При вызове функции в качестве аргумента нужно передавать имя массива и его длину.
int main() < double points[] = ; int npoints = sizeof(points)/sizeof(points[0]); print_array(points, npoints); return 0; >Внимание! Если функция print_array изменит значение элемента массива x (например, в цикле будет написано x[k]=0; ), то изменятся значения и в массиве points функции main. Элементы массива при вызове функций не копируются! Функция получает на вход адрес памяти, где записаны элементы массива. Эта память "общая" для вызывающей и вызываемой функции.
Динамические массивы: malloc и free
Статические массивы имеют одно существенное ограничение: размер массива должен быть известен в момент компиляции программы. В большинстве задач размер данных становится известным только в момент выполнения программы. Например, вы написали программу для обработки списка друзей или подписчиков в социальной сети. У одного пользователя друзей мало, а у другого — очень много. Какое значение выбрать для длины массива друзей? 200? 1000? Миллион? Если константа будет очень большой, чтобы "заведомо" (посмотрите как росло число пользователей Интернет) устраивать всех пользователей, то для подавляющего числа пользователей это приведет к излишним затратам памяти. Захотите ли Вы поставить на свой телефон программу, которая при запуске займет всю его память с сообщением: "А вдруг у тебя миллион друзей. Нет? Всего 12?! Неплохо, прямо как у Oушена! А y Трампа миллион. "? [Друзей не должно и не может быть так много, но это к делу не относится.] Чтобы избежать таких ситуаций нужно уметь выделять минимально необходимое количество памяти.
- выделение памяти под массив;
- освобождение памяти, когда она больше не требуется.
Стандартная библиотека языка Си содержит несколько функций для работы с динамической памятью. Нам понадобятся две: malloc (memory allocation — выделение памяти) и free (освобождение). Для использования этих удивительных функций нужно в программе подключить заголовочный файл . Пример программы приведен ниже. Сначала посмотрим, что делают эти функции.
malloc: динамическое выделение памяти
Прототип: void *malloc(size_t size); Параметры: size — беззнаковое целое число, размер запрашиваемой памяти в байтах. Возвращает: Адрес начала выделенной памяти или NULL, если не удалось выделить память. Функция malloc возвращает указатель типа void * — это "абстрактный" указатель на память, который может быть приведен к указателю на любой тип. Функция malloc не может сразу возвращать указатель нужного типа, так как она используется для создания разных массивов, а в прототипе нужно указать конкретный тип возвращаемого значения.
Для выделения памяти под массив из n элементов типа T, где в T могут быть стандартные типы int , double и т.п., необходимо знать размер значения T в байтах. Для определения этой величины в языке Си есть специальный оператор sizeof , который в момент компиляции программы вычисляет нужное значение. Например, массив из n целых чисел будет занимать n*sizeof(int) байт памяти.
Таким образом, для создания динамического массива некоторого типа, например с массива целых чисел, нужно использовать команду вида:
int length; int *points; // . получили значение length (длина массива) points = (int *)malloc(length * sizeof(int));
Если нужен другой тип данных, допустим double , то int заменяется на нужное имя ( double ) в трех местах (кроме первой строки, так как длина массива всегда является целым числом).
free: освобождение памяти
Функция free позволяет освободить область памяти, которая ранее была выделена программе при вызове malloc .
Прототип void free(void *ptr); Параметры: ptr — указатель, который был получен в результат вызова malloc.
В качестве аргумента функции free может использоваться только тот адрес, который был получен в результате вызова malloc. Нельзя создать статический массив и "освободить" его функцией free. Адрес может быть освобожден только один раз. Если два раза подряд вызвать функцию free с одним и тем же аргументом, то это приведет к аварийному завершению программы.
Пример программы с динамическим массивом
В качестве иллюстрации описанных методов рассмотрим программу, которая динамически выделяет память под массив и считывает его.
#include #include int main() < int npoints; double *points; int k; scanf("%d", &npoints); /* npoints получает значение в момент выполнения программы */ points = (double *)malloc(npoints*sizeof(double)); /* Выдели память для хранения npoints элементов, каждый размера sizeof(double) */ if(points == NULL) < printf("Произошла ошибка. Запросили слишком много памяти??\n"); return -1; >/* Считываем данные с использованием адресной арифметики */ k = 0; while(k < npoints && scanf("%lf", points+k) == 1) < k++; >/* Работаем с points как с обычным массивом */ /* Например, вызываем функцию print_array(points, npoints) */ free(points); /* Освободили память */ return 0; >
Функции, которые возвращают массив
Иногда бывает удобно сделать функцию, которая возвращает динамически созданный массив. Примером может служить функция считывания массива из файла. Такая функция может получать на вход файловую переменную ( FILE * ) и должна вернуть в вызывающую функцию массив значений. Например, массив действительнах чисел. Попробуем ее реализовать.
Во-первых, нужно понять, какой прототип должна иметь такая функция. Она должна вернуть два значения: адрес выделенной памяти и длину массива. Как мы уже знаем, несколько значений можно вернуть используя указатели. Длина массива имеет тип int . Значит параметр функции будет иметь тип int * (адрес, по которому нужно записать значение). Массив — это адрес нулевого элемента, то есть double * . Значит параметр будет иметь тип double ** — "указатель на указатель". Мы должны передать адрес (одна звездочка), по которому нужно записать результат вызова malloc, который имеет тип double * . В результате получаем следующий прототип:
int read_array(FILE *input, double **array, int *length);
Собственно возвращаемое значение функции ( int ) может быть кодом ошибки. Если функция вернет 0, то это означает успешное выполнение. Любое ненулевое значение означает ошибку.
Теперь можно рассмотреть структуру тела функции (для наглядности в приведенном ниже коде отсутствуют проверки успешности считывания и корректности данных).
int read_array(FILE *input, double **array, int *length) < double *arr; int arr_length, k; /* Считываем массив: сначала длину, потом элементы */ fscanf("%d", &arr_length); arr = (double *)malloc(arr_length * sizeof(double)); for(k = 0; k < arr_length; k++) fscanf("%lf", arr + k); /* Копируем результат по заданным адресам */ *length = arr_length; *array = arr; return 0; >
Массивы в языке Си
При решении задач с большим количеством данных одинакового типа использование переменных с различными именами, не упорядоченных по адресам памяти, затрудняет программирование. В подобных случаях в языке Си используют объекты, называемые массивами.
Массив — это непрерывный участок памяти, содержащий последовательность объектов одинакового типа, обозначаемый одним именем.
Массив характеризуется следующими основными понятиями:
Элемент массива (значение элемента массива) – значение, хранящееся в определенной ячейке памяти, расположенной в пределах массива, а также адрес этой ячейки памяти.
Каждый элемент массива характеризуется тремя величинами:
- адресом элемента — адресом начальной ячейки памяти, в которой расположен этот элемент;
- индексом элемента (порядковым номером элемента в массиве);
- значением элемента.
Адрес массива – адрес начального элемента массива.
Имя массива – идентификатор, используемый для обращения к элементам массива.
Размер массива – количество элементов массива
Размер элемента – количество байт, занимаемых одним элементом массива.

Графически расположение массива в памяти компьютера можно представить в виде непрерывной ленты адресов.
Представленный на рисунке массив содержит q элементов с индексами от 0 до q-1 . Каждый элемент занимает в памяти компьютера k байт, причем расположение элементов в памяти последовательное.
Адреса i -го элемента массива имеет значение
n+k·i
Адрес массива представляет собой адрес начального (нулевого) элемента массива. Для обращения к элементам массива используется порядковый номер (индекс) элемента, начальное значение которого равно 0 . Так, если массив содержит q элементов, то индексы элементов массива меняются в пределах от 0 до q-1 .
Длина массива – количество байт, отводимое в памяти для хранения всех элементов массива.
ДлинаМассива = РазмерЭлемента * КоличествоЭлементов
Для определения размера элемента массива может использоваться функция
int sizeof (тип);
sizeof ( char ) = 1;
sizeof ( int ) = 4;
sizeof ( float ) = 4;
sizeof ( double ) = 8;
Объявление и инициализация массивов
Для объявления массива в языке Си используется следующий синтаксис:
тип имя[размерность]=;
Инициализация представляет собой набор начальных значений элементов массива, указанных в фигурных скобках, и разделенных запятыми.
int a[10] = <0, 1, 2, 3, 4, 5, 6, 7, 8, 9>; // массив a из 10 целых чисел
0,>
Если количество инициализирующих значений, указанных в фигурных скобках, меньше, чем количество элементов массива, указанное в квадратных скобках, то все оставшиеся элементы в массиве (для которых не хватило инициализирующих значений) будут равны нулю. Это свойство удобно использовать для задания нулевых значений всем элементам массива.
int b[10] = <0>; // массив b из 10 элементов, инициализированных 0
0>
Если массив проинициализирован при объявлении, то константные начальные значения его элементов указываются через запятую в фигурных скобках. В этом случае количество элементов в квадратных скобках может быть опущено.
При обращении к элементам массива индекс требуемого элемента указывается в квадратных скобках [] .
#include
int main()
int a[] = < 5, 4, 3, 2, 1 >; // массив a содержит 5 элементов
printf( "%d %d %d %d %d\n" , a[0], a[1], a[2], a[3], a[4]);
getchar();
return 0;
>

Результат выполнения программы:
Однако часто требуется задавать значения элементов массива в процессе выполнения программы. При этом используется объявление массива без инициализации. В таком случае указание количества элементов в квадратных скобках обязательно.
Для задания начальных значений элементов массива очень часто используется параметрический цикл:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#define _CRT_SECURE_NO_WARNINGS
#include
int main()
int a[5]; // объявлен массив a из 5 элементов
int i;
// Ввод элементов массива
for (i = 0; i printf( "a[%d] = " , i);
scanf( "%d" , &a[i]); // &a[i] - адрес i-го элемента массива
>
// Вывод элементов массива
for (i = 0; i printf( "%d " , a[i]); // пробел в формате печати обязателен
getchar(); getchar();
return 0;
>

Результат выполнения программы
Многомерные массивы
В языке Си могут быть также объявлены многомерные массивы. Отличие многомерного массива от одномерного состоит в том, что в одномерном массиве положение элемента определяется одним индексом, а в многомерном — несколькими. Примером многомерного массива является матрица.
Общая форма объявления многомерного массива
тип имя[размерность1][размерность2]. [размерностьm];
Элементы многомерного массива располагаются в последовательных ячейках оперативной памяти по возрастанию адресов. В памяти компьютера элементы многомерного массива располагаются подряд, например массив, имеющий 2 строки и 3 столбца,
будет расположен в памяти следующим образом

Общее количество элементов в приведенном двумерном массиве определится как
КоличествоСтрок * КоличествоСтолбцов = 2 * 3 = 6.
Количество байт памяти, требуемых для размещения массива, определится как
КоличествоЭлементов * РазмерЭлемента = 6 * 4 = 24 байта.
Инициализация многомерных массивов
Значения элементов многомерного массива, как и в одномерном случае, могут быть заданы константными значениями при объявлении, заключенными в фигурные скобки <> . Однако в этом случае указание количества элементов в строках и столбцах должно быть обязательно указано в квадратных скобках [] .

Результат выполнения
Однако чаще требуется вводить значения элементов многомерного массива в процессе выполнения программы. С этой целью удобно использовать вложенный параметрический цикл.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#define _CRT_SECURE_NO_WARNINGS
#include
int main()
int a[2][3]; // массив из 2 строк и 3 столбцов
int i, j;
// Ввод элементов массива
for (i = 0; i for (j = 0; j printf( "a[%d][%d] = " , i, j);
scanf( "%d" , &a[i][j]);
>
>
// Вывод элементов массива
for (i = 0; i for (j = 0; j printf( "%d " , a[i][j]);
>
printf( "\n" ); // перевод на новую строку
>
getchar(); getchar();
return 0;
>

Результат выполнения
Передача массива в функцию
Обработку массивов удобно организовывать с помощью специальных функций. Для обработки массива в качестве аргументов функции необходимо передать
- адрес массива,
- размер массива.
Исключение составляют функции обработки строк, в которые достаточно передать только адрес.
При передаче переменные в качестве аргументов функции данные передаются как копии. Это означает, что если внутри функции произойдет изменение значения параметра, то это никак не повлияет на его значение внутри вызывающей функции.
Если в функцию передается адрес переменной (или адрес массива), то все операции, выполняемые в функции с данными, находящимися в пределах видимости указанного адреса, производятся над оригиналом данных, поэтому исходный массив (или значение переменной) может быть изменено вызываемой функцией.
Пример на Си Дан массив из 10 элементов. Поменять местами наибольший и начальный элементы массива. Для операций поиска максимального элемента и обмена использовать функцию.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#define _CRT_SECURE_NO_WARNINGS
#include
// Функция обмена
void change( int *x, int n)
// x - указатель на массив (адрес массива)
// n - размер массива
int i;
int max, index;
max = x[0];
index = 0;
// Поиск максимального элемента
for (i = 1; i if (x[i]>max)
max = x[i];
index = i;
>
>
// Обмен
x[index] = x[0];
x[0] = max;
>
// Главная функция
int main()
int a[10];
int i;
for (i = 0; i printf( "a[%d] = " , i);
scanf( "%d" , &a[i]);
>
change(a, 10); // вызов функции обмена
// Вывод элементов массива
for (i = 0; i printf( "%d " , a[i]);
getchar();
getchar();
return 0;
>

Результат выполнения
Пример на Си Дан массив размерности n. Вычислить произведение четных элементов
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#define _CRT_SECURE_NO_WARNINGS
#include
// Функция вычисления произведения чётных элементов
int func( int *x, int n) // произведение четных элементов
int p = 1; // начальное значение произведения
int i;
for (i = 0; i if (x[i] % 2 == 0) // остаток от деления на 2 равен 0?
p = p * x[i];
>
return p;
>
// Главная функция
int main()
int a[5]; // объявлен массив a из 5 элементов
int i;
int pr;
// Ввод элементов массива
for (i = 0; i printf( "a[%d] = " , i);
scanf( "%d" , &a[i]); // &a[i] - адрес i-го элемента массива
>
pr = func(a, 5); // вычисление произведения
printf( "\n pr = %d" , pr); // вывод произведения четных элементов
getchar(); getchar();
return 0;
>

Результат выполнения
Комментариев к записи: 139
В группе числится 10 студентов. Каждый студент сдает 3 экзамена: программирование, английский, философию. Полученные оценки заносятся в двумерный массив, где каждая новаястрока содержит оценки нового студента. Размер массива 3*10. Определить: – средний балл за все экзамены каждого студента; – у какого студента наибольший и наименьший средний балл; – количество несдач (оценка меньше 4)
В чём сложность?
Если с одномерными массивами, чутка разобрался, то тут сложность
Здесь получается массив arr[10][3]. Чтобы с таким массивом работать, нужен вложенный цикл. На сайте есть пример работы с многомерными массивами: https://prog-cpp.ru/c-massiv/
Помогите пожалуйста. Задание:Найти 2 первых элемента в массиве С(n), значения которых не попадают в заданный с клавиатуры диапазон [a, b]. Поменять их местами.Со вводом нет проблем ,а вот найти два первых значения и поменять их местами, никак не могу сделать вывод.
<
int choice; //обраний пункт меню
double arr_C[100]; //одновимірний масив c
int n; //кількість елементів масиву c
double a, b;
int j;
int index;
double k;
//введення кількості елементів масиву B
printf( "\nУведіть кількість елементів масиву C(n)-(максимум 100) = " );
while (1)
<
if (scanf( "%d" , &n)!=1 n //якщо не виконуються умови-виведення запиту на повторне введення
printf( "Ви ввели неправильне значення. Спробуйте ще раз:\n" );
while (getchar() != '\n') //очистка буфера вводу та очікуання на правильний результат
continue;
>
else if (n > 100)
//якщо переповнення масиву-виведення запиту на повторне введення
printf( "Забагато елементів. Спробуйте ще раз:\n" );
while (getchar() != '\n') //очистка буфера вводу та очікуання на правильний результат
continue;
>
else
break ;
>
//введення елементів масиву B
for ( int i = 0; i < n; i++) printf( "arr_C[%d] = " , i);
while (scanf( "%lf" , &arr_C[i])!=1)
//якщо не виконуються умови-виведення запиту на повторне введення
printf( "Ви ввели неправильне значення. Спробуйте ще раз:\n" );
while (getchar() != '\n') //очистка буфера вводу та очікуання на правильний результат
continue;
>
>
a = DInput( "Введіть a:" );
b = DInput( "Введіть b:" );
if ((arr_C[j]>a)&&(arr_C[j]
temp=arr_C[j];
arr_C[j]=arr_C[index];
arr_C[index]=temp;
Здравствуйте, подскажите пожалуйста, появились проблемы с написанием кода, не очень понимаю, как его написать, вот задача «Есть массив монет и купюр, содержащихся в кошельке. Подсчитать сумму наличных. Сортировка массива по методу «Сортировка пузырем» Буду благодарна за помочь
Елена Вставская
Я тоже не очень понимаю, при чем тут сортировка. Найти сумму элементов массива - да и вывести
Здравствуйте !Подскажите пожалуйста, как в заполненном одномерном массиве найти номера элементов, которые больше 10, и количество таких элементов? А обнаруженные номера вывести в порядке их роста.
Елена Вставская
В цикле сравнить каждый элемент массива с 10. Если больше, вывести номер и увеличить количество на 1.
Как найти сумму элементов массива?
int sum=0;
for ( int i=0; i sum+=mas[i];
Добрый день) Хоть убейте но не могу сделать задачу: создать 3х7 массив чтобы считало температуру в трех городах (Моска, Питер, Ростов) всю неделю с соотвевтсвующим выводом информации (Самую высокую и низкую темпиратуру среди всех трех городов за все дни недели, самую низкую темпиратуру в Москве, город с самой большой температурой в среду, самую низкую и высокую среднею температуру и в каком городе) Дошел до вот этого момента, а дальше вывод информации, привязка дней недели итд я не помню((
int i, j, min, imin, jmin, max, imax, jmax;
int a [3][7];
printf( "Maximum temperature %d and its in %d and in column %d \n " , max, imax+1, jmax+1);
printf(Minimum temperature %d and its in %d and in column %d \n ", min, imin+1, jmin+1);
Елена Вставская
Пока не пойму, в чем сложность? Температуру в Москве найти? Или в среду? Индексы соответствуют дням недели. Неделя в какой день начинается? И какой по счету день "среда"? Сравниваем a[0][среда], a[1][среда] и a[2][среда]
Здраствуйте Елена! Помогите пожалуйста с заданием : Сформировать массив из м (м
Елена Вставская
Чтобы изменять размер массива, можно воспользоваться динамическим выделением памяти.
Чтобы случайные числа не повторялись - возможно два варианта
1. Перетасовать случайным образом последовательность чисел. 2. При генерации следующего случайного числа сравнивать его со всеми предыдущими.
Вопрос по поводу работы с массивом из функции: Что произойдёт с данными исходного массива, если я: - передам указатель на массив и его размер в функцию - внутри функции создам копию исходного массива и изменю её - изменю указатель так, чтобы он ссылался на изменённую копию исходного массива Эти данные просто станут "мусорными" значениями или удаляться (как в Python)?
Елена Вставская
Так лучше не делать! Выделенная память не будет корректно освобождена до завершения работы программы.
Добрый день. Вопрос по поводу указания размера массива. Я считал, что память под статический массив выделяется при компиляции и размер массива должен быть указан константой (как минимум в соответствии с требованиями стандарта ANSI C). Однако к моему удивлению при объявлении массива размера n (неизвестного на этапе компиляции) компилятор (mingw64 под Win) не выдает ни ошибок, ни предупреждений причем при разных стандартах (-std=c89,c90,c99. ) и включении отображения ошибок (-Wall):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int i, a[n]; // Почему не ругается?
for (i=0; i printf( "enter element #%i:" , i);
scanf( "%i" ,&a[i]);
>
printf( "\nResult array:" );
for (i=0; i printf( " %i" , a[i]);
>
Елена Вставская
Вопрос к разработчикам компилятора. В Visual Studio так задавать нельзя
Здравствуйте! Компилятор gcc 5.1.0 c11. int a = 10; int arr[a]; Ошибки нет. Стивен Прата в книге "Язык программирования С лекции и упражнения" 6 издание пишет: "int n = 5; float a8[n]; // не было разрешено до появления стандарта С99".
Здравствуйте, Елена! Спасибо Вам за статью! У меня есть один вопрос по массивам переменной длины. В одной книге прочел "Понятие переменный в массиве переменной длины вовсе не означает возможность изменения длины массива после его создания. Будучи созданным, массив переменной длины сохраняет тот же самый размер. В действительности понятие переменный означает, что при указании размерностей при первоначальном создании массива можно использовать переменные" Я выполнил упражнение из книги в DevC++, у меня программа запрашивает ввод количества строк и столбцов двумерного массива. Потом производит операции с массивом: вычисляет среднее значение, наибольшее значение в каждом одномерном массиве, наибольшее значение среди всех одномерных массивов и выводит данные на экран. Все это происходит в бесконечном цикле while (1) пока пользователь на запрос не введет значение отличное от 1 - тогда сработает оператор break. У меня в цикле while() каждый раз размер массива вводится с помощью scanf ("%lf", &str ), scanf ("%lf", &stlb ) без всякой динамической памяти и нормально программа работает т.е. пользователь может менять размер массива много раз и код компилируется. Вопрос - почему размер массива меняется и ошибки не выдается? Мне вот это непонятно. Заранее благодарен.
int vvod_massiva ( const double [][stlb]);
double srednee_znach ( const double [][stlb], int n);
double srednee_znach_vseh ( const double [][stlb]);
double bolshee_znach ( const double [][stlb]);
int vuvod_znach ( const double [][stlb], double [], double , double );
int main( void )
const double massiv[str][stlb];
double sred [stlb];
double c;
double d;
int i=0;
int j=0;
int ch;
while (1)
printf ( "Введите количество строк\n" );
scanf( "%d" , &str);
printf ( "Введите количество столбцов\n" , stlb);
scanf( "%d" , &stlb);
printf ( "Введите %d массива по %d элементов типа double каждый\n" , str, stlb );
for (i=0, j=0; i
c=srednee_znach_vseh (massiv);
d=bolshee_znach (massiv);
vuvod_znach (massiv, sred, c, d );
printf ( "Для повтора программы нажмите -1. Для завершения - 2\n" );
scanf( "%d" , &ch);
if (ch!=1)
break ;
>
int vvod_massiva ( const double a[][stlb])
int stroka;
int stolbets;
double srednee_znach ( const double a[][stlb], int n)
double sum=0;
double srednee=0;
int i;
double srednee_znach_vseh ( const double a[][stlb])
int stroka;
int stolbets;
double sum=0;
double srednee;
for (stroka=0; stroka for (stolbets=0; stolbets sum+=a[stroka][stolbets];
>
srednee=sum/(str*stlb);
printf( "%f\n" , sum );
return srednee;
>
double bolshee_znach ( const double a[][stlb]) > int stroka; for (stolbets=0; stolbets printf( "%f " , a[stroka][stolbets]); printf( "Среднее значение %d - массива равно %f\n" , i+1, b[i] ); printf( "Среднее значение из %d равно %f\n" , (str*stlb), c ); Елена Вставская Мне тоже непонятно, что это за компилятор такой. Visual Studio 2019 на этот код 14 ошибок показывает.
<
int i=0;
int j;
int n=1;
int p1=1;
double massiv [str];
int k;
for (j=0, k=0; j p1=1;
i=0;
while (p1
int vuvod_znach ( const double a[][stlb], double b[], double c, double d)
int stolbets;
int i;
int j;
printf( "большее значение из %d равно %f\n" , (str*stlb), d );