Перейти к содержимому

Как приравнять массивы c

  • автор:

Как приравнять массивы c

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

Имя массива без индексов в Си является адресом его первого элемента. Соответственно через операцию разыменования мы можем получить значение по этому адресу:

#include int main(void) < int array[] = ; printf("array[0] = %d", *array); // array[0] = 1 return 0; >

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

#include int main(void) < int array[] = ; int second = *(array + 1); // получим второй элемент printf("array[1] = %d", second); // array[1] = 2 return 0; >

Более того, когда мы в обращаемся к определенному элементу массива, используя квадратные скобки, например:

array[2]

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

array+2

Поэтому мы даже можем написать 2[array] , что также будет валидным обращением к элементу массива:

#include int main(void) < int array[] = ; int third = 2[array]; printf("array[2] = %d", third); // array[2] = 3 return 0; >

Соответственно мы можем пробежаться по всем элементом массива, прибавляя к адресу определенное число:

#include int main(void) < int array[5] = ; for(int i = 0; i < 5; i++) < void* address = array + i; // получаем адрес i-го элемента массива int value = *(array + i); // получаем значение i-го элемента массива printf("array[%d]: address=%p \t value=%d \n", i, address, value); >return 0; >

То есть, например, адрес второго элемента будет представлять выражение a+1 , а его значение — *(a+1) .

Со сложением и вычитанием здесь действуют те же правила, что и в операциях с указателями. Добавление единицы означает прибавление к адресу значения, которое равно размеру типа массива. Так, в данном случае массив представляет тип int , размер которого, как правило, составляет 4 байта, поэтому прибавление единицы к адресу означает увеличение адреса на 4. Прибавляя к адресу 2, мы увеличиваем значение адреса на 4 * 2 =8. И так далее.

В итоге в моем случае я получу следующий результат работы программы:

array[0]: address=0060FE98 value=1 array[1]: address=0060FE9C value=2 array[2]: address=0060FEA0 value=3 array[3]: address=0060FEA4 value=4 array[4]: address=0060FEA8 value=5

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

int array[5] = ; array++; // так сделать нельзя int b = 8; array = &b; // так тоже сделать нельзя

Использование указателя для работы с массивом

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

#include int main(void) < int array[5] = ; int *ptr = array; // указатель ptr хранит адрес первого элемента массива array printf("value: %d \n", *ptr); // 1 return 0; >

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

#include int main(void) < int array[5] = ; int *ptr = array; // указатель ptr хранит адрес первого элемента массива array ptr = ptr + 2; // перемезаем указатель на 2 элемента вперед printf("value: %d \n", *ptr); // value: 3 return 0; >

Здесь указатель ptr изначально указывает на первый элемент массива. Увеличив указатель на 2, мы пропустим 2 элемента в массиве и перейдем к элементу array[2] .

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

#include int main(void) < int array[5] = ; int *ptr = array; // указатель ptr хранит адрес первого элемента массива array ptr = ptr + 2; // переходим к третьему элементу *ptr = 8; // меняем значение элемента, на который указывает указатель printf("array[2]: %d \n", array[2]); // array[2] : 8 return 0; >

Стоит отметить, что указатель также может использовать индексы, как и массивы:

#include int main(void) < int array[5] = ; int *ptr = array; // указатель ptr хранит адрес первого элемента массива array int value = ptr[2]; // используем индексы - получаем 3-й элемент (элемент с индексом 2) printf("value: %d \n", value); // value: 3 return 0; >

Строки и указатели

Ранее мы рассмотрели, что строка по сути является набором символов, окончанием которого служит нулевой символ ‘\0’. И фактически строку можно представить в виде массива:

char hello[] = "Hello METANIT.COM!";

Но в языке Си также для представления строк можно использовать указатели на тип char :

#include int main(void) < char *hello = "Hello METANIT.COM!"; // указатель на char - фактически строка printf("%s", hello); return 0; >

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

Перебор массива с помощью указателей

С помощью указателей легко перебрать массив:

