Учебная работа. Лабораторная работа: Одномерные и двумерные массивы

1 Звезда2 Звезды3 Звезды4 Звезды5 Звезд (Пока оценок нет)
Загрузка...
Контрольные рефераты

Учебная работа. Лабораторная работа: Одномерные и двумерные массивы

Кафедра: Автоматика и информационные технологии

ОДНОМЕРНЫЕ И ДВУМЕРНЫЕ МАССИВЫ

Содержание

1. Теоретическая часть

1.1 Определение массива

1.2 Размещение в памяти

1.3 Воззвание к элементу массива

1.4 инициализация массивов

1.4.1 Одномерные массивы

1.4.2 Двумерные массивы

1.5 Тип имени массива

1.6 Передача одномерных массивов в функцию

1.7 Передача двумерных массивов в функцию

1.8 Тип и базисный тип указателя

1.9 Правила определения типа указателей

1.10 Указатель на void

1.11 Постоянные указатели

1.12 Адресная математика

1.13 Одинарный указатель – это одномерный массив

1.14 Одномерный массив – это одинарный указатель

1.15 Двумерный массив – это двойной указатель.

1.16 Двойной указатель – это двумерный массив

1.17 Просмотр указателей в отладчике

1.18 Контрольные вопросцы.

2. Лабораторные задания

2.1 Скалярное произведение

2.2 Минимакс

2.3 Массивы строк

2.4 Трехмерный массив

3. Доп задания

Библиографический перечень

1. Теоретическая часть

1.1 Определение массива

Определение. Массивом именуется огромное количество частей 1-го типа, расположенных в памяти поочередно вереницей.

При первом упоминании о массиве в программке под него сходу выделяется память. Потому верно гласить не о объявлении массива, а о определении массива.

Синтаксис определения массива имеет вид

Тип элемента имя массива [n1
][n2
]…[nk
];

где имя массива — идентификатор, определяемый в качестве имени массива, а ni
— размеры массива. Массив именуется k-мерным массивом с элементами типа тип элемента. Элементы i-го измерения имеют индексы от 0 до ni
-1. Тип элемента массива быть может одним из главных типов, типом указателя (pointer), типом структуры (struct) либо типом объединения (union). Хотя элементы массива не могут быть функциями, они могут быть указателями на функции.

Ниже приведены некие примеры определений массива:

int Page[10]; /* одномерный массив из 10 частей типа int, пронумерованный с 0 до 9 */char line[81];/*массив знаков либо строчка, в которую можно записать не наиболее 80 знаков */float big[10][10], sales[10][5][8];

1.2 Размещение в памяти

Массивы могут быть последующих видов:

1. Локальные. Размещаются в стеке. к примеру,

main(){

int A[10];

//…..

}

2. Статические. Размещаются в области данных, глобальных и статических переменных. к примеру,

main(){

static int A[10];

//…..

}

3. Глобальные. Размещаются в области данных, глобальных и статических переменных. к примеру,

int A[10];

main(){

//…..

}

4. Далекие глобальные. Размещаются в далекой области глобальных переменных. К примеру,

far int A[10];

main(){

//…..

}

Двумерные массивы размещаются в памяти по строчкам. Исходную строчку массива именуют нулевой строчкой.

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

к примеру, трехмерный массив intA[3][4][5] размещается в памяти слоями A[0][…][…], …, A[2][…][…].

Любой слой, как двумерный массив, размещается по строчкам. к примеру, A[0][0][…], …, A[0][3][…].

Массивы могут располагаться лишь в границах 1-го сектора, другими словами общий размер массива в б не превосходит 64К.

1.3 Воззвание к элементу массива

Элементы массива могут стоять в обеих частях операции присваивания, другими словами являются объектами Lvalue.

Задание элемента k-мерного массива реализуется поочередным применением операций индексации:

x[i1
][i2
]…[ik
],

где ij
— целое выражение, при всем этом 0<=ij
<=nj
-1, где nj
-1 — наибольшее

Page[5]

line[i+j-1]

big[i][j]

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

Операция индексации является левоассоциативной операцией, другими словами производится в выражении слева вправо. Потому при воззвании к элементу массива сначала производится левая операция индексации []. К приобретенному результату применяется 2-ая операция индексации [] и т.д.

1.4 инициализация массивов

Инициализация массивов быть может полной и частичной.


1. В случае полной инициализации указывается полный перечень значений в фигурных скобках.

int A[4] = {1, 4, 2, 6};

