Среда, 27.11.2024, 22:33
Клан программистов ГлавнаяРегистрацияВход
Приветствую Вас Гость | RSS
Меню сайта
Категории каталога
Юмор [6]
Полезные статьи [94]
Полезная информация по вебразработке, продвижению своего проекта и т.д.
Photoshop [29]
Языки программирования [20]
Коды, читы для PSP [27]
Интересное в сети [12]
Самые интересные новости и достижения найденые в инете
Обзоры игр [31]
Железо [93]
Игровые приставки [4]
Мобильные телефоны [835]
Интернета и icq!Настройка [15]
описание как настроить ваш телефон для нормальной работы интернета и icq
Наш опрос
Почему вы не нажимаете на рекламу?))
Всего ответов: 69
 Каталог статей 
Главная » Статьи » Языки программирования

Массивы, указатели и структуры

453. Массивы

Как известно, тип описывает множество значений переменной и операций, которые могут выполняться над этой переменной. За исключением типа "символьная строка", все типы, с которыми мы имели дело до сих пор, представляли только единичные значения. В программах, выполняющих более содержательную работу, может понадобиться переменная, которая содержит множество значений. Переменная scores (баллы), например, должна содержать информацию о рейтинге 100 студентов. Или переменная salaries (оклады), которая содержит оклады всех служащих некоторой компании. Массив - это такая совокупность данных, которая содержит множество значений одного и того же типа. Например, можно создать один массив, который содержит 100 значений типа inl, и другой массив, который содержит 25 значений типа float. Тип каждого значения, присваиваемого массиву, должен быть таким же, как и тип самого массива. В этом разделе мы научимся создавать массивы в программе и работать с ними. После приобретения некоторого опыта работы с массивами легко убедиться в эффективности их использования. Для тех, кто уже имеет опыт работы со строками, освоение массивов не составит особого труда - символьная строка есть просто массив символов.

454. Объявление массива

Массив - это переменная, в которой можно хранить множество значений одного и того же типа. Для объявления массива необходимо указать требуемый тип (такой как int, float или double ) и размер массива. Для указания размера массива количество его элементов (размерность массива) помещается в квадратных скобках после имени массива. Например, в следующем объявлении создается массив scores, который может содержать баллы, обозначающие рейтинг 100 студентов:

int scores[100];

Аналогичным образом следующим оператором объявляется массив, содержащий зарплаты 50 служащих:

float salaries [50];

При объявлении массива Си выделяет достаточно памяти для размещения всех его элементов. Номер первого элемента начинается с нуля. Например, для массивов scores и salaries первым элементам можно присвоить значения 80 и 35000 следующим образом:

scores[0] = 80;
salaries[0] = 35000.0;

Поскольку номер первого элемента равен нулю, номер последнего элемента на 1 меньше, чем размер массива. Для массивов scores и salaries последним элементам можно присвоить значения следующим образом:

scores[99] = 75;
salaries[49] = 24000.0;

455. Размещение массива в памяти

Итак, массив - переменная, в которой хранится множество значений одного и того же типа. Для того чтобы лучше понять, как хранится информация в массиве, рассмотрим следующие объявления массивов:

char string[64];
float salaries[50];
int scores[100];
long planets[13];

После присваивания значений каждому из этих массивов они располагаются в памяти, как показано на рис. 455.

Рис. 455. Размещение значений в массиве

Как можно видеть, первое значение массива расположено по смещению 0. В разделе "Язык Си: начало" переменная определялась как имя, связываемое с одной или более ячеек памяти. В случае массива число таких ячеек памяти может быть значительно больше.

456. Объем памяти для массива

Как мы уже знаем, массив есть именованное множество значений одного и того же типа. При объявлении массива Си выделяет достаточно памяти для размещения указанного количества элементов, фактически требуемый объем памяти зависит от типа массива. Например, для массива из 100 элементов типа int требуется 100 * 2, т.е. 200 байтов памяти. С другой . стороны, для массива из 100 элементов типа float требуется 100* 4, или 400 байтов. В следующей программе ARRAYSIZ.C для вывода на экран количества памяти, требуемой для массивов различных типов, используется оператор sizeof:

#include <stdio.h>
void main(void)
{
int scores[100];
float salaries[100];
char string[100];

printf("Массиву int scores[100] выделяется %d байт\n", sizeof(scores));
printf("Массиву int salaries[100] выделяется %d байт\n", sizeof(salaries));
printf("Массиву char string[100] выделяется %d байт\n", sizeof(string));
}

После компиляции и выполнения этой программы на экран выводится:

C:\> ARRAYSIZ <Enter>
Массиву int scores[100] выделяется 200 байтов 
Массиву float salaries[100] выделяется 400 байтов 
Массиву char string[100] выделяется 100 байтов

457. Инициализация массива

