Главная страница
Навигация по странице:

  • Массив строк и массив указателей

  • Урок 14. Функции для обработки строк Особенности функций для работы со строками

  • Копирование частей строк

  • Описание некоторых функций для работы со строками

  • Урок 15. Структурный тип данных Объявление и определение структур

  • Программирование в Linux. Учебное пособие С. В. Шапошникова, Лаборатория юного линуксоида, май 2012 1 Пояснительная записка


    Скачать 0.88 Mb.
    НазваниеУчебное пособие С. В. Шапошникова, Лаборатория юного линуксоида, май 2012 1 Пояснительная записка
    АнкорПрограммирование в Linux
    Дата02.12.2022
    Размер0.88 Mb.
    Формат файлаpdf
    Имя файлаProgramming Linux C.pdf
    ТипУчебное пособие
    #824650
    страница7 из 10
    1   2   3   4   5   6   7   8   9   10
    Передача строки в функцию
    Передача строки в функцию ничем не отличается от передачи туда массива чисел:
    void change (char *s) {
    for (;*s != '\0'; s++)
    (*s)++;
    }
    В этом примере функция change() принимает в качестве параметра указатель на символ. В теле функции значение указателя инкрементируется, указывая на следующий символ массива. В теле цикла инкрементируется значение, которое находится по адресу, который содержит указатель.
    Задание
    Объявите в программе три массива символов. Данные для двух из них получите с помощью вызовов функции gets(). Третий массив должен содержать результат конкатенации
    (соединения) двух введенных строк. Напишите функцию, которая выполняет конкатенацию строк.
    Массив строк и массив указателей
    Рассмотрим более сложный пример. Допустим, у нас есть набор строк. Требуется выполнить сортировку строк по возрастанию по признаку длины: сначала вывести самые короткие строки, затем более длинные.
    Набор строк можно представить как двумерный массив, т.е. массив, состоящий из одномерных массивов, где каждый одномерный массив — это строка символов:
    char str[][10] = {"Hello", "World", "!!!", "&&&"};
    Представьте себе, что значит выполнить сортировку строк. Это значит, надо поменять местами содержимое множества ячеек памяти. Это достаточно трудоемкая для компьютера работа, особенно если строк очень много. Однако можно поступить по-иному. Достаточно создать массив указателей, каждый элемент которого будет указывать на соответствующую ему строку первого массива. Далее выполнить сортировку указателей, что несомненно быстрее. Конечно, сам массив строк отсортирован не будет, однако благодаря указателям у нас будет хранится отсортированный "срез" массива:
    54

    #include
    #include
    #define N 6
    void sortlen(char *s[]);
    main() {
    char strings[N][30];
    char *strP[N];
    int i;
    for(i=0; igets(strings[i]);
    strP[i] = &strings[i][0];
    }
    printf("\n");
    sortlen(strP);
    for(i=0; iprintf("%s\n",strP[i]);
    }
    }
    void sortlen(char **s) { // **s == *s[] - как бы массив указателей int i, j;
    char *str;
    for (i=0; i55
    for (j=0; j < N-i-1; j++) {
    if (strlen(s[j]) > strlen(s[j+1])) {
    str = s[j];
    s[j] = s[j+1];
    s[j+1] = str;
    }
    }
    }
    Примечания к программе:

    Функция strlen()
    объявлена в заголовочном файле string.h. Она возвращает длину строки без учета завершающего нулевого символа.

    На самом деле параметром функции sortlen()
    является указатель на указатель. Хотя для понимания проще сказать, что параметром является массив указателей на символы. Мы передаем в функцию указатель на первый элемент массива strP, который сам является указателем. Если бы в функции мы инкрементировали переменную s, то переходили бы к следующему элементу-указателю массива strP.

    Сортировка выполняется методом пузырька: если длина строки, на которую ссылается следующий указатель массива strP, меньше длины строки под текущим указателем, то значения указателей меняются.

    Выражение strP[i] = &strings[i][0] означает, что элементу массива указателей присваивается ссылка на первый символ каждой строки.
    Задание
    Напишите программу, которая сортирует строки по алфавиту. Для упрощения задачи пусть сортировка выполняется только по первым буквам строк (если первые буквы слов одинаковы, то вторые и последующие символы проверять не надо).
    Урок 14. Функции для обработки строк
    Особенности функций для работы со строками
    Языки программирования могут включать специальные функции для работы со строками, избавляя тем самым программиста от необходимости писать собственные функции обработки строк. Например, часто требуется определить длину строки, и поэтому в языках предусмотрена функция, измеряющая ее длину.
    В языке программирования
    C
    функции для работы со строками объявляются в заголовочном файле string.h, который надо не забывать подключать к своему исходному коду. Существует около двадцати функций для работы со строками. Среди них есть те, которые осуществляют поиск символов в строке, функции сравнения, копирования строк, а также более специфические. Перечень и описание большинства существующих на данный момент в языке
    C
    функций можно найти в приложении книги Б. Кернигана, Д. Ритчи "Язык программирования
    C
    . Второе издание".
    Все функции, объявленные в string.h, в процессе своей работы могут изменять или не изменять одну из переданных по указателю строк. Это зависит от назначения функции.
    Однако большинство из них что-то возвращают: либо указатель на символ, либо целое. При этом если функция меняет один из своих параметров и ради этого была вызвана, тогда то, что она возвращает, можно проигнорировать (т.е. ничему не присваивать в вызывающей функции).
    Например, функция strcpy()
    имеет такое объявление: char *strcpy (char *, const
    56
    char *)
    . Она копирует строку, на которую указывает второй параметр, в строку, на которую указывает первый параметр. Таким образом первый параметр изменяется. Кроме того, функция возвращает указатель на первый символ строки: char s1[10], s2[10];
    char *s3;
    s3 = s2;
    gets(s1);
    s3 = strcpy(s2,s1);
    puts(s2);
    puts(s3);
    printf("%p, %p\n", s2, s3);
    Здесь s2 и s3 указывают на один и тот же символ (
    printf()
    выводит одинаковые адреса).
    Однако то, что возвращает strcpy()
    , нельзя присвоить массиву. Результат работы этой функции обычно ничему не присваивают; бывает достаточно того, что она просто изменяет одну из переданных по указателю строк.
    Другое дело, такие функции как strlen()
    или strcmp()
    , которые не изменяют параметры, а вызываются ради результата. Функция strcmp()
    сравнивает две строки-аргумента по буквам
    (лексикографически) и возвращает 0, -1 или 1. Например, вызов strcmp("boy", "body") вернет 1, т.к. код буквы 'y' больше буквы 'd'. Вызов strcmp("body", "boy")
    вернет -1, т.к. первый аргумент лексикографически меньше второго.
    Функция strtok()
    С помощью функции strtok()
    можно разбить строку на отдельные части (лексемы).
    Объявление этой функции выглядит так char *strtok (char *, const char *)
    . При первом вызове функции в качестве первого параметра указывается строка, которую требуется разбить. Вторым параметром указывается строка-разделитель. При последующих вызовах функции для этой же строки первым параметром должен быть NULL, т.к. функция уже "запомнила" с чем работает. Рассмотрим пример:
    char str[] = "one, two, three, four, five";
    char *sp;
    sp = strtok(str, ", ");
    while (sp) {
    puts(sp);
    sp = strtok(NULL, ", ");
    }
    В результате выполнения данного кода на экран в столбик выводятся слова:
    one two three four five
    При первом вызове strtok()
    в функцию передается указатель на первый символ массива и строка-разделитель. После этого вызова массив str изменяется, в нем остается только слово "one", также функция возвращает указатель на это слово, который присваивается sp.
    Хотя мы потеряли остаток массива в вызывающей функции, однако внутри strtok() сохраняется указатель на остаток массива. Когда передается NULL, функция "знает", что надо работать с этим "хвостом".
    57

    Копирование частей строк
    Когда требуется просто соединить две строки, то проблема легко решается с помощью вызова функции strcat()
    , которая к концу первого аргумента присоединяет второй.
    Похожая функция strncat()
    присоединяет n символов второй строки к первой. n
    указывается в качестве третьего параметра.
    Что если ситуация более сложная? Например, есть две непустые строки и надо соединить начало первой и конец второй. Сделать это можно с помощью функции strcpy()
    , если передавать ссылки не на первые символы строк:
    char s1[20] = "Peter Smith", s2[] = "Julia Roberts";
    strcpy(s1+5, s2+5);
    puts(s1);
    В данном случае на экране будет выведено "Peter Roberts". Почему так произошло? В функцию strcpy()
    был передан указатель на шестой символ первой строки. Это привело к тому, что при копировании символы этой строки затираются только начиная с 6-го, т.к. strcpy()
    о предыдущих символах ничего не "знает". В качестве второго аргумента также передается только часть строки, которая и копируется в первую.
    Как вставить одну строку в середину другой? Можно решить эту задачу, используя третью "буферную" строку, куда можно сначала скопировать первую строку, потом вторую, затерев конец первой, потом присоединить конец первой. Но можно поступить и так:
    char s1[20] = "one three", s2[20] = "two";
    strcpy(s2+3, s1+3);
    strcpy(s1+4, s2);
    puts(s1);
    Здесь сначала во вторую строку копируется конец первой, получается "two three". Затем в первую строку, минуя ее начало, копируется вторая.
    Описание некоторых функций для работы со строками
    Задание
    Ниже представлены описания некоторых функций, выполняющих операции над строками.
    Придумайте и напишите маленькие программы, иллюстрирующие работу этих функций.

    char *strchr (const char *, int c)
    . Возвращает указатель на первое вхождение символа с в строку. Возвращает NULL, если такого символа в строке нет.

    char *strstr (const char *s2, const char *s1)
    . Возвращает указатель на первое вхождение строки s1 в строку s2. Если совпадений нет, возвращает NULL.

    char *strncpy (char *, const char *, size_t n)
    . Копирует n символов второй строки в первую.

    size_t strspn (const char *, const char *)
    . Возвращает длину начала первой строки, в которую входят символы, из которых состоит вторая строка.
    Урок 15. Структурный тип данных
    Объявление и определение структур
    Примером структуры может послужить любой объект, для которого описывается ряд его характеристик, имеющих значение в данной программе. Например, для книг это может быть название, автор, количество страниц; для окружности — координаты центра, диаметр, цвет.
    На языке программирования
    C
    объявление вышеупомянутых структурных типов данных
    58
    может выглядеть так:
    struct book {
    char title[50];
    char author[30];
    int pages;
    };
    struct circle {
    int x, y;
    float dia;
    char color[10];
    };
    В данном случае мы как бы создаем новый тип данных, но еще не объявляем переменных этих типов. Обратите внимание на точку с запятой в конце объявлений.
    Чаще переменные структур объявляются так:
    struct circle a, b, c;
    struct book mybook;
    Здесь объявляются три структуры типа circle и одна структура типа book. Можно объявлять типы структур и их переменные по-иному, но мы для избежания путаницы рассматривать другие способы не будем.
    Каждая переменная типа circle содержит четыре элемента (или поля) — x, y, dia, color.
    Можно сказать, что они представляют собой вложенные переменные. Причем эти переменные разных типов. Таким образом переменная-структура позволяет объединить под одним именем ряд разнородных данных. Обычно это нужно для удобства обработки данных.
    Если нельзя было бы создавать структуры, то пришлось бы создавать множество независимых переменных или ряд массивов, явной взаимосвязи между которыми не было бы. Структуру же позволяют объединять взаимосвязанные данные. Это конечно еще не объектно-ориентированное программирование, но уже взгляд в его сторону.
    Объявив переменную структурного типа, мы можем получить доступ к каждому ее элементу для присваивания, изменения или получения значения:
    a.x = 10; a.dia = 2.35;
    printf("%.2f ", a.dia);
    Значение элементов структуры можно сразу определять при объявлении переменной, что похоже на инициализацию массивов:
    struct book lang_c = {"Language C", "Ritchi", 99};
    Значение переменной-структуры можно присвоить переменной того же типа:
    struct book { char *title, *author; int pages; };
    struct book old, new;
    old.title = "GNU/Linux"; old.author = "people"; old.pages = 20213;
    new = old;
    new.pages += 2000;
    printf("%d, %d\n", old.pages, new.pages);
    В четвертой строке кода данные переменной old присваиваются new. В итоге вторая структура содержит копию данных первой. То, что можно выполнять присваивание по отдельным полям должно быть понятно.
    Задание
    Придумайте и опишите структуру. Объявите переменные этого типа. Присвойте им значения и выведите на экран.
    59

    Структуры и функции
    Структуры-переменные можно передавать в функции в качестве параметров и возвращать их оттуда. Структуры передаются по значению, как обычные переменные, а не по ссылке, как массивы.
    Рассмотрим программу, в которой одна функция возвращает структуру, а другая — принимает ее в качестве параметра:
    #include
    #include
    struct circle { int x, y; float dia; char color[10]; };
    struct circle new_circle();
    void cross (struct circle);
    main () {
    struct circle a;
    a = new_circle();
    cross(a);
    }
    struct circle new_circle() {
    struct circle new;
    printf("Координаты: "); scanf("%d%d", &new.x, &new.y);
    printf("Диаметр: "); scanf("%f", &new.dia); printf("Цвет: "); scanf("%s", new.color);//gets(new.color);
    return new;
    }
    void cross (struct circle c) {
    double hyp;
    hyp = sqrt((double) c.x * c.x + (double) c.y * c.y);
    printf("Расстояние от центра круга до начала координат: %.2lf\n", hyp);
    if (hyp <= c.dia / 2) puts("Круг пересекает начало координат");
    else puts("Круг не содержит точки начала коорднат");
    }
    Примечание. При компиляции программы в GNU/Linux команда выглядит так: gcc program.c -lm
    . Это связано с использованием библиотеки с математическими функциями.

    Объявляется структура circle как глобальный тип данных. Таким образом любая, а не только main()
    , функция может создавать переменные этого типа.

    Функция new_circle()
    возвращает структуру, а функция cross()
    принимает структуру по значению. Следует отметить, что можно создавать функции, которые как принимают (возможно, несколько структур) так и возвращают структуру.

    В функции new_circle()
    создается переменная new типа struct circle, поля которой заполняются пользователем. Функция возвращает значение переменной new в функцию main()
    , где это значение присваивается переменной a, которая также принадлежит типу sctruct circle.

    Функция cross()
    определяет, пересекает ли круг начало координат. В ее теле вычисляется расстояние от центра круга до начала координат. Это расстояние является гипотенузой прямоугольного треугольника, длина катетов которого равна
    60
    значениям x и у. Далее, если гипотенуза меньше радиуса, то круг пересекает начало координат, т.е. точку (0, 0).

    В функции main()
    при вызове cross()
    данные, содержащиеся в переменной a, копируются и присваиваются переменной c.
    Задание
    Придумайте и напишите свою программу, в которой была бы как минимум еще одна функция помимо main(), манипулирующая структурой.
    Указатели и структуры
    В отличие от массивов, структуры передаются в функции по значению. Это не всегда рационально, т.к. структуры могут быть достаточно большого размера, и копирование таких участков памяти может замедлять работу программы. Поэтому часто структуры в функцию передают по ссылке, при этом можно использовать как указатель, так и операцию получения адреса.
    struct book new; // переменная-структура struct book *pnew; // указатель на структуру reader(&new); // передаем адрес pnew = &new;
    reader(pnew); // передаем указатель
    Тогда функция reader()
    должна иметь примерно такое объявление:
    void reader (struct book *pb);
    Возникает вопрос, как при использовании указателей обращаться к элементам структур? Во первых надо получить саму структуру, т.е. если pnew указатель, то сама структура будет
    *pnew
    . Далее можно уже обращаться к полям через точку:
    *pnew.title
    . Однако это выражение не верно, т.к. приоритет операции "точка" (обращение к полю) выше операции "звездочка" (получить значение по адресу). Таким образом приведенная запись сначала пытается получить значение поля title у указателя pnew, но у pnew нет такого поля. Проблема решается с помощью скобок, которые изменяют порядок выполнения операций:
    (*pnew).title
    . В таком случае сначала извлекается значение по адресу pnew, это значение является структурой. Затем происходит обращение к полю структуры.
    В языке программирования
    C
    записи типа
    (*pnew).title часто заменяют на такие: pnew-
    >title
    , что позволяет синтаксис языка. Когда в программе вы видите стрелку (тире и скобка) всегда помните, то, что написано до стрелки, — это указатель на структуру, а не переменная-структура.
    Пример кода с использованием указателей:
    #include
    struct circle { int x, y; float dia; };
    void inversion (struct circle *);
    main () {
    struct circle cir, *pc = ○
    pc->x = 10; pc->y = 7; pc->dia = 6; inversion(pc);
    printf("x = %d, y = %d\n", cir.x, cir.y);
    }
    void inversion(struct circle *p) {
    p->x = -p->x;
    61
    p->y = -p->y;
    }
    Задание
    Придумайте свою программу, в которой бы использовались указатели на структуры.
    Массивы структур
    Обычно создание в программе одной переменной структурного типа не имеет особого смысла. Чаще структурами пользуются, когда необходимо описать множество похожих объектов, имеющих разные значения признаков. Значения каждого объекта следует объединить вместе (в структуру) и тем самым отделить от значений других объектов.
    Например, описание ряда книг или множества людей. Таким образом мы можем организовать массив, где каждый элемент представляет собой отдельную структуру, а все элементы принадлежат одному и тому же структурному типу.
    Напишем программу для учета персональных компьютеров в организации. Каждая структура будет описывать определенные модели и содержать поле, в котором будет указано количество таких объектов. Поэтому при объявлении структурного типа данных следует описать такие поля как тип компьютера, модель процессора, количество.
    Программа будет предоставлять возможность получать информацию о всех моделях и изменять количество компьютеров указанной пользователем модели. В программе будут определены две функции (помимо main()
    ): для вывода всей информации и для изменения количества компьютеров.
    #include
    #define N 5
    struct computer { char *type; char *proc; int qty; };
    void viewer (struct computer *);
    void changer (struct computer *);
    main () {
    struct computer comps[N]= {
    "Desktop", "earlier then P4", 10,
    "Desktop", "P4", 30 ,
    "Desktop", "Core", 20 ,
    "Desktop", "AMD", 2 ,
    "Notebook", "Core", 1 };
    viewer(comps);
    changer(comps);
    viewer(comps);
    }
    void viewer (struct computer *comp) {
    int i;
    for (i=0; i < N; i++, comp++)
    printf("%2d) %-8s - %-15s: %3d\n", i+1, comp->type, comp->proc, comp->qty);
    }
    void changer (struct computer *comp) {
    int i, differ;
    62
    printf("Введите номер модели: ");
    scanf("%d", &i); i--;
    printf("На сколько уменьшить или увеличить: ");
    scanf("%d", &differ);
    (comp+i)->qty += differ;
    }

    Массив структур инициализируется при его объявлении.

    Функции viewer()
    и changer()
    принимают указатели на структуру computer.

    В теле viewer()
    указатель инкрементируется в заголовке цикла; таким образом указывая на следующий элемент массива, т.е. на следующую структуру.

    В выражении
    (comp+i)->qty скобки необходимы, т.к оператор -> имеет более высокий приоритет. Скобки позволяют сначала получить указатель на i-ый элемент массива, а потом обратиться к его полю.

    Декрементирование i в функции changer()
    связано с тем, что индексация начинается с нуля, а номера элементов массива, которые пользователь видит на экране, с единицы.

    Для того, чтобы уменьшить количество компьютеров, при запросе надо ввести отрицательное число.
    Пример результата работы программы:
    Задание
    Придумайте свою программу, в которой бы использовался массив структур, или перепишите приведенную выше программу и дополните ее функцией, которая позволяет добавлять в массив новый элемент-структуру.
    1   2   3   4   5   6   7   8   9   10


    написать администратору сайта