размеры массивов при полной инициализации можно не указывать. Компилятор сам себе обусловит эти размеры и выделит подобающую память. Программер может отыскать размеры при помощи операции
. Операция возвращает размер всего, что угодно в б. А именно, sizeof(A) возвращает размер массива в б. к примеру,

int A[] = {1, 4, 2, 6};

int Dim = sizeof(A)/ sizeof(int); // 8/2=4

Лучшеписать

int Dim = sizeof(A)/ sizeof(A[0]); // 8/2=4

2. В случае частичной инициализации указывается размер массива и неполный перечень значений в фигурных скобках. Неинициализированные элементы получают нулевые значения. В случае

int A[4] = {1, 4};

элементы A[0] и A[1] получили значения, а в A[2] и A[3] записаны нули .

Если перечень инициализации больше размера массива, то возникнет ошибка компиляции.

// int A[4] = {1, 4, 4, 7, 2}; Ошибка


1. В случае полной инициализации указывается полный перечень значений в фигурных скобках. Любая строчка инициализируется в собственных фигурных скобках.

int A[3][4] ={ {1, 4, 2, 6},

{11, 1 4, 1 2, 1 6},

{1, 4, 2, 6}

};

1-ый размер массива, другими словами количество строк, при полной инициализации можно не указывать.

int A[][4] ={ {1, 4, 2, 6},

{11, 1 4, 1 2, 1 6},

{1, 4, 2, 6}

};

Компилятор сам себе обусловит количество по списку инициализации. Программер может отыскать 1-ый размер при помощи операции
. А именно, sizeof(A) возвращает размер двумерного массива в б, а sizeof(A[0]) возвращает размер строчки в б. к примеру,

int KolStrok = sizeof(A)/ sizeof(A[0]); // 24/82=3

2. В случае частичной инициализации указываются все размеры массива и неполные списки значений в фигурных скобках.

int A[4][4] ={ { 2, 6},

{ 1 4, 1 2, 1 6},

{6}

};

Если размер перечень инициализации больше хотя бы 1-го размера массива, то возникнет ошибка компиляции.

// int A[2][4] = {{1, 4, 4, 7, 2},

{1, 4, 4, 2}}; Ошибка

внимание. Допускается инициализация двумерного массива одной парой фигурных скобок

int A[2][4] = { 1, 4, 4, 7, 2, 1, 4, 4, 2};

Но таковой метод чреват логической ошибкой в случае частичной инициализации.

внимание.
При определении массива без инициализации все размеры нужно указывать очевидным образом.

1.5 Тип имени массива

Для одномерных массивов типом имени массива является

Примеры.

имя А массива charA[20]; имеет тип char[].

имя В массива floatB[10] имеет тип float[].

Для двумерных массивов типом имени массива является

В типе имени массива нет инфы о первом размере массива – о количестве строк.

Примеры.

имя А массива charA[10][20]; имеет тип char[][20].

имя B массива charB[100][20] тоже имеет тип char[][20].

имя C массива charC[20][10] имеет тип char[][10], хороший от типов А и В.

1.6 Передача одномерных массивов в функцию

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

В перечне формальных аргументов указываются лишь типы. Добавлять размер массива никчемно.

Массивы передаются в функцию по адресу. Это значит, что функция работает с оригиналом массива и может изменять его элементы. Это факт употребляется для возврата массивов из функции. При помощи оператора return массив возвратить недозволено.

Примеры прототипов

1. int max(int *A, int Dim);

Можно также писать

intmax(intA[], intDim);

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

// int max(int A[100]); ошибка

В перечне формальных характеристик записи int *A и intA[] равносильны.

2. float scal(float A[], float B[], int Dim);

Пример. Функция находит сумму частей одномерного массива типа int и программку.

int sum( int *A, int Dim); // прототипфункции

int sum( int *A, int Dim)

{

int S =0;

for (int i = 0; i < Dim; i++)

S += A[i];

return S;

}

void main ()

{

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

int N = sizeof(B)/sizeof(B[0]);

printf (“nСумма частей равна %d”, sum(B, N) );

}

Пример. Функция находит все четные элементы в одномерном массиве.

intVseChot(intA[], intDimA, intChot[], intDimChot);

Функция находит четные элементы массива А и помещает их в массив Chot. При всем этом нужно смотреть, чтоб количество четных частей не превысило размер DimChot массива Chot. Возвращает количество отысканных четных частей. Если количество четных частей превысило размер DimChot, то ворачивается -1, а массив Chot стопроцентно состоит из четных частей массива А.

