Создание передачи по указателю 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Создание передачи по указателю



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

Указатели передаются в функции, как и обычные значения. Конечно, необходимо объявлять параметр типа указатель. Пример – функция, меняющая значение двух целочисленных аргументов.

void swap(int *x, int *y)

{

int temp;

temp = *x;

*x = *y;

*y = temp;

}

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

#include <stdio.h>

void swap(int *x, int *y);

int main(void)

{

int x, y;

x = 10;

y = 20;

swap(&x, &y);

printf(“%d %d”, x, y);

return 0;

}

В данном примере переменной х присваивается значение 10, а переменной у – 20. Затем вызывается swap() с адресами х и у. Унарный оператор & используется для получения адресов переменных. Поэтому в функцию swap() передаются адреса х и у, а не их значения.

37. Передача массивов в функции

Когда массив используется в качестве аргумента функции, передаётся только адрес массива, а не копия всего массива. При вызове функции с именем массива в функцию передаётся указатель на первый элемент массива. Параметр должен иметь тип, совместимый с указателем. Имеется три способа объявления параметра, предназначенного для получения указателя на массив. Во-первых, он может быть объявлен как массив. Пример:

#include <stdio.h>

void display(int num[10]);

int main(void)

{ int t[10], i;

for(i = 0; i < 10; ++i) t[i] = i;

display(t);

return 0;

}

void display(int num[10])

{ int i;

for(i=0; i<10; ++i) printf(“%d “, num[i]);

}

Хотя параметр num объявляется как целочисленный массив из 10 элементов, С автоматически преобразует его указателю на целочисленный тип данных.

Следующий способ состоит в объявлении параметра для указания на безразмерный массив, как показано ниже:

void display(int num[ ])

{ int i;

for(i=0; i<10; ++i) printf(“%d “, num[i]);

}

где num объявлен как целочисленный массив неизвестного размера. Поскольку С не предоставляет проверку границ массива, настоящий размер массива не имеет никакого отношения к параметру.

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

void display(int *num)

{ int i;

for(i=0; i<10; ++i) printf(“%d “, num[i]);

}

Важно понять, что при использовании массива в качестве аргумента функции происходит передача в его функцию его адреса. Это означает, что код внутри функции может изменять значение массива, используемого при вызове.

При передаче многомерных массивов функции следует определить все размерности, кроме самой левой, это нужно компилятору для корректной индексации массива. Например, если массив определён как

int m[4][5][6][7];

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

int func1(int d[][5][6][7])

{

}

 

38. Аргументы функции main()

По стандарту поддерживаются два агрумента main() – это argc и argv. Они позволяют передавать аргументы командной строки в программу. Аргументы командной строки – это информация, следующая за именем программы в командной строке операционной системы.

Параметр argc содержит число аргументов командной строки и является целым числом. Он всегда больше либо равен 1, поскольку имя программы квалифицируется как первый аргумент. Параметр argv – это указатель на массив символьных указателей. Каждый элемент данного массива указывает на аргумент командной строки. Все аргументы командной строки – это строки, все числа конвертируются программой во внутренний формат. Следующая программа выводит “Hello”, а затем имя пользователя, если его набрать прямо за именем программы:

#include <stdio.h>

int main(int argc, char *argv[])

{ if(argc!=2) {

printf(“You forgot to type your name\n”);

return 1;

}

printf(“Hello%s”, argv[1]);

return 0;

}

Аргументы командной строки должны отделяться пробелами или табуляциями. Запятые, точки с запятыми и им подобные символы не рассматриваются как разделители. Если необходимо передать строку, содержащую пробелы или табуляции в виде одного аргумента, то её следует заключить в двойные кавычки. Например:

“this is a test”

Пример по использованию аргументов командной строки. Программа отсчитывает в обратном порядке от значения, указанного в командной строке и подаёт сигнал. Если в качестве второго аргумента присутствует строка “display”, то на экране будет отображаться счётчик.

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

int main(int argc, char *argv[])

{ int disp, count;

if(argv<2) {

printf(“You must enter the length of count\n”);

printf(“on command line. Try again.\n”);

return 1;

}

if(argc == 3 &&!strcmp(argv[2],”display”)) disp = 1;

else disp = 0;

for(count=atoi(argv[1]); count; --count)

if (disp) printf(“%d “, count);

printf(“%c”, ‘\a’);

return 0;

}

39. Возврат указателей

Функции могут возвращать указатели также как и любые другие типы. Важно помнить, что указатели – это не беззнаковые целые. Это адрес памяти некоторого типа данных, у них другая арифметика.

