Александр Борисович Крупник - Изучаем Си
Название: | Изучаем Си | |
Автор: | Александр Борисович Крупник | |
Жанр: | C, C++, C# | |
Изадано в серии: | неизвестно | |
Издательство: | Питер | |
Год издания: | 2001 | |
ISBN: | неизвестно | |
Отзывы: | Комментировать | |
Рейтинг: | ||
Поделись книгой с друзьями! Помощь сайту: донат на оплату сервера |
Краткое содержание книги "Изучаем Си"
Аннотация к этой книге отсутствует.
Читаем онлайн "Изучаем Си". [Страница - 12]
компилятор преобразует его по
правилам работы с указателями: *(arr + i). То есть, arr
— это как бы указатель, i — целочисленная переменная.
Сумма arr+i указывает на i-й элемент массива, а оператор
раскрытия ссылки * дает значение самого элемента. На
рис. 5.1 показан массив х, в котором хранятся три целых числа
— 13, 28 и 5, и некоторые комбинации имен элементов,
указателей и связанных с ними операторов & и *.
98
Рис. 5.1. Действия с именем массива и его элементами
Самая важная строчка на рисунке — предпоследняя. Она
показывает, что адрес нулевого элемента массива &(x[0])
равен имени массива x.
Преобразование x[0] в *x, показанное на рис. 5.1, может
навести на мысль, что имя массива — и есть указатель на его
нулевой элемент. Но это не так. Отличие имени массива от
указателя в том, что имя не может быть изменено. Пусть
имеется массив x[i]. Как мы теперь знаем, его нулевой
элемент равен *x, первый *(x+1) и т.д. Но если бы x был
настоящим указателем, его значение можно было бы
99
увеличить на единицу x=x+1 и тогда x указывал бы на первый
элемент массива. Ничего подобного с именем массива сделать
нельзя. Оно всегда указывает в одно и то же место — на свой
нулевой элемент.
Различие между указателями и массивами легче понять, если
сравнить два объявления строки, с которыми мы встретились в
главе 3 (разделы «Строки и символы» и «Указатели» главы 3):
сhar a[]="МАМА";
сhar *p="МАМА"; ,
Первая строка объявляет массив a[], в котором 5 элементов:
четыре буквы и завершающий символ '\0'. Адрес нулевого
элемента определяется во время компиляции, и в программе
нет такой переменной, где бы он хранился. Когда компилятор
видит в строке printf("%s",a) имя массива a, то
подставляет вместо него адрес начала строки.
Во втором случае адрес начала строки хранится в переменной p
(указателе). Эту строку можно вывести на экран так же, как и
первую, передав функции printf() значение указателя,
хранящееся в переменной p:
printf("%s",p).
Но в указатель p, как и в любую переменную, можно записать
новое значение и тогда p будет указывать на что-то другое.
Указатели и массивы
Если значение i-го элемента массива x[i] равно *(x + i),
то логично предположить, что всякий фрагмент программы
*(p + i), где p — указатель, а i — целочисленная
переменная, можно записать как p[i]. И это, к чести языка
Си, действительно так, потому что компилятор, встретив в
программе запись p[i] преобразует ее к виду *(p+i)
независимо от того, что такое p — массив или указатель.
Значит, в функции maxi(), (см. листинг 5.1) можно указатель
a в выражении *(a+i) заменить на a[i] и переписать всю
функцию следующим образом:
100
int maxi(int *a,int n){
int i,max;
max=a[0];
for(i=1;i max)
max=a[i];
return max;
}
Писать *(a+i) или же a[i]— дело вкуса, хотя те, кто
программировал на Бейсике, предпочтут, скорее всего, второй
способ.
Завершим этот раздел еще одним вариантом программы,
суммирующей все элементы массива (см. листинг 5.2). От
предыдущей версии (см. листинг 4.3) ее отличает прежде всего
строка int *p=dig, передающая адрес нулевого элемента
массива «настоящему» указателю p. Этот указатель p (в
отличие от имени массива) можно менять так, чтобы он
указывал последовательно на все элементы массива.
Листинг 5.2
#include
main(){
int dig[10]={5,3,2,4,6,7,11,17,0,13};
int sum=0,i;
int *p=dig;
for(i=0;inext ó NULL.
Функция display(), быть может, лучше всего дает
почувствовать красоту и лаконичность связанных списков.
Начав с переменной root, которая указывает на первую
запись в списке, display() высвечивает поле hero, затем
получает указатель на новую запись и так до тех пор, пока
указатель не станет нулевым. NULL, как мы уже говорили,
действительно равен нулю. Поэтому цикл do{} while()
перестанет выполняться и вывод на экран прекратится.
Задача 8.7. Можете ли вы сказать, не компилируя и не
запуская программу, показанную в листинге 8.15, что она
выведет на экран?
Задача 8.8. В программе из листинга 8.15 записи,
составляющие связанный список, создаются функцией
malloc(). Как освободить память, занимаемую связанным
списком? Имейте в виду, что если сначала освободить память,
занимаемую первой записью free(root), то освободить
следующую запись free(root->next)уже нельзя, потому
1
Присваивания a=b=c выполняются в Си справа налево, то
есть сначала значение c присваивается переменной b, а затем
значение b присваивается переменной a.
190
что Си не гарантирует, что указатель root->next --">
правилам работы с указателями: *(arr + i). То есть, arr
— это как бы указатель, i — целочисленная переменная.
Сумма arr+i указывает на i-й элемент массива, а оператор
раскрытия ссылки * дает значение самого элемента. На
рис. 5.1 показан массив х, в котором хранятся три целых числа
— 13, 28 и 5, и некоторые комбинации имен элементов,
указателей и связанных с ними операторов & и *.
98
Рис. 5.1. Действия с именем массива и его элементами
Самая важная строчка на рисунке — предпоследняя. Она
показывает, что адрес нулевого элемента массива &(x[0])
равен имени массива x.
Преобразование x[0] в *x, показанное на рис. 5.1, может
навести на мысль, что имя массива — и есть указатель на его
нулевой элемент. Но это не так. Отличие имени массива от
указателя в том, что имя не может быть изменено. Пусть
имеется массив x[i]. Как мы теперь знаем, его нулевой
элемент равен *x, первый *(x+1) и т.д. Но если бы x был
настоящим указателем, его значение можно было бы
99
увеличить на единицу x=x+1 и тогда x указывал бы на первый
элемент массива. Ничего подобного с именем массива сделать
нельзя. Оно всегда указывает в одно и то же место — на свой
нулевой элемент.
Различие между указателями и массивами легче понять, если
сравнить два объявления строки, с которыми мы встретились в
главе 3 (разделы «Строки и символы» и «Указатели» главы 3):
сhar a[]="МАМА";
сhar *p="МАМА"; ,
Первая строка объявляет массив a[], в котором 5 элементов:
четыре буквы и завершающий символ '\0'. Адрес нулевого
элемента определяется во время компиляции, и в программе
нет такой переменной, где бы он хранился. Когда компилятор
видит в строке printf("%s",a) имя массива a, то
подставляет вместо него адрес начала строки.
Во втором случае адрес начала строки хранится в переменной p
(указателе). Эту строку можно вывести на экран так же, как и
первую, передав функции printf() значение указателя,
хранящееся в переменной p:
printf("%s",p).
Но в указатель p, как и в любую переменную, можно записать
новое значение и тогда p будет указывать на что-то другое.
Указатели и массивы
Если значение i-го элемента массива x[i] равно *(x + i),
то логично предположить, что всякий фрагмент программы
*(p + i), где p — указатель, а i — целочисленная
переменная, можно записать как p[i]. И это, к чести языка
Си, действительно так, потому что компилятор, встретив в
программе запись p[i] преобразует ее к виду *(p+i)
независимо от того, что такое p — массив или указатель.
Значит, в функции maxi(), (см. листинг 5.1) можно указатель
a в выражении *(a+i) заменить на a[i] и переписать всю
функцию следующим образом:
100
int maxi(int *a,int n){
int i,max;
max=a[0];
for(i=1;i max)
max=a[i];
return max;
}
Писать *(a+i) или же a[i]— дело вкуса, хотя те, кто
программировал на Бейсике, предпочтут, скорее всего, второй
способ.
Завершим этот раздел еще одним вариантом программы,
суммирующей все элементы массива (см. листинг 5.2). От
предыдущей версии (см. листинг 4.3) ее отличает прежде всего
строка int *p=dig, передающая адрес нулевого элемента
массива «настоящему» указателю p. Этот указатель p (в
отличие от имени массива) можно менять так, чтобы он
указывал последовательно на все элементы массива.
Листинг 5.2
#include
main(){
int dig[10]={5,3,2,4,6,7,11,17,0,13};
int sum=0,i;
int *p=dig;
for(i=0;inext ó NULL.
Функция display(), быть может, лучше всего дает
почувствовать красоту и лаконичность связанных списков.
Начав с переменной root, которая указывает на первую
запись в списке, display() высвечивает поле hero, затем
получает указатель на новую запись и так до тех пор, пока
указатель не станет нулевым. NULL, как мы уже говорили,
действительно равен нулю. Поэтому цикл do{} while()
перестанет выполняться и вывод на экран прекратится.
Задача 8.7. Можете ли вы сказать, не компилируя и не
запуская программу, показанную в листинге 8.15, что она
выведет на экран?
Задача 8.8. В программе из листинга 8.15 записи,
составляющие связанный список, создаются функцией
malloc(). Как освободить память, занимаемую связанным
списком? Имейте в виду, что если сначала освободить память,
занимаемую первой записью free(root), то освободить
следующую запись free(root->next)уже нельзя, потому
1
Присваивания a=b=c выполняются в Си справа налево, то
есть сначала значение c присваивается переменной b, а затем
значение b присваивается переменной a.
190
что Си не гарантирует, что указатель root->next --">
Книги схожие с «Изучаем Си» по жанру, серии, автору или названию:
Дженнифер Грин, Эндрю Стиллмен - Изучаем C# Жанр: C, C++, C# Год издания: 2014 Серия: Head First O'Reely |
Ферроне Харрисон - Изучаем C# через разработку игр на Unity Жанр: Программирование игр Год издания: 2022 Серия: Библиотека программиста |
Дженнифер Грин, Эндрю Стиллмен - Изучаем C# Жанр: C, C++, C# Год издания: 2022 Серия: Head First O’Reilly |
Александр Борисович Крупник - Изучаем Ассемблер Жанр: Школьные учебники и пособия Год издания: 2005 Серия: КомпАс (Школьный клуб Компьютерный ас) |