Заглавная страница Избранные статьи Случайная статья Познавательные статьи Новые добавления Обратная связь КАТЕГОРИИ: АрхеологияБиология Генетика География Информатика История Логика Маркетинг Математика Менеджмент Механика Педагогика Религия Социология Технологии Физика Философия Финансы Химия Экология ТОП 10 на сайте Приготовление дезинфицирующих растворов различной концентрацииТехника нижней прямой подачи мяча. Франко-прусская война (причины и последствия) Организация работы процедурного кабинета Смысловое и механическое запоминание, их место и роль в усвоении знаний Коммуникативные барьеры и пути их преодоления Обработка изделий медицинского назначения многократного применения Образцы текста публицистического стиля Четыре типа изменения баланса Задачи с ответами для Всероссийской олимпиады по праву Мы поможем в написании ваших работ! ЗНАЕТЕ ЛИ ВЫ?
Влияние общества на человека
Приготовление дезинфицирующих растворов различной концентрации Практические работы по географии для 6 класса Организация работы процедурного кабинета Изменения в неживой природе осенью Уборка процедурного кабинета Сольфеджио. Все правила по сольфеджио Балочные системы. Определение реакций опор и моментов защемления |
Указатели символов и функции
Строчная константа, как, например, "I AM A STRING" является массивом символов. Компилятор завершает внутреннеепредставление такого массива символом \0, так что программымогут находить его конец. Таким образом, длина массива в па-мяти оказывается на единицу больше числа символов междудвойными кавычками. По-видимому чаще всего строчные константы появляются вкачестве аргументов функций, как, например, в PRINTF ("HELLO, WORLD\N"); когда символьная строка, подобная этой, появляется в прог-рамме, то доступ к ней осуществляется с помощью указателясимволов; функция PRINTF фактически получает указатель сим-вольного массива. Конечно, символьные массивы не обязаны быть только аргу-ментами функций. Если описать MESSAGE как CHAR *MESSAGE; то в результате оператора MESSAGE = "NOW IS THE TIME"; переменная MESSAGE станет указателем на фактический массивсимволов. Это не копирование строки; здесь участвуют толькоуказатели. в языке "C" не предусмотрены какие-либо операциидля обработки всей строки символов как целого. Мы проиллюстрируем другие аспекты указателей и массивов,разбирая две полезные функции из стандартной библиотеки вво-да-вывода, которая будет рассмотрена в главе 7. Первая функция - это STRCPY(S,T), которая копирует стро-ку т в строку S. Аргументы написаны именно в этом порядке поаналогии с операцией присваивания, когда для того, чтобыприсвоить T к S обычно пишут S = T сначала приведем версию с массивами: STRCPY(S, T) /* COPY T TO S */ CHAR S[], T[]; { INT I; I = 0; WHILE ((S[I] = T[I])!= '\0') I++; } Для сопоставления ниже дается вариант STRCPY с указате-лями. STRCPY(S, T) /* COPY T TO S; POINTER VERSION 1 */CHAR *S, *T;{ WHILE ((*S = *T)!= '\0') { S++; T++; }} Так как аргументы передаются по значению, функция STRCPYможет использовать S и T так, как она пожелает. Здесь они судобством полагаются указателями, которые передвигаютсявдоль массивов, по одному символу за шаг, пока не будет ско-пирован в S завершающий в T символ \0. На практике функция STRCPY была бы записана не так, какмы показали выше. Вот вторая возможность: STRCPY(S, T) /* COPY T TO S; POINTER VERSION 2 */CHAR *S, *T;{ WHILE ((*S++ = *T++)!= '\0');} Здесь увеличение S и T внесено в проверочную часть. Зна-чением *T++ является символ, на который указывал T до увели-чения; постфиксная операция ++ не изменяет T, пока этот сим-вол не будет извлечен. Точно так же этот символ помещается встарую позицию S, до того как S будет увеличено. Конечныйрезультат заключается в том, что все символы, включая завер-шающий \0, копируются из T в S. И как последнее сокращение мы опять отметим, что сравне-ние с \0 является излишним, так что функцию можно записать ввиде STRCPY(S, T) /* COPY T TO S; POINTER VERSION 3 */CHAR *S, *T;{ WHILE (*S++ = *T++);} хотя с первого взгляда эта запись может показаться загадоч-ной, она дает значительное удобство. Этой идиомой следуетовладеть уже хотя бы потому, что вы с ней будете часто вст-речаться в "C"-программах. Вторая функция - STRCMP(S, T), которая сравнивает сим-вольные строки S и т, возвращая отрицательное, нулевое илиположительное значение в соответствии с тем, меньше, равноили больше лексикографически S, чем T. Возвращаемое значениеполучается в результате вычитания символов из первой пози-ции, в которой S и T не совпадают. STRCMP(S, T) /* RETURN <0 IF S0 IF S>T */CHAR S[], T[];{ INT I; I = 0; WHILE (S[I] == T[I]) IF (S[I++] == '\0') RETURN(0); RETURN(S[I]-T[I]);} Вот версия STRCMP с указателями: STRCMP(S, T) /* RETURN <0 IF S0 IF S>T */CHAR *S, *T;{ FOR (; *S == *T; S++, T++) IF (*S == '\0') RETURN(0); RETURN(*S-*T);} так как ++ и -- могут быть как постфиксными, так ипрефиксными операциями, встречаются другие комбинации * и++ и --, хотя и менее часто. Например *++P увеличивает P до извлечения символа, на который указывает P, а *--P сначала уменьшает P. Упражнение 5-2 --------------- Напишите вариант с указателями функции STRCAT из главы2: STRCAT(S, T) копирует строку T в конец S. Упражнение 5-3 --------------- Напишите макрос для STRCPY. Упражнение 5-4 -------------- Перепишите подходящие программы из предыдущих глав и уп-ражнений, используя указатели вместо индексации массивов.Хорошие возможности для этого предоставляют функции GETLINE/главы 1 и 4/, ATOI, ITOA и их варианты /главы 2, 3 и 4/,REVERSE /глава 3/, INDEX и GETOP /глава 4/.
Указатели - не целые Вы, возможно, обратили внимание в предыдущих "с"-прог-раммах на довольно непринужденное отношение к копированиюуказателей. В общем это верно, что на большинстве машин ука-затель можно присвоить целому и передать его обратно, не из-менив его; при этом не происходит никакого масштабированияили преобразования и ни один бит не теряется. к сожалению,это ведет к вольному обращению с функциями, возвращающимиуказатели, которые затем просто передаются другим функциям,- необходимые описания указателей часто опускаются. Рассмот-рим, например, функцию STRSAVE(S), которая копирует строку Sв некоторое место для хранения, выделяемое посредством обра-щения к функции ALLOC, и возвращает указатель на это место.Правильно она должна быть записана так: CHAR *STRSAVE(S) /* SAVE STRING S SOMEWHERE */ CHAR *S; { CHAR *P, *ALLOC(); IF ((P = ALLOC(STRLEN(S)+1))!= NULL) STRCPY(P, S); RETURN(P); } на практике существует сильное стремление опускать описания: *STRSAVE(S) /* SAVE STRING S SOMEWHERE */ { CHAR *P; IF ((P = ALLOC(STRLEN(S)+1))!= NULL) STRCPY(P, S); RETURN(P); } Эта программа будет правильно работать на многих маши-нах, потому что по умолчанию функции и аргументы имеют типINT, а указатель и целое обычно можно безопасно пересылатьтуда и обратно. Однако такой стиль программирования в своемсуществе является рискованным, поскольку зависит от деталейреализации и архитектуры машины и может привести к непра-вильным результатам на конкретном используемом вами компиля-торе. Разумнее всюду использовать полные описания. (Отладоч-ная программа LINT предупредит о таких конструкциях, еслиони по неосторожности все же появятся).Многомерные массивы В языке "C" предусмотрены прямоугольные многомерные мас-сивы, хотя на практике существует тенденция к их значительноболее редкому использованию по сравнению с массивами указа-телей. В этом разделе мы рассмотрим некоторые их свойства. Рассмотрим задачу преобразования дня месяца в день годаи наоборот. Например, 1-ое марта является 60-м днем невисо-косного года и 61-м днем високосного года. Давайте введемдве функции для выполнения этих преобразований: DAY_OF_YEARпреобразует месяц и день в день года, а MONTH_DAY преобразу-ет день года в месяц и день. Так как эта последняя функциявозвращает два значения, то аргументы месяца и дня должныбыть указателями: MONTH_DAY(1977, 60, &M, &D) Полагает M равным 3 и D равным 1 (1-ое марта). Обе эти функции нуждаются в одной и той же информацион-ной таблице, указывающей число дней в каждом месяце. Так какчисло дней в месяце в високосном и в невисокосном году отли-чается, то проще представить их в виде двух строк двумерногомассива, чем пытаться прослеживать во время вычислений, чтоименно происходит в феврале. Вот этот массив и выполняющиеэти преобразования функции: STATIC INT DAY_TAB[2][13] = { (0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31), (0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)}; DAY_OF_YEAR(YEAR, MONTH, DAY) /* SET DAY OF YEAR */INT YEAR, MONTH, DAY; /* FROM MONTH & DAY */{ INT I, LEAP; LEAP = YEAR%4 == 0 && YEAR%100!= 0 \!\! YEAR%400 == 0; FOR (I = 1; I < MONTH; I++) DAY += DAY_TAB[LEAP][I]; RETURN(DAY);{ MONTH_DAY(YEAR, YEARDAY, PMONTH, PDAY) /*SET MONTH,DAY */INT YEAR, YEARDAY, *PMONTH, *PDAY; /* FROM DAY OF YEAR */{ LEAP = YEAR%4 == 0 && YEAR%100!= 0 \!\! YEAR%400 == 0; FOR (I = 1; YEARDAY > DAY_TAB[LEAP][I]; I++)YEARDAY -= DAY_TAB[LEAP][I]; *PMONTH = I; *PDAY = YEARDAY;} Массив DAY_TAB должен быть внешним как для DAY_OF_YEAR, таки для MONTH_DAY, поскольку он используется обеими этими фун-кциями. Массив DAY_TAB является первым двумерным массивом, с ко-торым мы имеем дело. По определению в "C" двумерный массивпо существу является одномерным массивом, каждый элемент ко-торого является массивом. Поэтому индексы записываются как DAY_TAB[I][J]а неDAY_TAB [I, J] как в большинстве языков. В остальном с двумерными массивамиможно в основном обращаться таким же образом, как в другихязыках. Элементы хранятся по строкам, т.е. При обращении кэлементам в порядке их размещения в памяти быстрее всего из-меняется самый правый индекс. Массив инициализируется с помощью списка начальных зна-чений, заключенных в фигурные скобки; каждая строка двумер-ного массива инициализируется соответствующим подсписком. Мыпоместили в начало массива DAY_TAB столбец из нулей для то-го, чтобы номера месяцев изменялись естественным образом от1 до 12, а не от 0 до 11. Так как за экономию памяти у наспока не награждают, такой способ проще, чем подгонка индек-сов. Если двумерный массив передается функции, то описаниесоответствующего аргумента функции должно содержать количес-тво столбцов; количество строк несущественно, поскольку, каки прежде, фактически передается указатель. В нашем конкрет-ном случае это указатель объектов, являющихся массивами из 13 чисел типа INT. Таким образом, если бы требовалось пере-дать массив DAY_TAB функции F, то описание в F имело бы вид: F(DAY_TAB)INT DAY_TAB[2][13];{...} Так как количество строк является несущественным, то описа-ние аргумента в F могло бы быть таким: INT DAY_TAB[][13]; или таким INT (*DAY_TAB)[13]; в которм говорится, что аргумент является указателем массиваиз 13 целых. Круглые скобки здесь необходимы, потому чтоквадратные скобки [] имеют более высокий уровень старшинст-ва, чем *; как мы увидим в следующем разделе, без круглыхскобок INT *DAY_TAB[13]; является описанием массива из 13 указателей на целые.
|
||||
Последнее изменение этой страницы: 2016-08-26; просмотров: 176; Нарушение авторского права страницы; Мы поможем в написании вашей работы! infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 18.224.73.125 (0.004 с.) |