Многомерные массивы и указатели на многомерные массивы 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Многомерные массивы и указатели на многомерные массивы



 

Теперь рассмотрим такой пример

 

void main() {

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

int **p = A;

}

 

Этот код либо не скомпилируется либо выдаст множество предупеждений. Дело в том, что правило подмены массива на указатель не рекурсивное. Поэтому при определении многомерного массива нужно указывать размер явно, а пустыми оставлять можно только первые скобки. Этот пример можно переписать так

 

#include <conio.h>

#include <stdio.h>

 

void main() {

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

int *p = A[0];

 

printf("%d\n", p[2]);

 

getch();

}

 

Мы получили указатель на первую строку. Далее вывели третий элемент. Либо так - создать массив указателей на строки.

 

#include <conio.h>

#include <stdio.h>

 

void main() {

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

int (*p)[3] = A;

 

printf("%d\n", p[0][2]);

//Или так

printf("%d\n", *(*(p + 0) + 2));

 

getch();

}

 

Только здесь уже p будет именем массива, каждый элемент которого является указателем. И точно так же, как мы обращались к элементам массива через массив указателей *p[3], через имя массива можно обратиться к элементу массива

 

printf("%d\n", *(*(A + 1) + 2));

 

Тоже самое правило действует и при вызове функций. Если функция требует указателя на указатель, то нельзя просто передать двумерный массив, потому что он не будет подменён указателем на указатель, а будет заменён массивом указателей.

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

Структура динамически созданного и статического одномерного массива идентичны

 

Другое дело - двумерный массив.

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

 

Двумерный статический и двумерный динамический массив имеют разную структуру

 

Чтобы динамически созданный двумерный массив имел структуру статического двумерного массива, необходимо, чтобы он знал "число столбцов" двумерного массива, то есть длину одной строки. Для этого можно воспользоваться указателем на одномерный массив. Неудобство такого подхода в том, что необходимо заранее знать число элементов каждого подмассива. Однако, многомерный массив всегда можно создать из одномерного, тогда вообще никаких проблем не обнаружится.

 

Указатель на одномерные массивы будет вести себя как статический двумерный массив

 

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

Перед тем как программа будет скомпилирована (или не будет, если найдены ошибки), текст программы обрабатывается препроцессором. Препроцессор позволяет изменять текст программы, используя специальные директивы.

Директива #define определяет новый макрос. Макрос, или макроподстановка, будет заменена в коде программы своим телом. Например, мы часто пользовались макросом

 

#define SIZE 20

 

и после этого использовали SIZE вместо размера массива.

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

 

#define BREAK_WORD "end"

 

Макрос подставляется непосредственно в текст вашей программы. То есть, если у вас был код

 

#include <conio.h>

#include <stdio.h>

 

#define LENGTH 128

 

void main() {

char buffer[LENGTH];

 

scanf("%127s", buffer);

printf("%s", buffer);

 

getch();

}

 

то он будет заменён на код

 

#include <conio.h>

#include <stdio.h>

 

void main() {

char buffer[128];

 

scanf("%127s", buffer);

printf("%s", buffer);

 

getch();

}

 

Иными словами, макроподстановка - это просто подмена одного куска текста на другой.

Макросы могут иметь аргументы.

 

#define MAX (a, b) a > b? a: b

 

Например

 

#include <conio.h>

#include <stdio.h>

 

#define MAX(x, y) x > y? x: y

 

void main() {

int a, b;

scanf("%d", &a);

scanf("%d", &b);

printf("max number is %d", MAX(a, b));

 

getch();

}

 

Несмотря на то, что этот код работает, в нём есть ошибки. Макроподстановка – это именно подстановка:

 

#include <conio.h>

#include <stdio.h>

 

#define MAX(x, y) x > y? x: y

 

void main() {

int a = 10;

int b = 10;

printf("max number is %d\n", MAX(a++, b++));

printf("a = %d\n", a);

printf("b = %d\n", b);

getch();

}

 

