Читайте также: |
|
2.
Язык С не обеспечивает никаких встроенных средств для выполнения таких общих операций как ввод-вывод, управление памятью, обработка строк, и т.п.. Вместо этого, такие средства определены в стандартной библиотеке, которую Вы подключаете к вашим программам. Библиотека GNU C, описанная в этом документе, содержит описание всех библиотечных функций, которые определены в соответствии c ANSI C стандартом, учитывая дополнительные особенности, специфические для POSIX-стандарта операционной системы UNIX, и расширений, специфических для GNU-разработок.
Цель этого руководства состоит в том, чтобы сообщить Вам, как использовать средства GNU библиотеки. Мы упомянули особенности ее стандартов, чтобы помочь Вам разобраться с вещами, которые являются потенциально непереносимыми на другие системы. Но переносимость не является основным вопросом данного руководства.
Если мы в этом руководстве описываем что-нибудь как функцию, то она может иметь и макроопределение. Обычно это не имеет никакого значение, потому что ваше макроопределение делает ту же самую вещь, что и функция. В частности, макро-эквиваленты для библиотечных функций вычисляют параметры ровно один раз, так же образом, как и при обращение к функции. Основная причина для этих макроопределений состоит в том, что иногда они могутреализовывать встроенное расширение, которое является значительно быстрее, чем фактическое обращение к функции. Взятие адреса библиотечной функции работает даже, если она определена как макрокоманда, потому что, в этом контексте, имя функции не сопровождается левой круглой скобкой, которая является синтаксически необходимой, чтобы распознать макрообращение. Вы можете иногда не использовать макроопределение функции, возможно, чтобы сделать вашу программу более простой или для отладки. Имеются два способа, сделать это:
Вы можете избегать макроопределения, включая имя функции в круглых скобках. Это работает, потому что имя функции не появляется в синтаксическом контексте, где оно распознаваемо как макрообращение. Вы можете подавить любое макроопределение для целого исходного файла, используя директиву препроцессора "#undef ", если в явно описании средства не указано обратное.Например, предположим, что заголовочный файл "stdlib.h" объявляет функцию abs как
extern int abs (int);
и также обеспечивает макроопределение для abs. Тогда при выполнении:
#include < stdlib.h >
int f (int * i) {return (abs (++ * i));}
cсылка на abs может относиться или к макрокоманде или функции. С другой стороны, в каждом из следующих примеров производится ссылка на функцию, а не на макрос.
#include < stdlib.h >
int g (int * i) {return ((abs) (++ * i));}
#undef abs
int h (int *i) { return (abs (++*i)); }
Так как макроопределения, дублирующие функции, ведут себя в точно так же, как фактическая версия функции, удаление макроопределений обычно делает вашу программу медленнее.
Зарезервированные имена
Имена всех библиотечных типов, макрокоманд, переменных и функций, которые исходят из ANSI C стандарта, зарезервированы безоговорочно; ваша программа не имеет права переопределять эти имена. Все другие библиотечные имена зарезервированы, если ваша программа явно включает заголовочный файл, который определяет или объявляет их. Для этих ограничений имеются несколько причин: Другие люди, читая ваш код могут запутаться, если Вы использовали функцию exit, чтобы делать что - нибудь полностью отличное от того, что делает стандартная функция выхода. Предотвращение этой ситуации помогает делать ваши программы более простыми, для понимания и способствует модульности. Это лишает пользователя возможности случайно переопределить библиотечную функцию, которая вызывается в соответствии c другими библиотечными функциями. Если переопределение позволялось, те другие функции не будут работать.
Имена, которые начинаются с "SIG", сопровождаемые символом верхнего регистра, зарезервированы для дополнительных имен сигнала. См. раздел 21.2 [Стандартные сигналы]. Имена, которые начинаются с "SIG_" сопровождаемые символом верхнего регистра, зарезервированы для дополнительных действий сигнала. См. раздел 21.3.1 [Обработка видеосигнала]. Имена, начинающиеся со "str", "mem", или "wcs", сопровождаемые символом нижнего регистра, зарезервированы для дополнительной строки и функций массива. См. Главу 5 [Уилиты для работы со строками и массивами].
Имена, которые заканчиваются на "_t" зарезервированы для дополнительных имен типа.
Кроме того, некоторые индивидуальные заголовочные файлы резервируют имена вне тех, что они фактически определяют. Если ваша программа включает такой специфический заголовочный файл, то у Вас есть повод для беспокойства.
Заголовочный файл "dirent.h" резервирует имена с предстоящим "d_".
Заголовочный файл "fcntl.h" резервирует имена с предстоящими "l_", "F_", "O_", и "S_".
Заголовочный файл "grp.h" резервирует имена с предстоящим "gr_".
Заголовочный файл "limits.h" резервирует имена, с приписанным "_MAX".
Заголовочный файл "pwd.h" резервирует имена с предстоящим "pw_".
Заголовочный файл "signal.h" резервирует имена с предстоящим "sa_" и "SA_".
Заголовочный файл "sys/stat.h" резервирует имена с предстоящим "st_" и "S_".
Заголовочный файл "sys/times.h" резервирует имена с предстоящим "tms_".
Заголовочный файл "termios.h" резервирует имена с предстоящим "c_", "V", "I", "O", и "TC", а также имена с предстоящим "B", сопровождаемым цифрой.
Макрокоманды управления особенностями. Управляя макрокомандами, Вы определяете точный набор особенностей, доступный, когда при компиляции исходного файла. Если Вы компилируете ваши программы, используя "gcc -ansi", Вы получаете только ANSI C библиотечные особенности, если Вы явно не запрашиваете дополнительные особенности, определяя одну или большее количество макрокоманд особенностей. См. раздел "Опции Команд GNU CC" в GNU CC Руководстве, для уточнения информации относительно опций GCC. Вы должны определить эти макрокоманды, используя директивы препроцессора "#define" в начале ваших файлов. Эти директивы должны стоять перед любым #include заголовочного файла системы. Лучше всего указывать их самыми первыми в файле, только после комментариев. Вы могли бы также использовать опцию `-D' для GCC, но лучше, если Вы создаете исходные файлы с указанием их собственного
Если Вы определяете эту макрокоманду, функциональные возможности из SVID, включены также как ANSI C, POSIX.1, и POSIX.2.
_GNU_SOURCE (макрос)
Если Вы определяете эту макрокоманду, включены все: ANSI C, POSIX.1, POSIX.2, BSD, SVID, и расширения GNU. В случаях, конфликтов POSIX.1 с BSD, POSIX определения берут верх. Если Вы хотите получить полный эффект _GNU_SOURCE, но установить BSD определениям больший приоритет необходимо примеенить следующую последовательность определений:
#define _GNU_SOURCE
#define _BSD_SOURCE
#define _SVID_SOURCE
Обратите внимание, что, если Вы делаете это, Вы должны компоновать вашу программу с BSD библиотекой совместимости, указывая опцию ` lbsd-compat' транслятору или компоновщику. Обратите внимание: если Вы забудете сделать это, Вы можете получить очень странные ошибки во время выполнения. Мы рекомендуем, чтобы Вы использовали _GNU_SOURCE в новых программах. Если Вы не определяете опцию `-ansi' для GCC и не определяете никакую из этих макрокоманд явнно, то _GNU_SOURCE дает тот же эффект. Когда Вы определяете макрокоманду, чтобы запросить больший класс особенностей, безобидно определить кроме того макрокоманду для подмножества этих особенностей. Например, если Вы определяете _POSIX_ C_SOURCE, то определение _POSIX_SOURCE не имеет никакого эффекта. Аналогично, если Вы определяете _GNU_SOURCE, определяя затем либо _POSIX_SOURCE либо _POSIX_C_SOURCE либо _SVID_SOURCE, также не будет никакого эффекта. Обратите внимание, что особенности _BSD_SOURCE не подмножество любой другой из обеспечиваемых макрокоманд особенностей. Потому что эта макрокоманда определяет особенности BSD, которые берут верх над особенностями POSIX, которые запрашиваются другими макрокомандами. По этой причине, определение _BSD_SOURCE в дополнение к другим макрокомандам особенностей заставляет особенности BSD брать верх при конфликтое с особенностями 2.POSIX. auto double int struct break else long switch
register tupedef char extern return void case float
unsigned default for signed union do if sizeof
volatile continue enum short while
3.
Структура программы.
Программа на языке С++ представляет собой набор функций. Одна из функций должна иметь имя main. Операционная система передает управление в программу пользователя на функцию с этим именем и тем самым начинается вы-полнение программы. От функций в программе функция main отличается тем, что ее нельзя вызвать изнутри программы, а ее параметры, если они существуют, обычно задаются ОС, хотя это необязательно, main бывает первой функцией в тексте программы.
main ()
{
...
return 0;
}
Если предположить, что main- первая функция, определенная в программе на языке С++, то поскольку ни одна функция не может содержать определения другой функции, следом в тексте будут располагаться определения вспомогатель-ных функций, «неглавных» функций. Их может быть различное количество.
main ()
{
...
return 0;
}
function 1 ()
{
}
function 2 ()
{
}
...
function n ()
{
}
Функции могут быть описаны в произвольном порядке. Удобно располо-жить их по алфавиту или сгруппировать по определенному признаку.
Единица трансляции, он же исходный модуль, -- один файл с расширением.c или
..cpp (или другими), подлежащий трансляции с помощью компилятора.
Важно понимать, что КАЖДЫЙ исходный модуль (единица трансляции), ОБРАБАТЫВАЕТСЯ
КОМПИЛЯТОРОМ ОТДЕЛЬНО ОТ ОСТАЛЬНЫХ.
Транслятор — программа или техническое средство, выполняющее трансляцию программы.
Транслятор обычно выполняет также диагностику ошибок, формирует словари идентификаторов, выдаёт для печати тексты программы и т. д.
Трансляция программы — преобразование программы, представленной на одном из языков программирования, в программу на другом языке и, в определённом смысле, равносильную первой.
Язык, на котором представлена входная программа, называется исходным языком, а сама программа — исходным кодом. Выходной язык называется целевым языком или объектным кодом.
4.
Общий вид функции
В общем виде функция выглядит следующим образом:
возвр-тип имя-функции(список параметров)
{
тело функции
}
возвр-тип определяет тип данного, возвращаемого функцией[1]. Функция может возвращать любой тип данных, за исключением массивов список параметров — это список, элементы которого отделяются друг от друга запятыми. Каждый такой элемент состоит из имени переменной и ее типа данных. При вызове функции параметры принимают значения аргументов. Функция может быть и без параметров, тогда их список будет пустым. Такой пустой список можно указать в явном виде, поместив для этого внутри скобок ключевое слово void.
В объявлениях (декларациях) переменных можно объявить (декларировать) несколько переменных одного и того же типа, используя для этого список одних только имен, элементы которого отделены друг от друга запятыми. А все параметры функций, наоборот, должны объявляться отдельно, причем для каждого из них надо указывать и тип, и имя. То есть в общем виде список объявлений параметров должен выглядеть следующим образом:
f(тип имя_переменной1, тип имя_переменной2,..., тип имя_переменнойN)
Вот, например, два объявления параметров функций, первое из которых правильное, а второе — нет:
f(int i, int k, int j) /* правильное */
f(int i, k, float j) /* неправильное, у переменной k должен быть собственный спецификатор типа */
В языке правила работы с областями действия — это правила, которые определяют, известен ли фрагменту кода другой фрагмент кода или данных, или имеет ли он доступ к этому другому фрагменту. Об областях действия, определяемых в языке С, говорилось в главе 2. Здесь же мы более подробно рассмотрим одну специальную область действия — ту, которая определяется функцией. Каждая функция представляет собой конечный блок кода. Таким образом, она определяет область действия этого блока. Это значит, что код функции является закрытым и недоступным ни для какого выражения из любой другой функции, если только не выполняется вызов содержащей его функции. (Например, нельзя перейти в середину другой функции с помощью goto.) Код, который составляет тело функции, скрыт от остальной части программы, и если он не использует глобальных переменных, то не может воздействовать на другие части программы или, наоборот, подвергаться воздействию с их стороны. Иначе говоря, код и данные, определенные внутри одной функции, без глобальных переменных не могут воздействовать на код и данные внутри другой функции, так как у любых двух разных функций разные области действия.
5.
Аргументы функции
Если функция должна принимать аргументы, то в ее объявлении следует декларировать параметры, которые примут значения этих аргументов. Как видно из объявления следующей функции, объявления параметров стоят после имени функции.
/* Возвращает 1, если символ c входит в строку s;
и 0 в противном случае. */
int is_in(char *s, char c)
{
while(*s)
if(*s==c) return 1;
else s++;
return 0;
}
Функция is_in() имеет два параметра: s и d. Если символ c входит в строку s, то эта функция возвращает 1, в противном случае она возвращает 0.
Хотя параметры выполняют специальную задачу, — принимают значения аргументов, передаваемых функции, — они все равно ведут себя так, как и другие локальные переменные. Формальным параметрам функции, например, можно присваивать какие-либо значения или использовать эти параметры в каких-либо выражениях.
Вызовы по значению и по ссылке
В языках программирования имеется два способа передачи значений подпрограмме. Первый из них — вызов по значению. При его применении в формальный параметр подпрограммы копируется значение аргумента. В таком случае изменения параметра на аргумент не влияют.
Вторым способом передачи аргументов подпрограмме является вызов по ссылке. При его применении в параметр копируется адрес аргумента. Это значит, что, в отличие от вызова по значению, изменения значения параметра приводят к точно таким же изменениям значения аргумента.
За небольшим количеством исключений, в языке С для передачи аргументов используется вызов по значению. Обычно это означает, что код, находящийся внутри функции, не может изменять значений аргументов, которые использовались при вызове функции.
Проанализируйте следующую программу:
#include <stdio.h>
int sqr(int x);
int main(void)
{
int t=10;
printf("%d %d", sqr(t), t);
return 0;
}
int sqr(int x)
{
x = x*x;
return(x);
}
В этом примере в параметр х копируется 10 — значение аргумента для sqr(). Когда выполняется присваивание х=х*х, модифицируется только локальная переменная х. А значение переменной t, использованной в качестве аргумента при вызове sqr(), по-прежнему остается равным 10. Поэтому выведено будет следующее: 100.10.
Помните, что именно копия значения аргумента передается в функцию. А то, что происходит внутри функции, не влияет на значение переменной, которая была использована при вызове в качестве аргумента.
Вызов по ссылке
Хотя в С для передачи параметров применяется вызов по значению, можно создать вызов и по ссылке, передавая не сам аргумент, а указатель на него[1]. Так как функции передается адрес аргумента, то ее внутренний код в состоянии изменить значение этого аргумента, находящегося, между прочим, за пределами самой функции.
Указатель передается функции так, как и любой другой аргумент. Конечно, в таком случае параметр следует декларировать как один из типов указателей. Это можно увидеть на примере функции swap(), которая меняет местами значения двух целых переменных, на которые указывают аргументы этой функции:
void swap(int *x, int *y)
{
int temp;
temp = *x; /* сохранить значение по адресу x */
*x = *y; /* поместить y в x */
*y = temp; /* поместить x в y */
}
Функция swap() может выполнять обмен значениями двух переменных, на которые указывают х и y, потому что передаются их адреса, а не значения. Внутри функции, используя стандартные операции с указателями, можно получить доступ к содержимому переменных и провести обмен их значений[2].
Помните, что swap() (или любую другую функцию, в которой используются параметры в виде указателей) необходимо вызывать вместе с адресами аргументов[3]. Следующая программа показывает, как надо правильно вызывать swap():
#include <stdio.h>
void swap(int *x, int *y);
int main(void)
{
int i, j;
i = 10;
j = 20;
printf("i и j перед обменом значениями: %d %d\n", i, j);
swap(&i, &j); /* передать адреса переменных i и j */
printf("i и j после обмена значениями: %d %d\n", i, j);
return 0;
}
void swap(int *x, int *y)
{
int temp;
temp = *x; /* сохранить значение по адресу x */
*x = *y; /* поместить y в x */
*y = temp; /* поместить x в y */
}
И вот что вывела эта программа:
i и j перед обменом значениями: 10 20
i и j после обмена значениями: 20 10
В программе переменной i присваивается значение 10, а переменной j — значение 20. Затем вызывается функция swap() с адресами этих переменных. (Для получения адреса каждой из переменных используется унарный оператор &.) Поэтому в swap() передаются адреса переменных i и j, а не их значения.На заметку Язык C++ при помощи параметров-ссылок дает возможность полностью автоматизировать вызов по ссылке. А в языке С параметры-ссылки не поддерживается
Вызов функций с помощью массивов
Подробно о массивах рассказывалось в главе 4. В настоящем же разделе рассказывается о передаче массивов функциям в качестве аргументов. Этот вопрос рассматривается потому, что эта операция является исключением по отношению к обычной передаче параметров, выполняемой путем вызова по значению[4].
Когда в качестве аргумента функции используется массив, то функции передается его адрес. В этом и состоит исключение по отношению к правилу, которое гласит, что при передаче параметров используется вызов по значению. В случае передачи массива функции ее внутренний код работает с реальным содержимым этого массива и вполне может изменить это содержимое. Проанализируйте, например, функцию print_upper(), которая печатает свой строковый аргумент на верхнем регистре:
#include <stdio.h>
#include <ctype.h>
void print_upper(char *string);
int main(void)
{
char s[80];
printf("Введите строку символов: ");
gets(s);
print_upper(s);
printf("\ns теперь на верхнем регистре: %s", s);
return 0;
}
/* Печатать строку на верхнем регистре. */
void print_upper(char *string)
{
register int t;
for(t=0; string[t]; ++t) {
string[t] = toupper(string[t]);
putchar(string[t]);
}
}
Вот что будет выведено в случае фразы "This is a test." (это тест):
Введите строку символов: This is a test.
THIS IS A TEST.
s теперь в верхнем регистре: THIS IS A TEST.
Правда, эта программа не работает с символами кириллицы.
После вызова print_upper() содержимое массива s в main() переводится в символы верхнего регистра. Если вам это не нужно, программу можно написать следующим образом:
#include <stdio.h>
#include <ctype.h>
void print_upper(char *string);
int main(void)
{
char s[80];
printf("Введите строку символов: ");
gets(s);
print_upper(s);
printf("\ns не изменялась: %s", s);
return 0;
}
void print_upper(char *string)
{
register int t;
for(t=0; string[t]; ++t)
putchar(toupper(string[t]));
}
Вот какой на этот раз получится фраза "This is a test.":
Введите строку символов: This is a test.
THIS IS A TEST.
s не изменилась: This is a test.
На этот раз содержимое массива не изменилось, потому что внутри print_upper() не изменялись его значения.
Классическим примером передачи массивов в функции является стандартная библиотечная функция gets(). Хотя gets(), которая находится в вашей стандартной библиотеке, и более сложная, чем предлагаемая вам версия xgets(), но с помощью функции xgets() вы сможете получить представление о том, как работает gets().
/* Упрощенная версия стандартной библиотечной функции gets(). */
char *xgets(char *s)
{
char ch, *p;
int t;
p = s; /* xgets() возвращает указатель s */
for(t=0; t<80; ++t){
ch = getchar();
switch(ch) {
case '\n':
s[t] = '\0'; /* завершает строку */
return p;
case '\b':
if(t>0) t--;
break;
default:
s[t] = ch;
}
}
s[79] = '\0';
return p;
}
Функцию xgets() следует вызывать с указателем char *. Им, конечно же, может быть имя символьного массива, которое по определению является указателем char *. В самом начале программы xgets() выполняется цикл for от 0 до 80. Это не даст вводить с клавиатуры строки, содержащие более 80 символов. При попытке ввода большего количества символов происходит возврат из функции. (В настоящей функции gets() такого ограничения нет.) Так как в языке С нет встроенной проверки границ, программист должен сам позаботиться, чтобы в любом массиве, используемом при вызове xgets(), помещалось не менее 80 символов. Когда символы вводятся с клавиатуры, они сразу записываются в строку. Если пользователь нажимает клавишу <Backspase>, то счетчик t уменьшается на 1, а из массива удаляется последний символ, введенный перед нажатием этой клавиши. Когда пользователь нажмет <ENTER>, в конец строки запишется нуль, т.е. признак конца строки. Так как массив, использованный для вызова xgets(), модифицируется, то при возврате из функции в нем будут находиться введенные пользователем символы.
Аргументы функции main(): argv и argc
Иногда при запуске программы бывает полезно передать ей какую-либо информацию. Обычно такая информация передается функции main() с помощью аргументов командной строки. Аргумент командной строки — это информация, которая вводится в командной строке операционной системы вслед за именем программы. Например, чтобы запустить компиляцию программы, необходимо в командной строке после подсказки набрать примерно следующее:
cc имя_программы
имя_программы представляет собой аргумент командной строки, он указывает имя той программы, которую вы собираетесь компилировать.
Чтобы принять аргументы командной строки, используются два специальных встроенных аргумента: argc и argv. Параметр argc содержит количество аргументов в командной строке и является целым числом, причем он всегда не меньше 1, потому что первым аргументом считается имя программы. А параметр argv является указателем на массив указателей на строки. В этом массиве каждый элемент указывает на какой-либо аргумент командной строки. Все аргументы командной строки являются строковыми, поэтому преобразование каких бы то ни было чисел в нужный двоичный формат должно быть предусмотрено в программе при ее разработке.
Вот простой пример использования аргумента командной строки. На экран выводятся слово Привет и ваше имя, которое надо указать в виде аргумента командной строки.
6.
согласование по колличеству и по типу наверное. ну по ссылки и по значению.
Ссылка. int aaa(int & b,int * c)
{
if(b==1) return -1;{ else b++; *c=123123}
}
7. ну ф-ции которые размещенны в подключаемых библиотеках, подключаешь и юзаешь, гетс и путс в stdio.h/getch/putch/
7.
Дата добавления: 2015-07-26; просмотров: 77 | Нарушение авторских прав
<== предыдущая страница | | | следующая страница ==> |
Надзор за исполнением законов об образовании и труде несовершеннолетних. | | | Создание библиотеки |