Пример – функция, возвращающая указатель на символ в месте, где найдено первое вхождение символа в строке:

char *match(char c, char *s)

{ register int count;

count = 0;

while(c!=s[count] && s[count]) count++;

if(s[count]) return (&s[count]);

else return NULL;

}

Функция match() пытается вернуть указатель на позицию в строке, где первый раз найден символ. Если не найдено соответствие, возвращается указатель, содержащий NULL.

40. Рекурсия

В С функции могут вызывать сами себя. Простой пример.

int factr(int n)

{ int answer;

if(n == 1) return 1;

answer = factr(n-1)*n;

return (answer);

}

 

41. Указатели на функции

Хотя функция – это не переменная, она имеет физическое положение в памяти, которое может быть присвоено указателю. Адрес, присвоенный указателю, является входной точкой в функцию. Указатель может использоваться вместо имени функции. Он также позволяет передавать функции как обычные аргументы в другие функции.

Адрес функции получается при использовании имени функции без скобок и аргументов. Пример:

#include <stdio.h>

#include <string.h>

#include <stdlib.h>

#include <math.h>

#define PI 3.1415

double del2(double a, double b, double eps, double (*f)(double));

int main(void)

{

double (*p)(double);

p = sin;

double a = -PI/2, b = PI/2, eps = 0.0001;

printf("Root = %lf\n", del2(a,b,eps,p));

printf("Root = %lf\n", del2(a,b,eps,sin));

return 0;

}

double del2(double a, double b, double eps, double (*f)(double))

{

if(b<a) {

puts("Left bigger than right");

exit(0);

}

if(eps <= 0) {

puts("Eps <= 0");

exit(0);

}

if(f(a) == 0) return a;

if(f(b) == 0) return b;

while(b-a >= eps) {

double x=(a+b)/2;

if(f(a)*f(x) < 0.) a=x;

else if(f(x) == 0.) return x;

else b=x;

}

return (a+b)/2;

}

Когда вызывается del2(), ей передаются числа с плавающей точкой двойной точности и один указатель на функцию. Скобки вокруг *f необходимы для правильной интерпретации компилятором данного выражения.

При объявлении указателя на функцию можно использовать прототип, оставив имена параметров пустыми.

Рассмотрим работу функции sin() в функции del2(). Оператор

if(f(a) == 0) return a;

осуществляет вызов функции, в данном случае sin(), с помощью f, который указывает на данную функцию. Вызов происходит с аргументом a.

Бывают моменты, когда выгодно передавать функции в процедуры или хранить массивы функций. Пример:

#include <stdlib.h>

#include <stdio.h>

#include <conio.h>

#include <string.h>

void enter(void), del(void); void review(void), quit(void);

int menu(void);

void (*options[])(void) = {

enter,

del,

review,

quit

};

int main(void)

{

int i;

i = menu();

(*options[i])();

getchar();

return 0;

}

int menu(void)

{

char ch;

do {

printf("1. Enter\n");

printf("2. Delete\n");

printf("3. Review\n");

printf("4. Quit\n");

printf("Select a number:");

ch = getche();

printf("\n");

}while(!strchr("1234", ch));

return ch-49;

}

void enter(void)

{ printf("In enter.");}

void del(void)

{ printf("In del.");}

void review(void)

{ printf("In review.");}

void quit(void)

{ printf("In quit.");

exit(0);

}

 

 

42. Структуры

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

Фрагмент кода объявляет структуру, определяющую имя и адрес. Ключевое слово struct сообщает компилятору об объявлении структуры.

struct addr {

char name[30];

char street[40];

char city[20];

char region[3];

unsigned long int zip;

};

Объявление завершается точкой с запятой, поскольку объявление структуры – это оператор. Имя структуры addr идентифицирует структуру данных и является спецификатором типа. Имя структуры часто используют как ярлык.

Пока не создано никакой переменной. Определена только форма данных. Для объявления переменной, соответствующей данной структуре, следует написать:

addr addr_info; //C++

struct addr addr_info; //C

Когда объявлена структурная переменная, компилятор автоматически выделяет необходимый участок памяти для размещения всех её членов.

При объявлении структуры можно одновременно объявить одну или несколько переменных. Например:

struct addr {

char name[30];

char street[40];

char city[20];

char region[3];

unsigned long int zip;

} addr_info, binfo, cinfo;

объявляет структуру addr и переменные addr_info, binfo, cinfo данного типа.

Если необходима только одна структурная переменная, то нет необходимости в ярлыке структуры. Это означает, что

struct {

char name[30];

char street[40];

char city[20];

char region[3];

unsigned long int zip;

} addr_info;