int VseChot(int A[], int DimA, int Chot[], int DimChot)

{

int count = 0;

for(int i = 0; i < DimA; i++)

if ( A[i] % 2 == 0) //четное число

if (count < DimChot)

Chot[count++] = A[i];

else

return -1;

}

void main()

{

int A[]={1, 2, 4, 6 ,7, 5};

int B[4];

int res = VseChot( A, 6, B, 4);

if(res = -1)

{

printf(“n Найдены не все четные элементы массива:”);

for (int I = 0; I < 4; i++)

printf(“%d ”, B[i]);

}

else

{

printf(“n Список четных частей массива :”);

for ( i = 0; i < 4; i++)

printf(“%d ”, B[i]);

}

}

1.7 Передача двумерных массивов в функцию

В случае двумерных массивов необходимо буквально соблюдать совпадение типов фактических и формальных характеристик функции.

Примеры.

1. Функция находит наибольший элемент в массиве

int max(int A[][100], int KolStroc, int KolStolb);

Данная функция может вызываться лишь для массивов, у каких 2-ой размер 100. В неприятном случае, будет ошибка компиляции.

К примеру, можно вызвать эту функцию для отчасти инициализированного массива

int A[][100] = {{1,3,5}, {15,2,3}};

int res = max( A, 2, 3);

2. Функция находит сумму частей двумерного массива

При передаче двумерного массива тут применено очевидное преобразование типа двумерного массива к типу одномерного массива. Это дозволяет вызывать функцию для всех двумерных массивов.

intsum(intA[], intKolStroc, intKolStolb)

{

};

void main()

{

}

1.8 Тип и базисный тип указателя

Определение. Указателем именуется переменная, объявленная последующим образом

type *имя_указателя;

Определение. Типом указателя именуется type*.

Определение. Базисным типом указателя именуется тип type данного, на который показывает указатель.

Примеры.

1. Одинарный указатель int *pi имеет тип int* и базисный тип int.

2. Одинарный указатель structdate *pd имеет тип date* и базисный тип date.

3. Двойной указатель float **ppf имеет тип float** и базисный тип float*.

4. Редчайший тройной указатель char ***pppc имеет тип char *** и базисный тип char **.

Более нередко употребляются одинарные и двойные указатели, очень изредка тройные указатели. Указатели с 4 звездочками — признак неверной ситуации.

1.9 Правила определения типа указателей

· Применение к хоть какой переменной name операции взятия адреса &name добавляет к типу результата одну *.

· Применение к хоть какому указателю ptr операции разыменования *ptr удаляет из типа результата одну *.

· Применение к хоть какому указателю ptr операции индексации ptr[0] удаляет из типа результата одну *.

Примеры.

int **ptr; // тип ptr – это int **

// тип *ptr – это int *

// тип **ptr – это int

// выражение ***ptrошибочно

// тип ptr[0] – это int *

// тип ptr[3][5] – это int

// выражение ptr[1][1][1] неверно

// тип *ptr[0] – это int

// тип &(*ptr[0]) – это int*

1.10 Указатель на void

Указатель типа void * не имеет базисного типа и для предстоящей работы с ним к нему нужно применить операцию очевидного преобразования типа.

Указатель типа void * показывает на все, что угодно. Главному слову void тут приписывается не

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

В операциях присваивания указатель на voidможет стоять в левой части. Это значит, что указателю void* можно присвоить хоть какой указатель. При размещении указателя void* в правой части его нужно преобразовывать к указательному типу левой части. К примеру.

int i=5, *pi = &i;

void *ptr;

ptr = pi;

//pi = ptr; ошибка

pi = (int *)ptr; // верно

1.11 Постоянные указатели

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

Пример 1.

int *Arr = {1,3,2,4,5}, *B = {1,1};

При данном определении допустимы последующие два оператора

Arr[0]=100;

Arr = B;

Пример 2.

const int *Arr = {1,3,2,4,5}, *B = {1,1};

// Arr[0]=100; ошибка

Arr = B;

Пример 3.

int *const Arr = {1,3,2,4,5}, *B = {1,1};

Arr[0]=100;

//Arr = B; ошибка

Пример 4.

const int *const Arr = {1,3,2,4,5}, *B = {1,1};

//Arr[0]=100; ошибка

//Arr = B; ошибка

Обычно постоянные указатели употребляют для строк

constchar* str = “Hello”;

В этом случае защищается содержимое текстовых строк.

1.12 Адресная математика

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