Многие примеры, приведенные в этой книге, содержат проинициализированные символьные строки,например:

char title[] = "1001 совет по C/C++";
char section[64] = "Массивы";

В первом случае Си-компилятор выделяет 20 байтов для хранения строки. Во втором случае компилятор выделяет массив 64 байта, инициализируя первые 8 символов словом "Массивы" и двоичным нулем. Большинство компиляторов инициализирует двоичным нулем и остальные байты. Массивы других типов инициализируются аналогично. Например, следующий оператор инициализирует массив scores целых чисел значениями 80,70,90,85 и 80:

int scores[5] = {80, 70, 90, 85, 80};

При присваивании массиву начальных значений эти значения заключаются в фигурные скобки { }. Е нашем случае размер массива соответствует количеству начальных значений. В следующем операторе присваивается 4 действительных значения массиву, который в действительности может содержать 64 значения:

float salaries[64] = {25000.0, 32000.0, 44000.0, 23000.0};

В зависимости от компилятора, остальным 60 элементам могут быть присвоены нули. Как правило, не следует делать предположения об инициализации оставшихся элементов по умолчанию. Если размерность массива не указана, компилятор выделит ровно столько памяти, сколько нужно для хранения заданных начальных значений. Следующее объявление создает массив достаточно большой для того, чтобы содержать 3 значения типа long:

long planets[] = {1234567L, 654321L, 1221311L};

458. Доступ к элементам массива

Значения, которые хранятся в массиве, называются элементами массива. В следующей программе ELEMENTS.C, содержащей инициализируемый массив scores, для вывода элементов массива используется функция printf:

#include <stdio.h>

void main(void) 
{
int scores[5] = (80, 70, 90, 85, 80);

printf("Значения элементов массива\n");
printf("scores[0] %d\n", scores[0]);
printf("scores[1] %d\n", scores[l]);
printf("scores[2] %d\n", scores[2]);
printf("scores[3] %d\n", scores[3]);
printf("scores[4] %d\n", scores[4]);
}

После компиляции и выполнения этой программы на экран выводится:

C:\> ELEMENTS <Enter>
Значения элементов массива
scores[0] = 80
scores[1] = 70
scores[2] = 90
scores[3] = 85
scores[4J = 80

Как можно видеть, для обращения к элементам массива необходимо указать сначала имя массива, а затем номер требуемого элемента в квадратных скобках.

459. Использование элементов массива в цикле

В С458 были выведены на экран значения элементов массива с номерами от 0 до 4. Обращение к элементам массива с указанием номера каждого элемента массива - способ достаточно громоздкий. В качестве альтернативы можно использовать переменную для указания номеров элементов. Например, предположим, что переменная;' содержит значение 2, тогда следующий оператор присвоит элементу array [2] значение 80:

i = 2;
array [i] =80;

В следующей программе SHOWARRA.C для вывода на экран значений массива scores используется переменная i и цикл for:

#include <stdio.h>

void main(void)
{
int scores[5] = (80, 70, 90, 85, 80);
int i;

printf("Значения элементов массива\n");

for (i = 0; i < 5; i++)
 printf("scores[%d] %d\n", i, scores[i]);
}

460. Использование констант при объявлении массивов

При работе с массивами необходимо указать размер массива. Например, в следующей программе 5_VALUES.C определяется массив из 5 элементов и используется цикл/олдля вывода значений этого массива:

#include <stdio.h>

void main(void)
{
int values[5] = {80, 70, 90, 85, 80);
int i;

for (i = 0; i < 5; i++)
printf("values[%d] %d\n", i, values[i]);
}

Предположим, мы хотим изменить программу, увеличив размер массива до 10 значений; в этом случае нам придется изменить не только объявление массива, но и границу цикла for. Альтернативой этому является объявление массива с использованием константы. В следующей программе 5_CONST.C объявляется массив, размер которого равен значению ARRAY_SIZE. Как видим, программа использует константу не только для объявления массива, но и в цикле for:

#include <stdio.h>

#define ARRAY_SIZE 5

void main(void)
{
int values[AKKAY_SIZE] = (80, 70, 90, 85, 80};
int i;

for (i = 0; i < ARRAY_SIZE; i++)
 printf("values[%d] %d\n", i, values[i]);
}

Теперь, если понадобится изменить размер массива, можно просто изменить значение константы ARRA Y_SIZE; в этом случае автоматически изменится как верхняя граница цикла обработки массива, так и размер самого массива.

461. Передача массивов как параметров функций

Массив - это переменная, которая может содержать множество значений одного и того же типа. Как и все другие переменные, массивы могут использоваться как параметры функций. В следующей программе ARRFUNCT.C определяется функция show_array для вывода значений массива. Как видим, программа передает функции массив и количество элементов, содержащихся в массиве:

#include <stdio.h>

void show_array(int values[], int number_of_elements) 
{
int i;

for (i = 0; i < number of elements; i++) 
 printf("%d\n", values[i]);
}

void main(void) 
{
int scores[5] = (70, 80, 90, 100, 90};

show_array(scores, 5);
}

При объявлении функции с массивом в качестве формального параметра необязательно указывать размер массива. В случае с функцией show_values квадратные скобки, которые следуют за именем переменной value, информируют компилятор лишь о том, что параметр является массивом; размер массива для компилятора не имеет значения.

462. Массивы как функции

Как известно (С461), при объявлении массива в качестве формального параметра функции не требуется указывать размер массива. Вместо этого необходимо указать только квадратные скобки [ ]. В следующей программе ARRPARAM.C функция show_values вызывается три раза с тремя различными массивами в качестве фактических параметров массива values:

#include <stdio.h>

void show array(int values[], int number of elements) 
{
int i;
printf("Вывод на экран %d значений\n", number of elements);
for (i = 0; i < number of elements; i++) 
 printf("%d\n", values[i]);
}

void main(void) 
{
int scores[5] = {70, 80, 90, 100, 90);
int count[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
int small[2] = {-33, -44);

show_array(scores, 5);
show array(count, 10);
show array(small, 2);
}

После компиляции и выполнения программы на экран выводятся значения всех трех массивов. Обратим внимание на то, что для функции не имеет значения размер массива. Заметим, однако, что все массивы, передаваемые в функцию, имеют тип int. При попытке передать в функцию массив тпа float компилятор выдаст сообщение об ошибке.

463. Отличительная особенность символьных строк как массивов

Примеры, в которых строки выступают в роли фактических параметров вызываемых функций, встречаются в нашей книге нередко. Как правило, в таких случаях длина строки не указывается как параметр функции. Например, в следующем фрагменте используется функция struprw" преобразования строчных букв в прописные [строка фрагмента не переведена на русский язык, поскольку функция strupr не делает преобразование русских букв. - Прим. перев.}:

char title[64] = "1001 совет по C/C++";
strupr(title);

Как нам уже известно, в языке Си конец символьной строки отмечается NULL-СИМВОЛОМ. Таким образом, в функциях для определения конца строки (рассматриваемого также и как массив) делается поиск элемента, равного нулю. В массивах других типов, например, int, float или long, нет эквивалента для NULL-символа. Поэтому, когда в качестве параметра функции указывается массив элементов, приходится указывать также и другой параметр - длину (количество элементов) массива.

464. Передача массивов в стек при вызове функций

В нескольких ранее приведенных примерах рассматривалось использование массива в качестве параметра функции. При передаче массива как фактического параметра функции Си помещает в стек только адрес первого элемента массива. На рис. 464 показаны, например, массив scores и функция show_array, использующая этот массив. Как можно видеть, в стек помещается только начальный адрес массива.

Рис. 464. При передаче массива как фактического параметра Си передает в стек только адрес первого элемента массива

Как видно из рис. 464, компилятор не передает в функцию никакой информации о длине массива.

465. Максимальный объем памяти, занимаемой массивом

Не существует одной общей границы для максимального объема памяти, занимаемой массивом. При работе в среде DOS максимальный объем памяти, который может быть отведен под массив, зависит от текущей модели памяти. Вообще говоря, массив не может использовать больше 64К памяти. Компиляция следующей программы ТОО_ВIG.С может быть неуспешной из-за того, что массивы требуют достаточно много памяти:

void main(void)
{
char string[66000L]; // 66,000 байт

int values[33000L]; // 33,000 * 2 = 66,000 байт

float numbers[17000]; // 17,000 * 4 = 68,000 байт 
}

466. Использование сверхбольшой модели памяти для больших массивов

Если объем памяти, требуемый для массива, превышает 64К, то можно настроить компилятор на использование сверхбольшой модели памяти, рассматривая массив как указатель и добавив ключевое слово huge в объявлении:

float huge values[17000];

В следующей программе HUGE_FLT.C создается массив с указателем, объявленным как huge:

#include <stdio.h>
# include <malloc.h>

void main (void) 
{
int i;
float huge *values;
if ((values = (float huge *) halloc (17000, 
 sizeof(float))) == NULL)
 printf ("Ошибка при выделении массива huge\n");
else
 {
 printf("Заполнение массива\n");

 for (i = 0; i < 17000; i++) 
 values[i] = i * 1.0;

 for (i = 0; i < 17000; i++) 
 printf ("%8.1f ", values[i]);

 hfree(values);
 } 
}

467. Массивы или динамическая память

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

Однако, как известно, при объявлении массива необходимо указать его размер. Если точный размер массива не известен заранее, то, возможно, будет выделено больше памяти, чем потребуется на самом деле. В результате память может расходоваться впустую. Если же задавать предельно маленький размер массива, то может потребоваться часто редактировать программу, изменяя размеры массивов, и компилировать программу заново.

Итак, объявляя в программе массив, следует иметь в виду, что идентичную обработку можно обеспечить с помощью динамического выделения памяти. Как мы увидим в разделе "Массивы, указатели и структуры", при доступе к динамически выделенной памяти можно с тем же успехом использовать индексы массива, что даст возможность исключить путаницу с указателями, часто смущающую начинающих программистов. Поскольку большинство операционных систем располагает достаточно быстрыми средствами выделения памяти, рекомендуется использовать в программах систему динамического управления памятью для более эффективной и гибкой работы в обмен на незначительную потерю времени, связанную с использованием управляющей информации.

468. Многомерные массивы

Как мы уже знаем, массив - это переменная, которая может содержать множество значений одного и того жетипа. Во всех предшествующих примерах массивы представляли собой линейную последовательность данных. Однако язык Си поддерживает двумерные, трехмерные и многомерные массивы, как показано на рис. 468.

Рис. 468. Многомерные массивы

Лучший способ наглядного представления двумерного массива - это таблица, в которой выделены строки и столбцы. Трехмерный массив наглядно описывается массивом из нескольких страниц, каждая из которых содержит двумерную таблицу. На рис. 468 представлены массивы, которые объявляются следующим образом:

char strings[64];
int table[10][5];
float pages[10][5][3];

469. Столбцы и строки двумерного массива

Итак, язык Си обеспечивает возможность работы с многомерными массивами. Двумерный массив хорошо представляется как таблица со строками и столбцами. Данные в строках рассматриваются слева направо, в то время как в столбцах - сверху вниз, как показано на рис. 469.

Рис. 469. Строки и столбцы массива

При объявлении двумерного массива первое измерение задается количеством строк, а второе - количеством столбцов.

int table [2] [3];

470. Доступ к элементам двумерного массива

В качестве модели представления двумерного массива была выбрана таблица строк и столбцов. Для обращения к требуемому элементу двумерного массива необходимо указать номер строки и номер столбца. На рис. 470 показаны операторы, в которых осуществляется доступ к требуемым элементам массива table.

Рис. 470. Для обращения к требуемому элементу двумерного массива необходимо указать номер строки и номер столбца.

471. Инициализация элементов двумерного массива

При инициализации элементов двумерного массива используется техника, аналогичная той, которая использовалась для одномерных массивов (см. С457)-перечисление значений, заключенных внутри фигурных скобок. Однако, в данном случае значения двумерного массива в каждой строке заключаются в фигурные скобки { } отдельно:

int table[2][3] = {{1, 2, 3}, 
 {4, 5, 6}};

Компилятор Си инициализирует элементы массива, как показано на рис. 471.

Аналогично, в следующем операторе показана инициализация элементов массива, в котором количество строк и столбцов больше:

int sales[4][5] {{1, 2, 3, 4, 5},
 {6, 7, 8, 9, 10},
 {11, 12, 13, 14, 15},
 {16, 17, 18, 19, 20}};

472. Определение объема памяти для многомерного массива

В С456 показано, как определить объем памяти, требуемый для одномерного массива - количество элементов массива умножается на количество байтов, требуемое для представления типа массива (например, 2 - для int, 1 -для char и т.д.). Для определения объема памяти в случае многомерного массива необходимо выполнить те же операции. Количество элементов многомерного массива просто равно произведению всех его измерений. Например, количество элементов двумерного массива равно произведению количества строк на количество столбцов. Следующие объявления массивов сопровождаются подсчетом соответствующего объема памяти:

int а[5][10]; // 2 * 5 * 10 == 100 байт 
float b[5][8]; //4 * 5 * 8==160 байт 
int c[3][4][5]; //2 * 3 * 4 * 5 = 120 байт

В следующей программе MDJ3IZE.C при определении количества байтов, требуемых для различных массивов, используется операция sizeof:

#include <stdio.h>
void main(void) 
{
int box[3][3];

float year_sales[52][5];

char pages[40][60][20];

printf("Для массива int bох[3][3] требуется %d байт \n", sizeof (box));
printf("Для массива float year_sales[52][5] требуется %d байт \n",
 sizeof(year_sales));
printf("Для массива char pages[40][60][20] требуется %ld байт \n",
 sizeof(pages));
}

После компиляции и выполнения программы на экране появляется следующее:

C:\> MDJSIZE <Enter>
Для массива int bох[3][3] требуется 18 байт
Для массива float year_sales[52][5] требуется 1040 байт
Для массива char pages[40][60][20] требуется 48000 байт

473. Использование элементов двумерного массива в цикле

В С458 было продемонстрировано использование переменной для индексирования элементов массива. При работе с двумерными массивами для обращения к элементам массива обычно используются две индексные переменные. В следующей программе SHOW_2D.C для вывода значений массива table используются переменные row и column:

#include <stdio.h>

void main(void) 
{
int row, column;

float table[3][5] = {{1.0, 2.0, 3.0, 4.0, 5.0}, 
 {6.0, 7.0, 8.0, 9.0, 10.0}, 
 {11.0, 12.0, 13.0, 14.0, 15.0}};

for (row = 0; row < 3; row++) 
 for (column = 0; column < 5; column++) 
 printf("table[%d][%d] = %f\n", row, column, table[row][column]);
}

Используя вложенные циклы for, программа выводит на экран элементы сначала из первой строки (от 1.0 до 5.0), затем из второй и, наконец, из третьей.

474. Обработка трехмерного массива

В С473 было показано, как пройти по всем элементам двумерного массива, используя две переменные row и column. В следующей программе SHOW_3D.C используются три переменные: row, column и table для обработки трехмерного массива:

#include <stdio.h>

void main(void) 
{
int row, column, table;

float values[2][3][5] = {
 {{1.0, 2.0, 3.0, 4.0, 5.0}, 
 {6.0, 7.0, 8.0, 9.0, 10.0}, 
 {11.0, 12.0, 13.0, 14.0, 15.0}},
 
 {{16.0, 17.0, 18.0, 19.0, 20.0}, 
 {21.0, 22.0, 23.0, 24.0, 25.0}, 
 {26.0, 27.0, 28.0, 29.0, 30.0}} 
 };

for (row = 0; row < 2; row++) 
 for (column =0; column < 3; column++) 
 for (table = 0; table < 5; table++)
 printf("values[%d][%d][%d] = %f\n", row, column, table, 
 values[row][column][table]);
}

475. Инициализация многомерного массива

В С474 было показано, как вывести на экран значения элементов трехмерного массива, используя три переменные: row, column и table. Там же была применена инициализация трехмерного массива:

float values[2][3][5] = {
 {{1.0, 2.0, 3.0, 4.0, 5.0},
 {6.0, 7.0, 8.0, 9.0, 10.0}, 
 {11.0, 12.0, 13.0, 14.0, 15.0}},

 {{16.0, 17.0, 18.0, 19.0, 20.0}, 
 {21.0, 22.0, 23.0, 24.0, 25.0}, 
 {26.0, 27.0, 28.0, 29.0, 30.0}}
 };

На первый взгляд, инициализация многомерного массива при его объявлении может породить путаницу. Для того, чтобы показать, как инициализируются такие массивы, ниже представлены различные примеры.

Для того, чтобы правильно задавать данные инициализации, необходимо представлять себе, каким образом формируются соответствующие операторы присваивания этих данных элементам массива. Прежде всего, глубина вложенности фигурных скобок соответствует количеству измерений массива. Самая внешняя пара фигурных скобок соответствует объявлению всего массива. Число пар скобок первого уровня соответствует числу строк (элементов первого измерения); число пар скобок второго уровня, заключенных в скобки первого уровня, соответствует числу столбцов (элементов второго измерения); число элементов, заключенных внутри каждой пары скобок второго уровня, соответствует количеству таблиц (элементов третьего измерения) и т.д. - Прим. перев}

int а[1][2][3] = {
 { {1, 2, 3}, (4, 5, 6} }
 
 }; // Эти скобки используются для инициализации 
 // всего массива

int b[2][3][4] = {
 { {1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12} }, 
 { {13, 14, 15, 16}, {17, 18, 19, 20}, {21, 22, 23, 24} } 
 
 }; // Эти скобки используются для инициализации 
 // всего массива

int с[3][2][4] = {
 { {1, 2, 3, 4}, {5, 6, 7, 8} },
 { {9, 10, 11, 12}, {13, 14, 15, 16} }, 
 { {17, 18, 19, 20}, {21, 22, 23, 24} } 
 
 }; // Эти скобки используются для инициализации 
 // всего массива

int d[l][2][3][4] = {
 {{{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}}, 
 {{13, 14, 15, 16}, {17, 18, 19, 20}, {21, 22, 23, 24}}} 
 
 }; // Эти скобки используются для инициализации 
 // всего массива

476. Двумерные массивы как фактические параметры

При работе с многомерными массивами может возникнуть необходимость определять функции, имеющие массивы в качестве параметров. В С461 мы видели, что при передаче функции в качестве фактического параметра одномерного массива нет необходимости указывать количество его элементов. При передаче функции двумерных массивов необязательным является только количество строк, но количество столбцов должно указываться. В следующей программе FUNCT_2D.C определяется функция show_2d_array для вывода значений элементов различных двумерных массивов:

#include <stdio.h>

void show_2d_array(int array[][10], int rows) 
{
int i, j;

for (i = 0; i < rows; i++) 
 for (j = 0; j < 10; j++)
 printf ("array [%d] [%d] =%d\n", i, j, array[i][j]);
}

void main(void) 
{
int a[1][10] = {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}};
intb[2][10] = {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
 {11, 12, 13, 14, 15, 16/ 17, 18, 19, 20}};

int c[3][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}};