объявляет одну переменную addr_info с типом определённым предшествующей ей структурой.

Стандартный вид объявления структуры следующий:

struct ярлык {

тип имя_переменной;

тип имя_переменной;

тип имя_переменной;

} структурные переменные;

43. Доступ к членам структуры

Доступ к отдельным членам структуры осуществляется с помощью оператора. («точка»). Например, следующий фрагмент кода присваивает члену zip структурной переменной addr_info значение 12345:

addr_info.zip = 12345;

Ко всем членам структуры доступ осуществляется точно таким же способом. Стандартный вид доступа следующий:

имя_структуры.имя_члена

Таким образом, массив символов addr_info.name может использоваться в fgets():

fgets(addr_info.name,30,stdin);

Для доступа к отдельным элементам addr_info.name можно использовать индекс. Например:

register int t;

for(t=0; addr_info.name[t]; ++t) putchar(addr_info.name[t]);

44. Присваивание структур

Информация, содержащаяся в одной структуре, может быть присвоена другой структуре того же типа с помощью одиночного оператора присваивания, то есть не нужно присваивать значение каждого члена по отдельности. Следующая программа демонстрирует присваивание структур:

#include <stdio.h>

int main()

{

struct ab{

int a;

int b;

} x, y;

x.a = 10;

x.b = 20;

y = x;

printf(“Contents of y: %d %d.”, y.a, y.b);

return 0;

}

После присваивания y.a и y.b будут содержать значения 10 и 20 соответственно.

45. Массивы структур

Для объявления массива структур следует сначала определить структуру, а затем объявить массив переменных данного типа. Например, для объявления 100-элементного массива структур типа addr, которая была ранее определена в данной лекции, следует написать:

struct addr addr_info[100];

Для доступа к отдельным структурам массива addr_info следует проиндексировать имя массива. Например, для вывода содержимого поля zip третьей структуры, следует написать:

printf(“%ld”, addr_info[2].zip);

Как и массивы переменных, массивы структур индексируются с 0.

46. Указатели на структуры

Указатели на структуры объявляются с помощью символа *,стоящего перед именем экземпляра структуры. Например, указатель addr_pointer на структуру addr объявляется так:

struct addr *addr_pointer;

Для того чтобы определить адрес структуры, достаточно поместить перед ее именем оператор &. Рассмотрим пример:

struct bal{

float balance;

char name[80];

}person;

struct bal *p; /создаем указатель на структуру/

p=&person; /присваиваем указателю p адрес структуры person/

Для доступа к членам структуры с помощью указателя на структуру следует использовать оператор «стрелка» ->.

p->balance /ссылка на поле баланс/

 

47. Битовые поля

Если ограничено место для хранения информации, можно сохранить несколько переменных в одном байте.

Некоторые интерфейсы устройств передают информацию, закодировав биты в один байт.

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

Стандартный вид объявления битовых полей следующий:

struct имя структуры {

тип имя1:длина;

тип имя2:длина;

тип имяN:длина; };

Пример

struct device {

unsigned active: 1;

unsigned ready: 1;

unsigned xmt_error: 1;

} dev_code;

Битовые поля имеют некоторые ограничения. Нельзя получить адрес переменной битового поля. Переменные битового поля не могут помещаться в массив. Переходя с компьютера на компьютер, нельзя быть уверенным в порядке следования бит (слева направо или справа налево). Любой код, использующий битовые поля, зависит от компьютера.

48. Объединения

Объединение- область памяти, которая используется для хранения переменных разных типов. Объединение позволяет интерпретировать один и тот же набор битов по-разному. Общий вид объявления объединения:

union имя_типа_объединения

{

тип имя_члена;

тип имя_члена;

.

}имена_экземпляров_объединения;

Например:

union union_type{

int i;

char ch[4];

};

49. Перечисления

Перечисления – это набор именованных целочисленных констант, определяющий все допустимые значения, которые может принимать переменная.

enum ярлык { список перечислений } список переменных;

enum coin{ penny, nickel, dime, quarter, half_dollar, dollar};

coin money;

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

printf(“The value of quarter is %d “, quarter);

Если явно не проводить инициализацию, значение первого имени перечисления будет 0, второго 1 и т.д.

Можно определить значение одного или нескольких символов, использую инициализатор. Это делается путём помещения за символом знака равенства и целочисленного значения. При использовании инициализатора, имена, следующие за инициализированным, получают значение большее на 1, чем указанное перед этим.

enum coin{ penny, nickel, dime, quarter=100,

half_dollar, dollar};

Теперь имена получат следующие значения:

penny 0

nickel 1

dime 2

quarter 100

half_dollar 101

dollar 102

