Вівторок, 19.03.2024, 14:49
Гость

Мішатронік

Мобільна версія | Додати у вибране  | Мій профіль | Вихід | RSS |
Меню сайту
Наше опитування
Чи знаєте вы Java
Всього відповідей: 3
Статистика

Онлайн всього: 1
Гостей: 1
Користувачів: 0


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

Строки - это массивы знаков. По соглашению, последним знаком строки должен быть нулевой знак \0. Поскольку имя массивафактически является указателем на первый элемент массива, переменные типа string могут также рассматриваться, как имеющие тип char *. Например, вторая переменная string_array в определении

 
char *string_pointer, string_array[81];
 

может рассматриваться также как знаковый указатель. Для строки, представленной первой переменной string_pointerпамятьдолжна быть выделена явно. С другой стороны, для массива string_array память является указателем на нее. Заметим, чтопамять должна быть также выделена или зарезервирована для признака конца строки \0.

 
! Нет ничего необычного не только в интерпретации переменных типа string, т.е. массивов знаков как указателей, но и в интерпретации строк, которые также могут рассматриваться двояко - как массивы и как указатели - и все в одной программе! Это особенно важно, когда строки передаются как аргументы функции. Вызывающая программа может рассматривать строку какмассив знаков, а вызываемая функция может рассматривать ее как знаковый указатель. Если длина строки непостоянна, то использование знаковых указателей для строк имеет определенные преимущества. Хотя строки переменной длины могут быть также реализованы с использованием массивов, такая реализация оказывается слишком неэкономной с точки зрения использования памяти и налагает ограничения на максимальную длину строки. Например, для размещения строк разной длины может быть создан массив знаковых указателей. Альтернативное решение с использованием двумерного массива знаков в общем случае будет использовать память неэффективно, так как в этом случае потребовалось бы сделать число столбцов равным числу знаков в строке наибольшей возможной длины.
 

Инициализация массивов и классы памяти

Мы знаем, что скалярные переменные можно инициализировать в описании типа при помощи таких выражений, как например:

 
int fix = 1;
float flax = PI*2;
 

при этом предполагается, что PI - ранее введенное макроопределение. Можно ли инициализировать массивы?

 
Внешние, статические и автоматические массивы можно инициализировать!
Регистровые массивы инициализировать нельзя!
 

Если ничего не засылать в массив перед началом работы с ним, то внешние, статические и автоматические массивы не инициализируются для числовых типов нулем и '\0' (null) для символьных типов, а регистровые массивы содержат какой-то мусор, оставшийся в этой части памяти. Если в статическом, внешнем или автоматическом массиве нам нужны первоначальные значения, отличные от нуля, в этом случае мы можем делать так:

 
/* дни месяца */
int days[12]={31,28,31,30,31,30,31,31,30,31,30,31};
main( )
{
 int index;
 extern int days[];/*необязательное описание */
 for(index = 0; index<12; index++)
 printf("Месяц %d имеет %d дней.\n", index+1, 
 days[index]);
}
 

Результат:

 
Месяц 1 имеет 31 дней.
Месяц 2 имеет 28 дней.
Месяц 3 имеет 31 дней.
Месяц 4 имеет 30 дней.
Месяц 5 имеет 31 дней.
Месяц 6 имеет 30 дней.
Месяц 7 имеет 31 дней.
Месяц 8 имеет 31 дней.
Месяц 9 имеет 30 дней.
Месяц 10 имеет 31 дней.
Месяц 11 имеет 30 дней.
Месяц 12 имеет 31 дней.
 

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

 

Предыдущую программу лучше переписать так:

 
int days[ ] = {31,28,31,30,31,30,31,31,30,31,30,31};
main( )
{
 int index;
 extern int days[ ];/* необязательное описание */
 for(index=0;index<sizeof(days)/(sizeof(int));
 index++)
 printf("Месяц %d имеет %d дней.\n",index +1, 
 days[index]);
}
 

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

 

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

 

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

 

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

Функции, массивы и указатели

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

 
/* массив-аргумент */
main( )
{
 int ages[50]; /* массив из 50 элементов */
 convert(ages);
 _
}
convert(years);
int years[ ];/* каков размер массива? */
{
 _
}
 

Очевидно, что массив ages состоит из 50 элементов. А что можно сказать о массиве years? Оказывается в программе нет такогомассива. Описатель

 
int years[ ];
 

создает не массив, а указатель на него! Посмотрим, почему это так. Вот вызов нашей функции:

 
convert(ages);
 

ages - аргумент функции convert. Имя ages является указателем на первый элемент массива, состоящего из 50 элементов. Таким образом, оператор вызова функции передает ей указатель, т. е. адрес функции convert( ). Это значит, что аргумент функцииявляется указателем, и мы можем написать функцию convert( ) следующим образом:

 
convert(years);
int *years;
{
 _
}
 

Действительно, операторы

 
int years[ ];
int *years;
 

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

 

Как теперь связать его с массивом ages? При использовании указателя в качестве аргумента, функция взаимодействует с соответствующей переменной в вызывающей программе, т.е. операторы, использующие указатель years в функции convert( ), фактически работают с массивом ages, находящимся в теле функции main( ). Короче говоря, когда имя массива применяется в качестве аргумента, функции передается указатель. Затем функция использует этот указатель для выполнения изменений в исходноммассиве, принадлежащем программе, вызывающей функцию.

 

Операции с указателями

  1. Присваивание. Указателю можно присвоить адрес. Обычно мы выполняем это действие, используя имя массива или операцию получения адреса &.
  2. Определение значения. Операция * выдает значение, хранящееся в указанной ячейке.
  3. Получение адреса указателя. Подобно любым переменным, переменная типа указатель имеет адрес и значение. Операция &сообщает нам, где находится сам указатель.
  4. Увеличение указателя. Мы можем выполнять это действие с помощью обычной операции сложения либо с помощью операции увеличения. Увеличивая указатель, мы перемещаем его на следующий элемент массива.
  5. Разность. Можно находить разность двух указателей. Обычно это делается для указателей, ссылающихся на элементы одного и того же массива, чтобы определить, на каком расстоянии друг от друга находятся элементы. Помните, что результат имеет тот же тип, что и переменная, содержащая размер массива!

 

Форма входа
Пошук
Друзі сайту
Календар
«  Березень 2024  »
ПнВтСрЧтПтСбНд
    123
45678910
11121314151617
18192021222324
25262728293031

Єдина Країна! Единая Страна!