WWW.DISSERS.RU

БЕСПЛАТНАЯ ЭЛЕКТРОННАЯ БИБЛИОТЕКА

   Добро пожаловать!


Pages:     | 1 |   ...   | 8 | 9 || 11 | 12 |   ...   | 13 |

можно "заказать" считывание 50 элементов данных, а получить только 10. Ошибки при этом не возникнет.

Пример использования функций fread() и fwrite():

#include #include main () { FILE *file;

char shelf1[50], shelf2[100];

int n, m;

file = fopen("shelf1.txt", "rb");

n=fread(shelf1, sizeof(char), 50, file);

fclose(file);

file = fopen("shelf2.txt", "rb");

m=fread(shelf2, sizeof(char), 50, file);

fclose(file);

shelf1[n] = '\0';

shelf2[m] = '\n';

shelf2[m+1] = '\0';

file = fopen("shop.txt", "wb");

fwrite(strcat(shelf2,shelf1), sizeof(char), n+m, file);

fclose(file);

} Здесь осуществляется попытка чтения из первого файла 50-ти символов. В n сохраняется количество реально считанных символов. Значение n может быть равно 50 или меньше.

Данные помещаются в строку. То же самое происходит со вторым файлом. Далее первая строка присоединяется ко второй, и данные сбрасываются в третий файл.

Решение задач Задание 1. Напишите программу, которая запрашивает у пользователя имя (адрес) текстового файла, далее открывает его и считает в нем количество символов и строк.

2. Напишите программу, которая записывает в файл данные, полученные из другого файла и так или иначе измененные перед записью. Каждая строка данных, полученная из файла, должна помещаться в структуру.

Урок 18. Аргументы программы (или функции main()) Бывает, что данные в программу передаются из командной строки при ее вызове. Такие данные называются аргументами командной строки. Выглядит это так, например:

./a.out test.txt ls -lt /home/peter/ Здесь вызываются программы a.out (из текущего каталога) и ls (из одного каталога, указанного в переменной окружения PATH). Первая программа из командной строки получает одно слово — test.txt, вторая — два: -lt и /home/peter/.

Если программа написана на языке C, то при ее запуске управление сразу передается в функцию main(), следовательно, именно она получает аргументы командной строки, которые присваиваются ее переменным-параметрам.

До этого мы определяли функцию main() так, как-будто она не принимает никакие параметры и ничего не возвращает. На самом деле в языке C любая функция по-умолчанию (если не определено ничего иного) возвращает целое число. В этом можно убедиться. Если записать код таким образом:

main() { printf("Hi\n");

return 0;

}, то никакого предупреждения или ошибки при компиляции не возникнет. То же самое будет, если записать int main(). Это доказывает, что функция по-умолчанию возвращает целое число, а не ничто (void). Хотя то, что возвращает функция всегда можно "переопределить", например, void main() или float main().

При вызове программы из командной строки в нее всегда передается пара данных:

1. целое число, обозначающее количество слов (элементов, разделенных пробелами) в командной строке при вызове, 2. указатель на массив строк, где каждая строка — это отдельное слово из командной строки.

Следует иметь в виду, что само имя программы также считается. Например, если вызов выглядит так:

./a.out 12 theme, то первый аргумент программы имеет значение 4, а массив строк определяется как {"./a.out", "12", "theme", "2"}.

Обратите внимание на терминологию, есть всего два аргумента программы (число и массив), но сколько угодно аргументов командной строки. Аргументы командной строки "преобразуются" в аргументы программы (в аргументы функции main()).

Эти данные (число и указатель) передаются в программу даже тогда, когда она просто вызывается по имени без передачи в нее чего-либо:./a.out. В таком случае первый аргумент имеет значение 1, а второй указывает на массив, состоящий всего из одной строки {"./a.out"}.

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

Ошибки не возникнет.

Чтобы получить доступ к переданным в программу данным, их необходимо присвоить переменным. Поскольку аргументы сразу передаются в main(), то ее заголовок должен выглядеть таким образом:

main (int n, char *arr[]) В первой переменной (n) содержится количество слов, а во второй — указатель на массив строк. Часто второй параметр записывают в виде **arr. Однако это то же самое. Вспомним, что сам массив строк, содержит в качестве своих элементов указатели на строки. А в функцию мы передаем указатель на первый элемент массива. Получается, что передаем указатель на указатель, т.е. **arr.

Задание Напишите такую программу:

#include main(int argc, char **argv) { int i;

printf("%d\n", argc);

for (i=0; i < argc; i++) puts(argv[i]);

} Она выводит количество слов в командной строке при ее вызове и каждое слово с новой строки. Вызовите ее без аргументов командной строки и с аргументами.

В программе мы использовали переменные-параметры argc и argv. Принято использовать именно такие имена, но на самом деле они могут быть любыми. Лучше придерживаться этого стандарта, чтобы ваши программы были более понятны не только вам, но и другим программистам.

Практическое значение передачи данных в программу Если у вас есть опыт работы в командной строке GNU/Linux, вы знаете, что у большинства команд есть ключи и аргументы. Например, при просмотре содержимого каталогов, копировании, перемещении в качестве аргументов указываются объекты файловой системы, над которыми выполняется команда. Особенности ее выполнения определяются с помощью ключей. Например, в команде cp -r../les_1../les_cp — это имя команды, -r — ключ, а../les_1 и../les_101 — аргументы команды.



Вообще чаще всего в программы при их запуске передаются адреса файлов и "модификаторы" (это ключи) процесса выполнения программы.

Напишем программу, которая открывает указанные пользователем в командной строке файлы на запись или добавление и записывает (добавляет) туда одну и туже информацию, которую пользователь вводит с клавиатуры в процессе выполнения программы:

#include #include main (int argc, char **argv) { int i, ch;

FILE *f[5];

if (argc < 3 || argc > 7) { puts("Неверное количество параметров");

return 1;

} if (strcmp(argv[1], "-w") != 0 && strcmp(argv[1], "-a") != 0) { puts("Первый параметр может быть либо -w, либо -a");

return 2;

} for (i=0; i < argc-2; i++){ f[i] = fopen(argv[i+2], argv[1]+1);

if (f[i] == NULL) { printf("Файл %s нельзя открыть\n", argv[i+2]);

return 3;

} } while ((ch = getchar()) != EOF) for (i=0; i < argc-2; i++) putc(ch,f[i]);

for (i=0; i < argc-2; i++) fclose(f[i]);

return 0;

} Пояснения к коду:

1. Создается массив из пяти файловых указателей. Следовательно можно одновременно открыть не более пяти файлов. Файловый указатель первого файла будет хранится в элементе массива f[0], второго — в f[1] и т.д.

2. Проверяется количество аргументов командной строки. Их должно быть не меньше трех, т.к. первый - это имя программы, второй — режим открытия файла, третий — первый или единственный файл, в который будет производится запись. Поскольку программа позволяет открыть только пять файлов, то общее число аргументов командной строки не может быть больше семи. Поэтому если количество аргументов меньше 3 или больше 7, то программа завершается, т.к. оператор return приводит к выходу из функции, даже если после него есть еще код. Возвращаемое из функции значение неравное 0, может быть интерпретировано родительским процессом, как сообщение о том, что программа завершилась с ошибкой.

3. Проверяется корректность второго аргумента командной строки. Если он не равен ни "-w", ни "-a", то условное выражение во втором if возвращает 1 (true). Функция strcmp() позволяет сравнивать строки и возвращает 0 в случае их равенства.

4. В цикле for открываются файлы по указанным адресам, которые начинаются с третьего элемента массива argv. Именно поэтому к i прибавляется 2, чтобы получать элементы массива argv, начиная с третьего. Выражение argc-2 указывает на количество переданных имен файлов; т.к. в argc хранится общее число аргументов командной строки, первые два из которых не являются именами файлов.

5. Выражение argv[1]+1 позволяет "вырезать" из строки "-w" (или "-a") подстроку "w" (или "a"), т.к. argv[1] по сути указатель на первый элемент строки. Прибавляя к указателю единицу, мы смещаем его к следующему элементу массива.

6. Если файл отрыть не удается, то функция fopen() возвращает NULL. В таком случае программа завершается.

7. Каждый символ, введенный пользователем с клавиатуры, записывается во все открытые файлы.

8. В конце файлы закрываются.

Задание Придумайте свой пример использования аргументов командной строки в программе.

Реализуйте его.

Урок 19. Препроцессор языка С В компилятор языка программирования C входит так называем препроцессор, который осуществляет подготовку программы к компиляции. Среди прочего он, например, включает содержимое одних файлов в другие, заменяет в тексте исходного кода имена констант на их значения, удаляет символы конца строки (которые нужны только программисту, чтобы код можно было легко читать, но не нужны компилятору). Что-то препроцессор делает поумолчанию, а какие-то его действия программируются с помощью специальных директив в исходном коде. Директивы препроцессора начинаются со знака # и заканчиваются переходом на новую строку. В отличие от законченного выражения на языке C, в конце директив не надо ставить точку с запятой. Ниже рассматриваются наиболее распространенные директивы препроцессора и некоторые его свойства, но это далеко не все, что может делать препроцессор.

Директива #include С этой директивой мы уже не раз встречались, подключая заголовочные файлы стандартной библиотеки языка, содержащие объявления (прототипы) функций. Когда препроцессор встречает такую директиву, то понимает, что после нее идет имя файла, и включает все содержимое указанного файла в исходный код программы. Поэтому объем кода вашей программы после обработки ее препроцессором может сильно увеличиться.

Если имя файла после директивы #include заключено в угловые скобки (например, ), то поиск заголовочного файла производится в стандартном (специально оговоренном системой) каталоге. Однако в тексте программы может встречаться и такая запись:

#include "ext.h" В таком случае заголовочный файл в первую очередь будет искаться в текущем каталоге.

Таким образом, программист сам может определять заголовочные файлы для своих проектов. Кроме того, можно указывать адрес заголовочного файла:

#include "/home/iam/project10/const.h" Директива #define Символические константы С директивой препроцессора #define мы также уже знакомы. С ее помощью объявляются и определяются так называемые символические константы. Например:

#define N #define HELLO "Hello. Answer the next questions, please." Когда перед компиляцией исходный код будет обработан препроцессором, то все символьные константы (в примере это N и HELLO) в тексте исходного кода на языке C будут заменены на соответствующие им числовые или строковые константы.





Символические константы можно определять в любом месте исходного кода. Однако чтобы переопределить их (изменить значение), необходимо отменить предыдущее определение.

Иначе возникнет если не ошибка, то предупреждение. Для удаления символической константы используют директиву #undef:

#include #define HELLO "Hello. Answer the next questions, please.\n" main () { printf(HELLO);

#undef HELLO #define HELLO "Good day. Tell us about you.\n" printf(HELLO);

} Если в этом примере убрать строку #undef HELLO, то при компиляции в GNU/Linux появляется предупреждение: "HELLO" переопределён.

Символические константы принято писать заглавными буквами. Это только соглашение для удобства чтения кода.

Макросы как усложненные символьные константы С помощью директивы #define можно заменять символьными константами не только числовые и строковые константы, но почти любую часть кода:

#include #define N #define PN printf("\n") #define SUM for(i=0; i

SUM;

printf("%d", sum);

PN;

} Здесь в теле функции main() PN заменяется препроцессором на printf("\n"), а SUM на цикл for. Такие макроопределения (макросы) в первую очередь удобны, когда в программе часто встречается один и тот же код, но выносить его в отдельную функцию нет смысла.

В примере выше PN и SUM являются макросами без аргументов. Однако препроцессор языка программирования C позволяет определять макросы с аргументами:

#include #define DIF(a,b) (a) > (b) (a)-(b) : (b)-(a) main () { int x = 10, y = 30;

printf("%d\n", DIF(67,90));

printf("%d\n", DIF(876-x,90+y));

} Вызов макроса DIV(67,90) в тексте программы приводит к тому, что при обработке программы препроцессором туда подставляется такое выражение (67) > (90) (67)(90) : (90)-(67). В этом выражении вычисляется разница между двумя числами с помощью условного выражения (см. урок 3). В данном случае скобки не нужны. Однако при таком разворачивании (876-x) > (90+y) (876-x)-(90+y) : (90+y)-(876-x) скобки подчеркивают порядок операций. Если бы вместо сложения и вычитания фигурировали операции умножения или деления, то наличие скобок было бы принципиальным.

Обратите внимание, что в имени макроса не должно быть пробелов: DIF(a,b). Первый пробел после идентификатора означает конец символической константы и начало выражения для подстановки.

Задание Напишите программу, содержащую пару макросов: один вычисляет сумму элементов массива, другой выводит элементы массива на экран.

Напишите программу, содержащую макросы с аргументами, вычисляющие площади различных геометрических фигур (например, квадрата, прямоугольника, окружности).

Директивы условной компиляции Так называемая условная компиляция позволяет компилировать или не компилировать части кода в зависимости от наличия символьных констант или их значения.

Условное выражение для препроцессора выглядит в сокращенном варианте так:

#if … … #endif То, что находится между#if и #endif выполняется, если выражение при #if возвращает истину. Находится там могут как директивы препроцессора так и исходный код на языке C.

Следует знать, что условное включение может быть расширено за счет веток #else и #elif.

Рассмотрим несколько примеров.

Если в программе константа N не равна 0, то цикл for выполнится, и массив arr заполнится нулями. Если N определена и равна 0, или не определена вообще, то цикл выполняться не будет:

#include #define N main() { int i, arr[100];

#if N for(i=0; i

printf("%d ", arr[i]);

} #endif printf("\n");

} Если нужно выполнить какой-то код в зависимости от наличия символьной константы, а не ее значения, то директива #if будет выглядеть так:

#if defined(N) Или сокращенно (что тоже самое):

#ifdef N Когда нет уверенности, была ли определена ранее символьная константа, то можно использовать такой код:

#if !defined(N) #define N #endif Таким образом мы определим константу N, если она не была определена ранее. Такие проверки могут встречаться в многофайловых проектах. Выражение препроцессора #if ! defined(N) может быть сокращено так:

#ifndef N Следует знать, что условную компиляцию иногда используют при отладке программного кода, а также с ее помощью компилируют программы под конкретные операционные системы.

Помните, что препроцессор обрабатывает программу до компиляции. В двоичном коде уже отсутствуют какие-либо условные выражения для препроцессора. Поэтому в логическом выражении "препроцессорного if" не должно содержаться переменных, значение которых определяется в момент выполнения программы.

Задание Придумайте и напишите программу, которая может быть скомпилирована по-разному в зависимости от того, определена или нет в ней какая-либо символьная константа.

Константы, определенные препроцессором Препроцессор самостоятельно определяет пять констант. От обычных (определенных программистом) они отличаются наличием пары символов подчеркивания в начале и конце их имени.

• DATE - дата компиляции;

• FILE - имя компилируемого файла;

• LINE - номер текущей строки исходного текста программы;

• STDC - равна 1, если компилятор работает по стандарту ANSI для языка C;

• TIME - время компиляции.

Pages:     | 1 |   ...   | 8 | 9 || 11 | 12 |   ...   | 13 |










© 2011 www.dissers.ru - «Бесплатная электронная библиотека»

Материалы этого сайта размещены для ознакомления, все права принадлежат их авторам.
Если Вы не согласны с тем, что Ваш материал размещён на этом сайте, пожалуйста, напишите нам, мы в течении 1-2 рабочих дней удалим его.