50. Использование typedef

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

Общий вид операторв typedef:

typedef тип имя;

Тип- допутимый тип, имя-псевдоним этого типа.

Например, float можно переименовать следующим образом:

typedef float balance;

51. Ввод, вывод, потоки и файлы

Потоки и файлы

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

Текстовые потоки

Текстовые потоки – это последовательности символов. В текстовых потоках некоторые символы могут преобразовываться согласно требованиям среды.

Двоичные потоки

Двоичные потоки – это последовательности байт, имеющих однозначное соответствие с байтами во внешнем устройстве. Тем не менее, может добавляться некоторое количество нулевых байт к двоичному потоку. Эти нулевые байты могут использоваться для унификации представления информации, например для заполнения сектора диска.

Файлы

В С файлы – это логическая концепция, применимая ко всему, начиная от дисковых файлов и заканчивая терминалами. Поток связывается с конкретным файлом с помощью операции открытия. Если файл открыт, то может осуществляться обмен между файлом и программой.

Не все файлы имеют одинаковые возможности.

В начале работы программы по стандарту ANSI C открыты три предопределённых текстовых потока: stdin, stdout, stderr.

 

Поток Устройство
stdin Клавиатура
stdout Экран
stderr Экран

 

 

52. Форматированный консольный ввод-вывод

Стандартная библиотека С содержит две функции, выполняющие форматированный ввод и вывод стандартных типов данных: printf() и scanf(). Функция printf() используется для вывода данных на консоль, scanf() – для чтения данных с консоли. Как printf(), таки scanf() могут работать с любыми стандартными типами, включая символы, строки и числа.

Функция printf() имеет следующий прототип:

int printf(const char * форматная_строка, …);

Первый аргумент форматная_строка определяет способ вывода последующих аргументов. Он содержит два типа элементов: символы, выводимые на экран, и спецификаторы формата, определяющие способ вывода аргументов, следующих за форматной строкой.

Код %c %d %i %e %E %f %g   %G   %o %s %u %x %X %p %n   %% Формат Символ Знаковое десятичное целое число Знаковое десятичное целое число Научная нотация (e – строчная) Научная нотация (E – строчная) Десятичное число с плавающей точкой Использует или %e, или %f, соответствующее более короткому представлению Использует или %E, или %f, соответствующее более короткому представлению Беззнаковое восьмеричное число Строка символов Беззнаковое десятичное число Беззнаковое шестнадцатиричное число (строчные буквы) Беззнаковое шестнадцатиричное число (прописные буквы) Вывод указателя Ассоциированный аргумент – это указатель на целое, в которое записывается число уже выведенных символов Выводит знак %

Функция printf() возвращает число напечатанных символов. В случае ошибки она возвращает EOF. Стандартный ввод с консоли, как правило, осуществляется с помощью scanf(). Она читает все стандартные типы данных и автоматически преобразует числа к правильному внутреннему формату. Стандартный вид scanf() следующий:

int scanf(const char * форматная_строка, …);

форматная_строка определяет способ чтения значений в переменные, на которые указывает список аргументов. Форматная строка состоит из трёх типов символов:

- Спецификаторы формата

- Специальные символы

- Стандартные символы

Спецификаторы формата перечислены в таблице

Код %c %d %i %e %f %g %o %s %x %p %n %u %[] Значение Читает одиночные символы Читает десятичное число Читает десятичное число Читает число с плавающей запятой Читает число с плавающей запятой Читает число с плавающей запятой Читает восьмеричное число Читает строку Читает шестнадцатиричное число Читает указатель Получает целочисленное значение, равное числу прочитанных символов Читает беззнаковое целое Сканирует множество символов

В scanf() следует передавать адреса. Это означает, что все аргументы должны быть указателями на переменные. Например, для чтения целого в переменную count надо использовать следующий вызов scanf():

scanf(“%d”, &count);

Печать символов

Для печати отдельных символов используется %c. Для печати строки %s.

Вывод чисел

Пример

#include <stdio.h>

int main(void)

{

double f;

 

for(f=1.0; f<1.0e+10; f=f*10)

printf("%g ", f);

 

getchar();

 

unsigned int num;

 

for(num=0; num<255; num++) {

printf("%o ", num);

printf("%x ", num);

printf("%X ", num);

}

getchar();

return 0;

}

Вывод адресов

Пример

#include <stdio.h>

int main(void)

{

/* Вывод адресов */

int sample;

printf("%p ", &sample);

getchar();

return 0;

}

 



Поделиться:


Последнее изменение этой страницы: 2017-02-06; просмотров: 163; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 18.116.40.53 (0.16 с.)