int array[5] = ; for(int *ptr=array; ptr

Так как указатель хранит адрес, то мы можем продолжать цикл, пока адрес в указателе не станет равным адресу последнего элемента ( ptr

Аналогичным образом можно перебрать и многомерный массив:

#include int main(void) < int array[3][4] = < , , >; int n = sizeof(array)/sizeof(array[0]); // число строк int m = sizeof(array[0])/sizeof(array[0][0]); // число столбцов int *final = array[0] + n * m - 1; // указатель на самый последний элемент for(int *ptr=array[0], i = 1; ptr > return 0; >

Так как в данном случае мы имеем дело с двухмерным массивом, то адресом первого элемента будет выражение array[0] . Соответственно указатель указывает на этот элемент. С каждой итерацией указатель увеличивается на единицу, пока его значение не станет равным адресу последнего элемента, который хранится в указателе final.

Мы также могли бы обойтись и без указателя на последний элемент, проверяя значение счетчика, пока оно не станет равно общему количеству элементов (m * n):

for(int *ptr = array[0], i = 0; i < m*n;) < printf("%d \t", *ptr++); if(++i%m==0) < printf("\n"); >>

Но в любом случае программа вывела бы следующий результат:

1 2 3 4 5 6 7 8 9 10 11 12

String interning

Стоит отметить, что в языке Си для работы со строками применяется такой механизм как string interning или интернирование строк . В этом случае строки в виде строковых литералов сохраняются в приложении в секции .rodata (read-only data), которые предназначены для данных только для чтения, а строковые литералы рассматриваются как неизменяемые данные. Например, возьмем следующую программу:

#include char* str1 = «Hello»; char* str2 = «World»; int main(void)

Здесь определены две строки — str1 и str2, в функции main выводим адрес первого символа каждой из этих строк. Так, в моем случае я получу вывод:

str1 = 00007FF75E674000 str2 = 00007FF75E674006

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

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

#include char* str1 = «Hello World»; char* str2 = «Hello World»; int main(void)

Теперь вывод будет другим:

str1 = 00007FF75F674000 str2 = 00007FF75F674000

Оба адреса одинаковые, потому что обе переменных в реальности указывают на одну и ту же строку. Интернирование строк позволяет избежать дублирования строк, более эффективно использовать память. Причем здесь не важно, что переменные являются глобальными. Они также могут быть локальными, но все равно будут указывать на один и тот же адрес в .rodata .

#include int main(void)

Как приравнять массивы c

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

Массив указателей определяется одним из трех способов:

тип *имя_массива [размер]; тип *имя_массива [] = инициализатор; тип *имя_массива [размер] = инициализатор;

Используем все эти способы:

int array[] = ; int *p1[3]; int *p2[] = < &array[1], &array[2], &array[0] >; int *p3[3] = < &array[3], &array[1], &array[2] >;

Массив указателей p1 состоит из трех элементов, но он не инициализирован и является пустым.

Массивы p2 и p3 в качестве элементов хранят адреса на элементы массива a.

Выведем на конслоль значения, на которые ссылаются указатели:

#include int main(void) < int array[] = ; int *p[] = < &array[1], &array[2], &array[0] >; for(int i = 0; i < 3; i++) < printf("%d", *p[i]); >return 0; >

Здесь выражение *p[i] означает, что мы сначала обращаемся к i-тому адресу в массиве p, а потом применяет операцию разыменования для получения данных по этому адресу. В итоге на консоль будет выведено в строку:

Вместо *p[i] мы могли бы написать **(p+i) :

  • p+i — к адресу в указателе p прибавляем число i и таким образом перемещаемся по указателям в массиве p.
  • *(p+i) — разыменовываем i-тый указатель в массиве и в результате получаем адрес одного из элементов из массива array .
  • **(p+i) — получаем значение по полученному на предыдущем шаге адресу элемента из массива array.
#include int main(void) < int array[] = ; int *p[] = < &array[1], &array[2], &array[0] >; for(int i = 0; i < 3; i++) < printf("%d", **(p+i)); >return 0; >

Указатель и массив строк

Соответственно если указатель типа char можно представить в виде строки, то массив указателей типа char представляет собой массив строк:

#include int main(void) < char *fruit[] = ; for(int i=0; i < 5; i++) < printf("%s \n", fruit[i]); >return 0; >

Здесь массив указателей fruit хранит пять строк — фактически пять адресов, по которым размещены начальные символы каждой строки. Результат работы программы:

apricot apple banana lemon orange

Также мы могли бы написать:

#include int main(void) < char *fruit[] = ; for(int i=0; i < 5; i++) < printf("%s \n", *(fruit + i)); >return 0; >

Указатели на указатели

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

int **ptr;

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

#include int main(void) < int x = 22; int *px = &x; // указатель px хранит адрес переменной x int **ppx = &px; // указатель ppx хранит адрес указателя px printf("Address of px: %p \n", (void *)ppx); printf("Address of x: %p \n", (void *)*ppx); printf("Value of x: %d \n", **ppx); return 0; >

Здесь указатель ppx хранит адрес указателя px . Поэтому через выражение *ppx можно получить значение, которое хранится в указателе px — адрес переменной x . А через выражение **ppx можно получить значение по адресу из px , то есть значение переменной x .

двухуровневая адресация и указатель на указатель в языке программирования Си

Присвоению массиву другой массив

Голые массивы в С++ являются некопируемыми объектами. Они не копируются ни при инициализации, ни через присваивание. (На массивы, обернутые в классы и копируемые неявно и опосредованно вместе с классом, это ограничение не распространяется.)

Поэтому присвоить массив double а[n] массиву double b[n] у вас никак не получится. Либо явный цикл, либо скрытый цикл, типа memcpy , std::copy и т.п.

Разумеется настоящие «кул хацк0ры» не удержатся от соблазна предложить такое решение

int main() < const unsigned N = 100; double a[N] = < 1, 2, 3 >; double b[N]; struct S < double a[N]; >; *(S *) b = *(S *) a; > 

но с точки зрения языка С++ такой код нарушает правила aliasing и его поведение не определено.

Как сравнить 2 массива типа char

допустим, у меня есть 2 массива типа char char a1[10] и char a2[10] Соответственно, они используются у меня как строка, могу ли я их как-то сравнить? Например в а1 занесено «привет», в а2 — «привет», соответственно они равны. Можно ли сравнить целиком a1 == a2 . или только посимвольно.

Отслеживать
задан 27 апр 2017 в 13:10
187 1 1 серебряный знак 9 9 бронзовых знаков
посимвольно или strcmp
27 апр 2017 в 13:14

1 ответ 1

Сортировка: Сброс на вариант по умолчанию

В C строка — это массив из char, заканчивающийся байтом с кодом 0x00. Если у вас это соблюдается (нулевой байт в конце строки), то можно использовать функции сравнения строк, например,

int strcmp( const char * string1, const char * string2 ) 

она производит лексикографическое сравнение и возвращает: -1, если строка string1 «меньше» строки string2; 0, если строки равны, 1, если строка string1 «больше» строки string2.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *