x = xn;
dx = (xk - xn) / (n - 1); for(i = 1; i <= n; i++)
{
printf("%5d%10.2f%10.2\n", i, x, f(x)); x += dx;
}
} |
|
/*Файл main.c |
*/ |
#include”bibl.h” |
|
int main(void) |
|
{double xn, xn: int n;
printf(“Количество расчетных точек=”) scanf(“%d”, &n);
printf(“Начальное значение аргумента=”); scanf(“%lf”, &xn);
printf(“Конечное значение аргумента=”); scanf(“%lf”, &xк);
/* Табулирование функции y = x2 */ table(sqr, xn, xk, n);
/* Табулирование функции y = x3 */ table(cube, xn, xk, n);
return 0;
}
Между указателями и массивами в языке Си имеется тесная связь. Эта связь проявлется в сдудующем:
∙имя массива является константным укзателем.
∙массив, передаваемый в качестве аргумента в вызове функции, «вырождается» в указатель.
∙к любому указателю синтаксически применима операция индексирования (семантически это оправдано только для указателей, работающих с массивами).
На практике часто возникают задачи, связанные с обработкой текста. Текстом называется упорядоченная последовательность строк. Это положение приводит к необходимости рассмотрения вопроса о преставлении строк и способах их обработки.
106
Строкой в программировании называется последовательность символов переменной длины. Для работы с отдельными символами в языке Си используется тип char. А как обстоит дело со строками в языке Си? Строкового типа в языке Си нет. Концепция работы со строками в языке Си базируется на двух положениях:
1.Строки в языке Си хранятся в массивах, элементы которых имеют тип char.
2.Строки в символьном массиве хранятся в виде последовательности символов, дополненной нуль-символом.
Нуль-символ – символ, код которого равен нулю (‘\0’).
Строковый литерал – это последовательность символов, заключенная в кавычки. Например: “”Hello.
Какой тип имеет строковый литерал? Строковый литерал “Hello” имеет тип char[6]. В общем случае строковый литера, состоящий из “n” символов имеет тип char[n + 1]. Наличие слагаемого 1 учитывает необходимость хранить нуль-символ.
Таким образом, символьный литерал – это разновидность символьного массива. Возможна т другая трактовка символьного литерала, в соответствии с которой строковый литерал является указателем. Например, char * p = “Hello”; В этом примере указатель p инициализируется адресом строкового литерала.
Часто текст строкового литерала не помещается на одной строке экрана. Возможны два способа продолжения ее на следующей строке. Рассмотрим вначале первый способ. Он состоит в использовании в конце текущей строки символа обратная косая черта. Например
printf(“Эта длинная строка \ введена на нескольких\ строчках, которые заканчиваются \ обратной чертой”);
Отступы на строках продолжения не следует делать, а если они сделаны, то пробелы, их представляющие будут внесены в строку.
Второй способ продолжить строку состоит в следующем: необходимо в конце текущей строки поставить символ “ (т. е.) закрыть строку, а на следующей строке экрана открыть новую строку. Например:
printf(“Начало строки” “продолжение строки”);
Символическая строковая константа – это константа, имеющая имя. Возможны два способа объявления символических констант:
∙использование директивы препроцессора define
∙использование ключевого слова const.
107
Примеры.
#define MESSAGE “Ошибка ввода”
const char message2[] = “Ошибка ввода”ж
Строковая переменная – это идентификатор массива, элементы которого имеют тип char. Этот массив должен содержать текст строки, в конце которой должен находиться нуль-символ. Рассмотрим следующий фрагмент программного кода.
#define MLEN 128 char str1[MLEN]; int main(void)
{
char str2[MLEN]; // …
}
Здесь str1 является пустой строкой. Дело в том, что str1 является глобальной переменной, а все глобальные переменные инициализируются нулями. С другой стороны str2 – локальная переменная. Такая переменная содержит “мусор”, среди которого может нуль-символ отсутствовать.
Возможны два способа инициализации строковой переменной:
∙строковым литералом.
∙символьным массивом
Ограничимся рассмотрением наиболее широко применяемого способа
инициализации с помощью строкового литерала. Пример. char str3[10] = “Hello”;
В результате выполнения этой строки программного кода будет выделено 10 байт памяти. В первых шести байт этой памяти будет записан текст константы, а затем нуль-символ.
Отметим, что при наличии инициализатора требуемый объем памяти
можно не указывать. Пример. char str4[10] = “Hello”;
В этом случае будет выделено шесть байт памяти.
Следует отметить, что инициализировать строковую переменную можно только в момент ее определения. Ниже следующий код содержит
синтаксическую ошибку. char str4[10];
str4 = “Hello”; // Синтаксическая ошибка
Со строковыми переменными можно выполнять только поэлементные операции. Все остальные операции:
108
∙присваивание (копирование),
∙сравнение,
∙поиск подстрок и др.
следует выполнять либо с помощью библиотечных функций, либо разрабатывать собственную (пользовательскую) функцию.
Для работы с библиотечными функциями, предназначенными для работы со строками, следует подключать заголовочный файл string.h.
Имеется ряд библиотечных функций, помощью которых можно выполнять ввод строк:
∙scanf(),
∙gets(),
∙fgets().
Начнем рассмотрение с функции scanf().
Особенность функции scanf() состоит в том, что с ее помощью нельзя вводить строки, содержащие пробелы. Эта функция может использоваться
только для ввода отдельных слов. Приведем пример.
#define MLEN 129 #include<stdio.h>
int main(void)
{
char st[MLEN]; prinf(“Enter a string: ”); scanf(“%s”, st); printf(“%s”, st);
/* */ return 0;
}
Следует учитывать, что переменная st является указателем и при ее передаче в функцию scanf() оператор взятия адреса & не нужен. Протокол
работы с программой, приведенной выше, будет иметь следующий вид:
Enter a string: Hello, world<Enter> Hello,
Выше с помощью подчеркивания выделен ввод пользователя. Из протокола видно, что введенным оказалось только первое слово (Hello,).
Другой особенностью функции scanf() является возможность ограничения количества вводимых символов. Для этого в спецификации преобразования функции следует использовать дополнительный параметр, как это показано на примере, приводимом ниже:
#define MLEN 129 #include<stdio.h>
109
int main(void)
{
char st[MLEN]; prinf(“Enter a string: ”); scanf(“%5s”, buffer); printf(“%s”, buffer);
/* */ return 0;
}
Протокол работы с программой, приведенной выше, будет иметь
следующий вид:
Enter a string: 1234567890<Enter> 12345
В этом примере спецификация преобразования содержит дополнительный параметр в виде числового литерала (5). В рассматриваемом случае воспринимается не более 5 вводимых символов.
Отметим, что при организации ввода строк программист обязан предусмотреть выделение памяти для вводимой строки. Ниже приведен пример, в котором отсутствует выделение памяти для вводимой строки.
#include<stdio.h>
int main(void)
{
char *s;
prinf(“Enter a string: ”); scanf(“%5s”, s); /* Ошибка*/ printf(“%s”, s);
/* */ return 0;
}
Ошибка состоит в том, что используется неинициализированный указатель s.
Прототип этой функции имеет следующий вид:
#include<stdio.h> char* gets(char* str);
Рассматриваемая функция читает символы, вводимые с клавиатуры в символьный массив, на который установлен указатель str. Это чтение выполняется до тех пор, пока не встретится либо символ “новая строка” (\n), либо конец файла. После записи последнего прочитанного символа в массив str добавляется нуль – символ. Если встречается символ “новая строка”, то он отбрасывается. Если выполнение функции завершено успешно, то она возвращает указатель str. Функция gets() вернет значение NULL в том случае, когда при достижении конца файла ни один из символов не оказался прочитанным, причем содержимое массива, на который установлен указатель
110