show_2d_array(a, 1);
show_2d_array(b, 2);
show_2d_array(с, 3);
}

477. Одномерная интерпретация многомерных массивов

Из С476 мы знаем, что при передаче функции двумерного массива как фактического параметра необходимо указать количество столбцов:

void show 2d array(int array[][10], int rows)

Однако, если работа с многомерным массивом не требует доступа к элементам посредством конкретного указания строк, столбцов и т.д., то функция может воспринимать многомерный массив как одномерный, хотя в качестве фактического параметра ей передается многомерный массив. В следующей программе SUM_2D.C определяется функция, возвращающая сумму значений двумерного массива.

#include <stdio.h>

long sum_array(int array[], int elements) 
{
long sum = 0;
int i;
for (i = 0; i < elements; i++) 
 sum += array[i];
return(sum);
}

void main(void) 
{
int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int b[2][10] = {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
 {11, 12, 13, 14, 15, 16, 17, 18, 19, 20}};

int c[3][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}};

printf("Сумма элементов первого массива %d\n", sum array(a, 10));
printf("Сумма элементов второго массива %d\n", sum array(b, 20));
printf("Сумма элементов третьего массива %d\n", sum array(с, 30));

Как видим, функция sum_arrays справляется с одномерными массивами так же, как и с двумерными и многомерными массивами. Секрет работы функции с массивами заключен в способе, принятом в Си для хранения в памяти многомерных массивов. Подробнее об этом в С478.