Будет выведено

 

max number is 11

a = 11

b = 12

 

Это связано с тем, что код будет подменён следующим образом

 

("max number is %d\n", a++ > b++? a++: b++);

 

В данном случае возвращаемое значение будет ещё раз инкрементировано.

Теперь рассмотрим макрос.

 

#include <conio.h>

#include <stdio.h>

 

#define SPHERE_VOLUME(r) 4.18879020 * (r) * (r) * (r)

 

void main() {

float halfA = 10.f, halfB = 20.f;

printf("Volume of sphere is %.3f", SPHERE_VOLUME(halfA + halfB));

getch();

}

 

С одной стороны, этот макрос должен делать программу быстрее, если заменить им вызов функции. Но на деле работать он будет медленнее. Макрос развернётся в следующий код

 

4,18879020 * (halfA + halfB) * (halfA + halfB) * (halfA + halfB)

 

итого, три раза будет вызвано сложение. Вот ещё пример ошибки

 

#include <conio.h>

#include <stdio.h>

 

#define MUL(x, y) x * y

 

void main() {

printf("(2 + 3) * (4 + 5) %d\n", MUL(2 + 3, 4 + 5));

getch();

}

 

В данном случае будет выведено 19 вместо 45, так как макрос будет раскрыт в выражение

 

2 + 3 * 4 + 5 == 2 + 12 + 5 == 19

 

Решением будет следующий макрос:

 

#include <conio.h>

#include <stdio.h>

 

#define MUL(x, y) (x) * (y)

 

void main() {

printf("(2 + 3) * (4 + 5) %d\n", MUL(2 + 3, 4 + 5));

getch();

}

 

И ещё одна ошибка, которая также встречается очень часто. Давайте напишем макрос, который будет выводить на печать массив. Мы воспользуемся им в сортировке пузырьком, чтобы видеть, как изменяется массив во время сортировки.

 

#include <conio.h>

#include <stdio.h>

 

#define DISPLAY_ARRAY(arr, size) \

for (i = 0; i < size; i++) {\

printf("%d ", arr[i]);\

}\

printf("\n");

 

#define SIZE 10

 

void main() {

int a[SIZE] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};

int tmp;

char flag, i;

 

do {

flag = 0;

//Проходим по массиву. Если следующий элемент больше предыдущего, то

//меняем их местами и по новой проверяем массив

for (i = 1; i < SIZE; i++) {

if (a[i] > a[i - 1]) {

tmp = a[i];

a[i] = a[i - 1];

a[i - 1] = tmp;

flag = 1;

}

DISPLAY_ARRAY(a, SIZE);

}

 

} while(flag);

 

getch();

}

 

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

 

#include <conio.h>

#include <stdio.h>

 

#define DISPLAY_ARRAY(arr, size) {\

int i;\

for (i = 0; i < size; i++) {\

printf("%d ", arr[i]);\

}\

printf("\n");\

}

 

#define SIZE 10

 

void main() {

int a[SIZE] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};

int tmp;

char flag, i;

 

do {

flag = 0;

//Проходим по массиву. Если следующий элемент больше предыдущего, то

//меняем их местами и по новой проверяем массив

for (i = 1; i < SIZE; i++) {

if (a[i] > a[i - 1]) {

tmp = a[i];

a[i] = a[i - 1];

a[i - 1] = tmp;

flag = 1;

}

DISPLAY_ARRAY(a, SIZE);

}

 

} while(flag);

 

getch();

}

 

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

Поэтому, при работе с параметризованными макросами

1. Всегда окружайте параметры круглыми скобками

2. Старайтесь передавать параметры явно и не передавать выражения, которые должны быть вычислены. Это будет приводить к неявным побочным эффектам и замедлению работы за счёт повторного выполнения кода.

3. Тело сложного макроса заносите под фигурные скобки.


Тема №9



Поделиться:


Последнее изменение этой страницы: 2017-01-20; просмотров: 391; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 18.226.96.61 (0.027 с.)