Студопедия

Главная страница Случайная страница

Разделы сайта

АвтомобилиАстрономияБиологияГеографияДом и садДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеталлургияМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРелигияРиторикаСоциологияСпортСтроительствоТехнологияТуризмФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника






  • Указатели и массивы






     

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

    Объявление

    int a[10];

    Определяет массив a размера 10, т. е. блок из 10 последовательных объектов с именами a[0], a[1], …, a[9].

     

    Запись a[i] отсылает нас к i -му элементу массива. Если pa есть указатель на int, т. е. объявлен как

    int *pa;

    то в результате присваивания

    pa =& a[0];

    pa будет указывать на нулевой элемент a, иначе говоря, pa будет содержать адрес элемента a[0].

    Теперь присваивание

    x = *pa;

    будет копировать содержимое a[0] в x.

    Если pa указывает на некоторый элемент массива, то pa+1 по определению указывает на следующий элемент, pa+i - на i -й элемент после pa, a pa-i - на i -й элемент перед pa. Таким образом, если pa указывает на a[0], то

    *(pa+1)

    есть содержимое a[1], a+i - адрес a[i], a *(pa+i) - содержимое a[i].

     

    Сделанные замечания верны безотносительно к типу и размеру элементов массива a. Смысл слов " добавить 1 к указателю", как и смысл любой арифметики с указателями, состоит в том, чтобы pa+1 указывал на следующий объект, a pa+i - на i -й после pa.

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

    pa =& a[0];

    ра и a имеют одно и то же значение. Поскольку имя массива является синонимом расположения его начального элемента, присваивание pa=& a[0] можно также записать в следующем виде:

    pa = a;

    Еще более удивительно (по крайней мере на первый взгляд) то, что a[i] можно записать как *(a+i). Вычисляя a[i], Си сразу преобразует его в *(a+i); указанные две формы записи эквивалентны. Из этого следует, что полученные в результате применения оператора & записи & a[i] и a+i также будут эквивалентными, т. е. и в том и в другом случае это адрес i -го элемента после a. С другой стороны, если pa - указатель, то его можно использовать с индексом, т. е. запись pa[i] эквивалентна записи *(pa+i). Короче говоря, элемент массива можно изображать как в виде указателя со смещением, так и в виде имени массива с индексом.

    Между именем массива и указателем, выступающим в роли имени массива, существует одно различие. Указатель - это переменная, поэтому можно написать pa=a или pa++. Но имя массива не является переменной, и записи вроде a=pa или a++ не допускаются.

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

    /* strlen: возвращает длину строки */

    int strlen(char *s)

    {

    int n;

    for (n = 0; *s! = '\0'; s++)

    n++;

    return n;

    }

    Так как переменная s - указатель, к ней применима операция ++; s++ не оказывает никакого влияния на строку символов функции, которая обратилась к strlen. Просто увеличивается на 1 некоторая копия указателя, находящаяся в личном пользовании функции strlen. Это значит, что все вызовы, такие как:

    strlen(" 3дравствуй, мир"); /* строковая константа */

    strlen(array); /* char array[100]; */

    strlen(ptr); /* char *ptr; */

    правомерны.

    Формальные параметры

    char s[];

    и

    char *s;

    в определении функции эквивалентны. Мы отдаем предпочтение последнему варианту, поскольку он более явно сообщает, что s есть указатель. Если функции в качестве аргумента передается имя массива, то она может рассматривать его так, как ей удобно - либо как имя массива, либо как указатель, и поступать с ним соответственно. Она может даже использовать оба вида записи, если это покажется уместным и понятным.

    Функции можно передать часть массива, для этого аргумент должен указывать на начало подмассива. Например, если a - массив, то в записях

    f(& a[2])

    или

    f(a+2)

    функции f передается адрес подмассива, начинающегося с элемента a[2]. Внутри функции f описание параметров может выглядеть как

    f(int arr[]) {…}

    или

    f(int *arr) {…}

    Следовательно, для f тот факт, что параметр указывает на часть массива, а не на весь массив, не имеет значения.

    Если есть уверенность, что элементы массива существуют, то возможно индексирование и в " обратную" сторону по отношению к нулевому элементу; выражения p[-1], p[-2] и т.д. не противоречат синтаксису языка и обращаются к элементам, стоящим непосредственно перед p[0]. Разумеется, нельзя " выходить" за границы массива и тем самым обращаться к несуществующим объектам.

     






    © 2023 :: MyLektsii.ru :: Мои Лекции
    Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав.
    Копирование текстов разрешено только с указанием индексируемой ссылки на источник.