478. Хранение многомерных массивов в Си

В С454 мы видели, что при объявлении массива, например inl scores[100], Си выделяет память, достаточную для размещения всех элементов массива. Для многомерного массива делается то же самое. Хотя логически многомерный массив содержит строки, столбцы и т.д., для компилятора он представляет собой один длинный массив байтов. Например, пусть в программе объявлен следующий массив:

#include table[3][5];

На рис. 478 показано логическое представление и фактическое использование памяти для этого массива.

Рис. 478. Размещение в памяти элементов двумерного массива

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

479. Расположение элементов массива по строкам или по столбцам

В С478 мы видели, что компилятор Си располагает многомерный массив в одномерной памяти. Для этого есть две возможности. Как показано на рис. 479, компилятор может начать расположение элементов многомерного массива как с первого столбца, так и с первой строки и далее:

Рис. 479. Два способа расположения в памяти элементов двумерного массива

Если компилятор размещает в памяти сначала первый столбец, затем второй и т.д., то такое размещение называется по столбцам. Если компилятор размещает в памяти сначала первую строку, затем вторую и т.д., то такое размещение называется по строкам. Компиляторы Си размещают многомерные массивы по строкам.

480. Массивы структур и структуры в массивах

Массивы и структуры объединяют информацию в группы. Известно, что Си позволяет создавать массивы структур и использовать массивы как члены структур. Вообще говоря, в Си не предусмотрено ограничение на глубину вложенности при размещении данных в структурах. Например, в нижеследующем фрагменте объявляется массив, содержащий данные о 100 служащих, организованные в виде структуры. Внутри этой структуры объявляется массив emp_dates структур типа Date, который содержит даты приема, первой и последней аттестации служащего:

struct Employee { 
 char name[64];
 int age;
 char ssan[ll]; // Социальный номер служащего int pay_grade;
 float salary;
 unsigned employee_number;

struct Date {
 int month;
 int day;
 int year;
 } emp_dates[3];
} staff[100];

Обращение к элементам структуры и массивам выполняется по вложенности, "слева направо", начиная с внешнего имени. Например, дата приема служащего устанавливается следующим образом:

staff[10].emp_dates[0].month = 7;
staff[10].emp_dates[0].day = 7;
staff[10].emp_dates[0].year = 7;

Хотя использование вложенных структур и массивов может быть очень удобно, имейте в виду, что дальнейшее усложнение структуры может привести к ухудшению понимания вашей программы другими программистами.

481. Объединения

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

struct EmpDates { 
 int days worked;
 struct LastDate { 
 int month;
 int day;
 int year;
 } last_day;
};

Поскольку в программе на самом-то деле используется либо days_worked (количество отработанных дней), либо last_day (дата последнего выхода на работу), то это приводит к ненужной трате памяти, поскольку в любом случае имеется неиспользуемое значение. В качестве альтернативы Си поддерживает union (объединение), что позволяет выделять память, достаточную для хранения элемента объединения наибольшей длины:

union EmpDates { 
 int days worked;
 struct LastDate { 
 int month;
 int day;
 int year;
 } last_day;
};

Для обращения к члену объединения используется оператор "точка" (.), как при обращении к членам структуры. В отличие от структуры, объединение хранит значение только одного члена. На рис. 481 показано, как Си выделяет память для структуры и объединения.

Рис. 481. Распределение памяти для похожих структуры и объединения

Как станет известно, использование объединений не только экономит память, но дает также возможность различной интерпретации значений в памяти.

482. Экономия памяти при использовании объединений

В С481 мы познакомились с еще одним способом хранения информации - объединение (union). При использовании объединений Си выделяет память, требуемую элементу объединения наибольшей длины. В следующей программе UNIONSIZ.C для вывода на экран объема памяти, требуемой для хранения объединений, используется операция sizeof:

#include <stdio.h>

void main(void)
{
union EmployeeDates { 
 int days_worked;
 struct Date { 
 int month;
 int day;
 int year;
 } last_day;
} emp_info;

union Numbers { 
 int a;
 float b;
 long c;
 double d; // Элемент наибольшей длины требует 8 байт 
 } value;

printf("Длина объединения EmployeeDates равна %d байт\n", sizeof(emp info));
printf("Длина объединения Numbers равна %d байт\n", sizeof(value));
}

После компиляции и выполнения программы на экране появляется следующее:

C:\> UNIONSIZ <Еnter>
Длина объединения EmployeeDates равна 6 байт
Длина объединения Number равна 8 байт

483. Объединение REGS - классический пример объединения

Итак, использование объединений в программе экономит память и позволяет интерпретировать одну и ту же память по-разному. В разделе "Обслуживающие средства DOS и BIOS" мы увидим, что для того, чтобы получить доступ к средствам DOS и BIOS в программе, выполняется присваивание необходимых значений определенным регистрам (на уровне языка ассемблера). Для обеспечения доступа к системным средствам DOS и BIOS в Си-программах большинство Си-компиляторов предоставляет соответствующие библиотечные подпрограммы, которые используют объединение типа REGS:

struct WORDREGS {
 unsigned int ax, bx, ex, dx, si, di, cflag, flags;
};

struct BYTEREGS {
 unsigned char al, ah, bl, ah, cl, ch, dl, dh;
};

union REGS {
 struct WORDBEGS x;
 struct BYTEREGS h;
};

При обращении к регистрам общего назначения (АХ, ВХ, СХ, DX) их содержимое может быть доступно как в целом, в 16-разрядном формате, так и по отдельным байтам, старшим и младшим (AL, АН, BL, ВН, CL, CH, DL, DH). Поскольку оба метода относятся к одним и тем же регистрам, имеем два способа просмотра одних и тех же ячеек памяти. В таких случаях удобно использование объединения. На рис. 483 показано, как хранится информация в памяти при использовании объединения REGS.

Рис. 483. Хранение значений переменных типа "объединение REGS"

484. Использование объединения REGS

В С483 было выделено особо одно из наиболее часто используемых объединений -объединение REGS. В следующей программе GET_VERX.C объединение REGS используется для вывода на экран текущей версии DOS. Регистры общего назначения используются в 16-разрядном формате (слово). #include <stdio.h> #include <dos.h> void main(void) { union REGS inregs, outregs; inregs.x.ax = 0х3000; intdos(&inregs, &outregs); printf("Текущая версия DOS: %d.%d\n", outregs.x.ax & OxFF, outregs.x.ax >> 8); }


В следующей программе GET_VERH.C для вывода на экран текущей версии DOS используются регистры в 8-разрядном формате (байт):

#include <stdio.h> 
#include <dos.h>

void main(void)
{
union REGS inregs, outregs;

inregs.h.ah = 0х30;
inregs.h.al = 0;
intdos(finregs, &outregs);

printf("Текущая версия DOS: %d.%d\n", outregs.h.al, outregs.h.ah);
}

485. Битовые поля

Во многих функциях, приведенных в этой книге, показано, как уменьшить количество переменных (а значит, и количество занимаемой памяти) в том случае, когда параметры функции или возвращаемое значение имеют битовую структуру. Если отдельные биты или их комбинации имеют определенный смысл, то можно использовать в программе битовые операции для получения значений группы битов. Предположим для примера, что программа работает с датами количеством 100 000. Один из способов - создать структуру типа Date:

struct Date {
int month; // от 1 до 12
int day; // от 1 до 31
int year; // последние две цифры 
};

В качестве альтернативы для хранения даты можно использовать'битовое представление одного значения unsigned int, как показано на рис. 485.

Рис. 485. Битовое представление даты

Таким образом, каждый раз при присваивании даты в программе необходимо использовать корректные битовые операции, как показано в следующем фрагменте:

unsigned date;
date = month;
date = date | (day << 4);
date = date | (year << 9);