Пример.

1. int *A = (int *)malloc(20);

2. int **A; char *c; A = (int **)c;

Операция разыменования
* возвращает

Пример.

int i = 5, *pi = &i;

*pi = 10;// i =10

Получение адреса указателя
. Подобно хоть каким переменным, переменная типа указатель имеет адресок и

Операция & добавляет к типу результата одну *.

Пример.

int n=20, *pn = &n, **ppn;

ppn = &pn;

Повышение указателя
. К указателю
можно добавлять и вычитать хоть какое целое число
. При всем этом указатель меняется на количество б равное n, умноженному на размер в б базисного типа указателя ptr.

Соответственно, к указателям применимы операции инкремента ++ и декремента —.

Пример.

int n=20, *pn = &n, **ppn;

pn = pn + 5;

// базисный тип pn – int, занимает 2 б, потому pn возрастет на 10 б.

ppn = &pn+5;

// базисный тип выражения &pn – int*, занимает 4 б для модели large, потому pn возрастет на 20 б.

Сопоставление указателей
на равенство и неравенство применимо лишь к указателям 1-го типа.

Разность указателей
. Можно отыскивать разность 2-ух указателей 1-го типа. Результатом является количество частей базисного типа, находящимися меж этими указателями. Итог имеет тип int для ближних указателей и тип long для далеких указателей.

Пример.

int A[10];

int *px = &A[1], *py = &A[9];

int n;

n = (int)(py — px); // n = 8

1.13 Одинарный указатель – это одномерный массив

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

int n=10, *pi;

pi = &i;

Тогда pi[0] – это переменная i, . pi[1] – это переменная типа int, расположенная справа от i, pi[-1] – это переменная типа int, расположенная слева от i.

1.14 Одномерный массив – это одинарный указатель

имя одномерного массива, взятое {само по себе}, является постоянным указателем на исходный элемент этого массива. К имени массива можно использовать операции указательной математики, не изменяющие содержимое указателя.

intA[5];

Тип А – это int *, базисный тип — int.

А+1 – это адресок элемента A[1], а разыменование *(А+1) – это A[1], *(А+4) – это крайний элемент A[4], внедрение выражения *(А+5) в хоть какой части операции присваивания является логической ошибкой выхода индекса массива за спектр.

внимание
. Большое отличие меж указателем и именованием массива заключается в том, что указатель является переменной, размещаемой в ОЗУ. Указатель сам имеет адресок и занимает 2 либо 4 б зависимо от того, ближний это указатель либо далекий. имя массива является адресной константой, не имеет адреса и не занимает места в ОЗУ.

1.15 Двумерный массив – это двойной указатель

Разглядим определение двумерного массива

intA[3][5];

Массив имеет три строчки по 5 частей типа int. При всем этом A[0] – это исходная, строчка из 5 частей типа int, другими словами одномерный массив из 5 частей типа int. Но тип имени одномерного массива не содержит размера этого массива. Потому тип указателя A[0] – это int*, а базисный тип int.

Соответственно, А[1] – это 1-ая строчка массива, тип А[1] – это int*. Практически A[0] – это адресок исходного элемента нулевой строчки, A[1] – это адресок исходного элемента первой строчки и т.д.

Обратите внимание, тип A[0] не содержит размера 5.

Разглядим имя двумерного массива А, взятое {само по себе}. Идентификатор А – это адресок исходной строчки из 5 частей типа int. Тип А – это int

[5]. В данном выражении участвуют три операции: круглые скобки (), индексация [] и разыменование*. Перечисление операций тут идет по убыванию приоритета. Читать выражение int

[5] необходимо последующим образом: указатель на массив из 5 частей типа int.

Таковым образом, тип указателя А содержит один из размеров двумерного массива, а конкретно количество столбцов. Отсюда вытекает, что два массива

intB[10][5], C[3][20];

имеют различные типы. Указатель В имеет этот же тип int

[5], а указатель С имеет иной тип int

[20].

Дальше, применим к двойному постоянному указателю А указательные операции.

A+1 – это адресок первой строчки из 5 частей типа int. Тип A+1 – это int

[5], базисный тип – одномерный массив из 5 частей типа int, другими словами int*.

В общем случае, A+i это адресок i-ой строчки из 5 частей типа int. Тип A+i – это int

[5], базисный тип – одномерный массив из 5 частей типа int, другими словами int*.

*(A+i) – это сама i-ая строчка из 5 частей типа int, другими словами адресок нулевого элемента первой строчки. Тип *(A+i) – это int*, базовыйтип *(A+i) – это int.