printf("Месяц %d День %d Год %d", date & OxF, 
 (date >> 4) & OxIF, (date >> 9));

Однако, чтобы программа была проще для понимания, Си дает возможность создавать структуру, составленную из битовых полей. При объявлении такой структуры в каждом поле после двоеточия указывается количество битов на этом поле:

struct Date {
unsigned month:4;
unsigned day:5;
unsigned year:7;
} date;

Обращение к битовым полям делается следующим образом:

date.month = 12;
date.day = 31;
date.year = 94;

printf("Месяц %d День %d Год %d", date.month, date.day, date.year);

Примечание: При объявлении битовых полей тип элементов структуры должен быть целым без знака (unsigned int).

486. Представление битовых полей в структуре

В С485 обсуждалась возможность поразрядного представления значения с использованием структуры битовых полей. При объявлении такой структуры Си выделяет достаточно памяти для хранения всех битов структуры. Если в последнем байте некоторые биты не использованы, те большинство Си-компиляторов инициализируют эти биты нулями. Для пояснения того, как Си хрант информацию в структуре битовых полей, приводится рис. 486, иллюстрирующий представление битовых полей структуры Date.

struct Date {
unsigned month:4;
unsigned day:5;
unsigned year:7;
} date;

Рис. 486. Представление битовых полей структуры Date

Категория: Языки программирования | Добавил: Fenix (25.03.2008)
Просмотров: 9492 | Комментарии: 2 | Рейтинг: 5.0/1 |
Всего комментариев: 2
2 eoygejr  
0
http://imtov.ru/?ps_catalog=smartfon-htc-one-sv-czvet-goluboi Смартфон HTC One SV. Цвет: голубой

1 TS-Disguise  
0
Всем Привет !!!

Компания In-Disguise .com
Рада Представить Вам - Анонимный и Полностью Автоматический VPN Сервис!

In-Disguise .com - Полностью Автоматический VPN Сервис, Вам Больше не Придеться Терять Время На:

1 - Поиск Сапортов при внезапном отключении одного из серверов, Поиски как устоновить Стандартный OpenVPN Клиент,
Скачивая Конфиги тратить время на их установку в программе.
Устранять неполадки в системе при обновляниях, для стабильного соединения с ВПН.
Сервис предостовляет Подписки Включающие доступ ко Всем Серверам Всего за 9 EURO.
DoubleVPN, OPENVPN и PPTP VPN - ДОСТУП К 18 СЕРВЕРАМ В 10 СТРАНАХ !
Подписка на Все Сервера нашего Сервиса = Всего 9 Euro; 3 месяца = 20 Euro; 6 месяцев = 35 Euro; 1 год = 55 Euro.

2 - Уникальный VPN Клиент, Который ЛЕГКО Устанавливается на Все Виды Операционных Систем: MAC/Windows/Liinux!!!
Позволит Вам ЛЕГКО Переключать ВПН Сервера Между Стран, Таких Как: USA/DE/UK/IT/NL/LU/EG/PA/RO/MY
Постоянно Пополняеться Список Доступных Стран.
В Ближайшее Время появятся Сервера в: Испании, Греции, Швеции, Мексике, Чехии, Польше, Китае, Бельгии...
Виды VPN Соединение Включающие в одной Подписке - DoubleVPN и OpenVPN.

3 - В Программе ЛЕГКО Настраивать Функции VPN соединения с Вашим Интернетом:
- Автоматически Блокировать Интернет Соединения при Разрыве Связи с ВПН.
- Автоматическое Соединение с ВПН при Включение Интернета.

4 - Компания принимает Все Виды Oплаты в Автоматическом Режиме!!!
WebMoney/Visa/Masteer Card/PayPall/Liberty Reserv/BitCoin/SMS и много других.

Наш Сайт Тут in-disguise .com /?aff=69
С Уважением Компания In-Disguise .com

=====

А так же у Сервиса Имеется Уникальная З-х Уровневая, Приватная Партнерская Программа:

Вы получаете 35 % От всех платежей Ваших клиентов !!!
Pегистрация только по инвайт коду - 1 код расчитан на одну регистрацию.
Если вы зарегестрируетесь, Вы можете стать Овнером Партнерки!!!
Прдложив партнерку своим друзьям вы бдете получать 11% со всех их клиентов.
Наш Сайт Тут - vpnincome .com
Если Вас Заитересовал Наш ВПН Сервис или Партнерка, то по всем вопросам Обращайтесь по Контакту:
ICQ: 6850058
E-mail: paul_okenfold @ yahoo.com

Пишите Нам, Мы будем Рады Вас Слышать!!!
Комнпания In-Disguise .com

Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]
Форма входа
Поиск
Друзья сайта
Статистика

Онлайн всего: 1
Гостей: 1
Пользователей: 0
Copyright MyCorp © 2024Используются технологии uCoz