*(A+i) + j – это адресок j-го элемента i-ой строчки. Тип *(A+i) + j – это int*, базисный тип int.

*(*(A+i) + j) – это сам j-й элемент i-ой строчки. Тип *(*(A+i) + j) – int

Таковым образом, двойная индексация A[i][j] равносильна записи
*(*(A+i) + j).

1.16 Двойной указатель – это двумерный массив

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

Пример. Разглядим двойной указатель

intn=5;

int* pi = &n;

int **ppi = &pi;

Построим схему ОЗУ для всех 3-х переменных

Рис.1.

Оператор

ppi[1][1] = 10;

синтаксически верный, но логически ошибочен. В данном случае число 10 записано в наобум избранной ячейке ОЗУ, что может приводить время от времени к фатальным ошибкам.

Оператор

ppi[0][1] = 20;

также синтаксически верный, но логически ошибочен.

Пример. Разглядим массив строк

char *Arr[] = {“Hello”, “ ”, “World!”};

В согласовании с ценностью операций тип Arr – это char*[], другими словами массив типов char*, иными словами массив строк. Но строчка – это одномерный массив частей типа char, другими словами тип строчки – это char*. Потому тип Arr – это также и char**. Таковым образом, мы проявили, что Arr – двойной указатель.

1.17 Просмотр указателей в отладчике

Для указателя ptri, определенного, как

int i = 4, *ptri;

ptri = &i;

окно просмотра в отладчике по Alt-F4 имеет вид

Таблица.1.

8FAC:FFF2
п
Ds:FFF4

[0]
4(0x0004)

int *

тут указаны:

· адресок самой переменной ptri, равный 8FAC:FFF2;

·

· также содержимое ячейки по адресу Ds:FFF4, т.е.

Для того, чтоб выяснить содержимое ячеек, окружающих переменную i, необходимо пользоваться композицией кнопок Alt-I, ввести исходный индекс (Starting index) и число ячеек (Count). Если, к примеру, введены числа -5 и 15, то можно в приведенном выше окне можно просмотреть элементы массива
ptri[-5], ptri[-4],… ,ptri[10].

1.18 Контрольные вопросцы

1.
Как найти размер одномерного массива?

2.
Какие размеры можно опустить у массива при его инициализации?

3.
Нарисуйте схему ОЗУ при выходе за спектр массива intA[3][4] в случае логической ошибке A[3][4] = 0;

4.
Обусловьте смещение в б элемента A[i][j] относительно начала массива floatA[4][5].

5.
Напишите программку, в какой находится сумма частей первой и крайней строчки и столбца матрицы A[m][n].

6.
Объявлены переменные

char c;

int *pi;

float **ppf;

Укажите типы и базисные типы выражений, если они есть

&c, *(&c), &pi[0], &(p+10), &&ppf, ppf[10], (*ppf)[3]

7.
Имеется указатель

int n=5, m=20;

int *const pi = &n;

Какие операторы синтаксически неверны

*pi = 10;

pi = &m;

*pi++;

(*pi)++;

2. Лабораторные задания

2.1 Скалярное произведение

Напишите функцию, которая находит скалярное произведение 2-ух векторов.

2.2 Минимакс

Две функции находят построчный минимакс и построчный максимин частей прямоугольной матрицы целых чисел. Под построчным минимаксом понимается малое из наибольших частей во всех строчках матрицы.

2.3 Массивы строк

Напишите функцию, которая соединяет воединыжды массив строк в одну строчку, также тест данной функции.

2.4 Трехмерный массив

Найдите сумму частей трехмерного массива с внедрением лишь указательной математики.

3. Доп задания

1. Написать функцию, которая добавляет строчку к массиву строк.

2. Написать функцию, которая конвертирует в текст валютную сумму.

Библиографический перечень

1. Керниган Б. язык программирования Си / Б. Керниган, Д. Ритчи. СПб.: Невский диалект, 2001. 352 с.

2. Подбельский В.В. Программирование на языке Си / В.В. Подбельский, С.С. Фомин. М.: деньги и статистика, 2004. 600 с.

3. Программирование в Си. Организация ввода–вывода: способ указания / сост. С.П. Трофимов. Екатеринбург: УГТУ, 1998. 14 с.

4. Программирование в Си. Динамическое распределение памяти: способ. указания / сост. С.П. Трофимов. Екатеринбург: УГТУ, 1998. 13 с.

]]>