Содержание
- 2. где в качестве выступают ключевые слова, определяющие тип значений, хранимых по адресу, задаваемому переменной-указателем. Примеры: int
- 3. k = &z; l = *k; Причем данные два оператора по своему действию эквивалентны одному оператору
- 4. pr01 (x, y, px, py); } void pr01 (int x, int y, int *px, int *py)
- 5. Программа 20: #include /* Программа с ошибкой */ /* Где она скрыта? */ void main (void)
- 6. Она выдаст на экран сообщение: Выделяемая память для s = 2 Указатель s = 0000 Значение
- 7. Далее идут сообщения об адресах переменных f и q и c помощью последнего вызова функции printf
- 8. Сама проблема задания начального значения переменной-указателю, не противоречащему смысловому содержанию решаемой задачи, может быть решена несколькими
- 9. Программа 21: #include #include void main (void) { extern void printval (int , int , int);
- 10. void printval (int a, int b, int c) { printf ("\n a = %d b =
- 11. начальные значения на отрицательные. Однако получается неожиданный результат a = -155 b = -155 c =
- 12. Следующая распространенная ошибка, связанная с указателями непосредственно связана с присвоением переменной-указателю адресного значения (конкретный адрес какой-либо
- 13. Подобным же образом вводится понятие массива в языке СИ, однако благодаря наличию указателей возможным стало организовать
- 14. В языке СИ по умолчанию массивы индексируются с нуля. Так, например, для 375 элементов массива s
- 15. if ((n NSIZE)) { printf ("\n0 goto l1; } sum = 0; for (i=0; i {
- 16. Здесь важно усвоить, что имя массива – константа-указатель и ему нельзя присваивать значение какой-либо другой переменной-указателя.
- 17. В первом операторе объявляется целочисленный массив l, содержащий 150 элементов. Второй оператор вводит трехмерный массив, состоящий
- 18. Программа 23: #include #define N 5 #define L 4 #define M 3 void main (void) {
- 19. } printf ("\n"); /* Вычисляем элементы матрицы c */ for (i=0; i for (j=0; j {
- 20. Здесь s – переменная целого типа, а p – переменная-указатель на целый тип. Тогда для снятой
- 21. pd = d; e = *pd; поскольку переменная pd указывает на нулевой элемент массива d, то
- 22. Таблица 1.2.1 Операции над указателями Таким образом, описанная группа операций позволяет манипулировать элементами массивов, используя указатели.
- 23. Программа 24: #include #define NSIZE 20 /* Максимальный размер массива */ void main (void) { int
- 24. Для двумерного массива, объявленного оператором int date0 [10][15], I, j; имя массива date0 является указателем на
- 25. { printf ("Введите элемент a[%d][%d] = ", i, j); scanf ("%f", &p); *(*(a+i)+j) = p; }
- 26. printf ("Элемент матрицы c[%d][%d} = %f\n",i, j, *(*(c+i)+j)); } } } Ссылки в качестве параметров функций.
- 29. Скачать презентацию
где в качестве <тип> выступают ключевые слова, определяющие тип значений,
где в качестве <тип> выступают ключевые слова, определяющие тип значений,
Примеры:
int *px, qy, *tq;
char *str0, string1;
short l, *k, z;
В первом операторе идентификаторы px и tq являются ссылками на целое, во втором имя переменной str0 – указателем на символьное, в третьем k является указателем на короткое целое.
Над переменными-указателями в СИ определены операции. Наиболее важная из них – операция обращения по адресу * (иначе ее называют операцией снятия ссылки) и операция определения адреса &. С помощью операции обращения по адресу можно использовать задаваемый адрес для выборки содержимого ячейки, определяемой им. Так, в соответствии с первым оператором описания, из приведенного выше примера можем записать
qy = *tq;
Здесь переменной qy присваивается значение содержимого ячейки памяти ПЭВМ, на которую указывает переменная tq.
Операция же определения адреса & дает адрес памяти операнда, на который она действует. Операндом этой операции всегда должна быть переменная, например:
px = &qy;
Записи вида &(qy+5) или &9 недопустимы. Согласно правилам действия описанных операций справедливо следующее:
k = &z;
l = *k;
Причем данные два оператора по своему действию
k = &z;
l = *k;
Причем данные два оператора по своему действию
l = z;
Итак, операции * и & позволяют Вам определять значения переменных-указателей и считывать значение из ячейки с адресом, который определяется ими.
Для того чтобы понять работу со ссылками и введенными ранее операциями, проанализируем результаты, получаемые с помощью приведенной далее программы:
Программа 19:
#include
void main (void)
{
int x, y, *px, *py;
void pr01 (int, int, int *, int *);
x = 100;
y = 200;
px = &x;
py = &y;
printf ("\n");
pr01 (x, y, px, py);
py = px;
y = x;
pr01 (x, y, px, py);
}
void pr01 (int x,
pr01 (x, y, px, py);
}
void pr01 (int x,
{
printf ("Значение x = %d и значение y = %d\n",x,y);
printf ("Значение адреса x = %p
и значение адреса y = %p\n",px,py);
}
Программа первый раз выведет на экран дисплея с помощью функции pr01 такую информацию:
Значение x = 100 и значение y = 200
Значение адреса x = FFF4 и значение адреса y = FFF2
Во второй раз появится информация вида
Значение x = 100 и значение y = 100
Значение адреса x = FFF4 и значение адреса y = FFF4
Инициализация ссылок. Как уже упоминалось ранее, необходимы определенные навыки и осторожность, аккуратность при работе со ссылками. В большинстве случаев ошибки при работе со ссылками обусловлены инициализацией (присвоением начальных значений) переменных-указателей. Ошибки такого рода могут приводить к серьезным последствиям.
В качестве примера рассмотрим простейшую программу.
Программа 20:
#include
/* Программа с ошибкой */
/* Где она
Программа 20:
#include
/* Программа с ошибкой */
/* Где она
void main (void)
{
int *s, *t, f, q;
/* s = &q; Ошибка здесь (активизируйте оператор) */
*s = 5;
f = 5;
t = &f;
q = f;
printf ("\n");
printf ("Выделяемая память для s = %d\n", sizeof(s));
printf ("Указатель s = %p\n", s);
printf ("Значение по адресу s = %d\n", *s);
printf ("Адрес переменной f = %p\n", &f);
printf ("Адрес переменной q = %p\n", &q);
printf ("Значение по адресу t = %d\n", *t);
}
Она выдаст на экран сообщение:
Выделяемая память для s =
Она выдаст на экран сообщение:
Выделяемая память для s =
Указатель s = 0000
Значение по адресу s = 185
Адрес переменной f = FFF4
Адрес переменной q = FFF2
Значение по адресу t = 5
Согласно тексту данной программы указатели s и t заданы так, что они указывают на переменные типа int, а переменные и объявлены целыми (int). Оператор *s = 185; означает, что по адресу, определяемому переменной-указателем s, помещается значение 185 (поскольку в нем операция * используется как операция снятия ссылки). Далее переменной f присваивается значение 5. Потом с помощью оператора t = &f указателю t присваивается значение адреса переменной f, что с точки зрения синтаксиса языка СИ вполне справедливо. Ссылка t указывает на тип int (т. е. содержит адрес переменной целого типа). По этому адресу уже занесено значение 5 оператором f = 5. Следовательно, значение указателя t со снятой ссылкой *t равно 5. Программа при помощи первого обращения к функции printf выведет на экран монитора строку
Выделяемая память для s = 2
Так как в ней используются близкие указатели, то для их хранения будет отведено по два байта. Затем выводится адрес s в виде целого без знакового числа и за ним значение, помещенное по этому адресу, а именно
Значение по адресу s = 185
Далее идут сообщения об адресах переменных f и q и c
Далее идут сообщения об адресах переменных f и q и c
Однако в данной программе имеется серьезная ошибка, которая может нарушить нормальную работу компьютера. Укажем каким образом она проявляется. Данная ошибка связана с тем, что в программе описан указатель s на переменную типа int. В таком случае компилятор отводит память в два байта для хранения адреса. Однако сама переменная, значение которой должно храниться по этому адресу, не описана и, следовательно, компилятор не отведет соответствующего объема памяти для хранения ее значения. Так как значением является целое число, то для его хранения необходимо два байта оперативной памяти. В подобной ситуации, когда программист хочет по адресу, описываемому ссылкой, поместить целое значение, как в нашем примере, то он сам он сам должен позаботиться о выделении необходимого объема памяти для этой цели. Таким образом, ответственность за инициализацию указателей ложится на самого программиста.
Представленный пример демонстрирует то, что существует проблема инициализации указателей, которую в любой конкретной программе, решающей некоторую прикладную задачу, обязан решать сам программист.
И, наконец, основной вывод, вытекающий из рассмотренного примера, состоит в том, чтобы пользователь (программист) усвоил и надежно зафиксировал в своей памяти то, что любое описание переменной-указателя в программе не приводит к его инициализации разумным значением и также к резервированию памяти для значения переменной, хранящейся по адресу, указанному им.
Сама проблема задания начального значения переменной-указателю, не противоречащему смысловому содержанию
Сама проблема задания начального значения переменной-указателю, не противоречащему смысловому содержанию
Второй способ основан на том, чтобы указателю присвоить адрес уже описанной переменной. Поскольку компилятор отводит память в момент описания, то описанная выше процедура гарантирует, что необходимый объем памяти будет автоматически отведен. Естественно, что после проведения операции снятия ссылки над таким указателем его значение будет совпадать со значением переменной, адрес которой присваивался указателю.
Третий способ таков: указателю присваивается значение другого указателя, который к этому моменту уже инициализирован. При этом одному адресу памяти как бы даются разные имена (идентификаторы указателей). Если значение, на которое будут ссылаться оба указателя, изменится с помощью оператора присваивания, где используется один из указателей, то изменится и значение, получаемое при помощи другого указателя.
Рассмотренная в последнем случае ситуация называется двойным указанием (один объект имеет два различных имени) и может приводить к определенным проблемам. Рассмотрим до некоторой степени следующий искусственный пример, иллюстрирующий возможные неприятности, которые обусловлены двойным указанием.
Программа 21:
#include
#include
void main (void)
{
extern void printval
Программа 21:
#include
#include
void main (void)
{
extern void printval
int *aa = (int *) malloc (sizeof (int)),
*bb = (int *) malloc (sizeof (int)),
*cc = (int *) malloc (sizeof (int)),
*aa = 144;
*bb = 155;
*cc = 166;
printval (*aa, *bb, *cc);
*cc = *aa;
printval (*aa, *bb, *cc);
bb = aa; /* Двойное указание */
printval (*aa, *bb, *cc);
*aa = -144;
*bb = -155;
*cc = -166;
printval (*aa, *bb, *cc);
printf ("\n");
}
void printval (int a, int b, int c)
{
printf ("\n
void printval (int a, int b, int c)
{
printf ("\n
}
Оператор описания типа int *aa = (int *) malloc (sizeof (int)) приводит к тому, что переменная aa описана, как указатель на целое, и начальное значение адреса этого указателя равно адресу, которое возвращается функцией malloc, содержащейся в библиотеке
Результаты работы программы таковы. При первом вызове функции printval будет выведена следующая информация:
a =144 b = 155 c = 166
в соответствии с тремя операторами присваивания, выполненными до этого момента.
Выполнение оператора *cc = *aa приведет к тому, что значения *cc и *aa будут одинаковы, и поэтому в результате второго обращения к функции printval получим
a =144 b = 155 c = 144
как следует из комментария программы, стоящего после оператора bb = aa, выполнение которого будет обозначать, что переменные-указатели bb и aa имеют одно и то же значение, а именно адрес одной и той же ячейки оперативной памяти. Следовательно, операции снятия ссылки, проведенные над bb и aa, дадут один и тот же результат, о чем свидетельствует информация, полученная при третьем обращении к функции printval
a =144 b = 144 c = 144
Далее в программе проводится обновление значений по адресам соответствующих переменным-указателям aa, bb и cc операторами присваивания, которые меняют
начальные значения на отрицательные. Однако получается неожиданный результат
a = -155
начальные значения на отрицательные. Однако получается неожиданный результат
a = -155
такой результат является следствием работы оператора bb = aa потому, что операторы присваивания *aa = -144 и *bb = -155, выполняемые последовательно, обновляют значение по одному и тому же адресу и, следовательно, значение, равное -155, будет выведено с помощью операции снятия ссылки, применяемой к переменным-указателям aa и bb. Такого рода неожиданные результаты являются следствием двойного указания, что, несомненно, демонстрирует важность процедуры инициализации переменных-указателей, которую программист должен проводить аккуратно и с особой тщательностью.
Четвертый способ задания начального значения указателю основан на использовании функции автоматического распределения памяти malloc из библиотеки
void *malloc (int size)
Она отводит size байт памяти. При этом если для запрашиваемой переменной отводимой памяти не достаточно, то она возвращает нулевое значение, в противном случае возвращает указатель на блок выделяемой памяти. После использования выделенной памяти ее необходимо освободить, что осуществляется вызовом функции free, описанной следующим образом
void free (void *block);
Эта функция применяется в паре с функцией malloc.
Следующая распространенная ошибка, связанная с указателями непосредственно связана с присвоением
Следующая распространенная ошибка, связанная с указателями непосредственно связана с присвоением
Еще один вид ошибок связан с возвратом адресов автоматических переменных функциями. Это так же может привести к сбою в работе программного обеспечения, установленного на Вашем компьютере.
Отметим, что применение переменных-указателей требует жесткой дисциплины при выполнении правил их использования, а также особой тщательности и внимания при анализе различного рода программных ситуаций.
Ссылки и массивы. Достаточно часто при решении конкретных задач возникает ситуация, когда набор значений одной и той же величины должен выступать в роли одного и того же объекта программы. Известно, что для скалярных величин одному значению в программе соответствует какой-либо идентификатор. Ну, а как же быть выше упомянутом случае? Для этого во всех языках программирования вводится сложный тип данных – массив. Под массивом понимается некоторый однотипный набор значений, который в программе описывается одним именем, а для того, чтобы указать на один из элементов данного набора, как правило, используется индексная переменная. Ее значение определяет порядковый номер элемента в рассматриваемом наборе.
Подобным же образом вводится понятие массива в языке СИ, однако
Подобным же образом вводится понятие массива в языке СИ, однако
Согласно основному правилу языка СИ, все переменные должны быть описаны, в том числе и массивы, для которых оператор описания имеет вид
<тип> имя массива [размер массива];
В данной конструкции под словом <тип> понимается одно из ключевых слов, указывающий на тип принимаемых значений элементами массива. Под именем массива следует понимать идентификатор, который как бы выделяет его среди других объектов программы. В квадратных скобках, следующих за идентификатором, указывается размер массива – количество элементов, входящих в него. Приведем примеры операторов описания массивов:
double s[375];
int i[131], k[15];
char text [38], text1 [12];
В первом операторе массив с именем s содержит в себе 375 элементов, которые принимают значения типа double. Компилятор обрабатывая такой оператор, отведет для хранения всех элементов массива память объемом в 3000 байт, так как для хранения одного элемента требуется 8 байт. При обработке второго оператора будут введены в программе два массива, состоящих из 131 и 15 элементов и принимающих целочисленные значения, с именами i и k соответственно. Для хранения всех элементов массива i будет отведена память объемом 262, а для массива k – 30 байт. Как уже говорилось массивы индексируются для того, чтобы обеспечить возможность доступа к его отдельному элементу.
В языке СИ по умолчанию массивы индексируются с нуля. Так,
В языке СИ по умолчанию массивы индексируются с нуля. Так,
Примеры:
i [35] – тридцать шестой элемент массива i.
text [8] – девятый элемент массива text.
Таким образом, с помощью индексной переменной, принимающей целочисленные значения, можно обращаться в программе к отдельному элементу любого массива. Такая технология работы с массивами и его элементами используется во всех языках программирования высокого уровня: fortran, pascal, basic и другие. Приведем примеры программ, иллюстрирующие такую технологию работы с массивами.
Задача. Требуется найти сумму всех элементов массива, принимающих значения типа float. Количество элементов массива задано.
Программа 22:
#include
#define NSIZE 20 /* Максимальный размер массива */
viod main (void)
{
int i, n;
float s [NSIZE], sum;
printf ("\nВведите количество элементов массива n = ");
l1:scanf )"%d", &n);
if ((n <=0) || (n>NSIZE))
{
printf ("\n0 < n <=
{
printf ("\n0 < n <=
goto l1;
}
sum = 0;
for (i=0; i<=n; i++)
{
printf ("Введите элемент массива s[%d] = ", i);
scanf ("%f", &s[i]);
sum = sum + s[i];
}
printf ("\nСумма элементов массива sum = %f", sum);
}
Основная идея алгоритма заключается в том, чтобы организовать цикл, с помощью которого, используя индексную переменную i, программа вводила бы очередной элемент массива и одновременно накапливала бы сумму его элементов в переменной sum.
Другой способ работы с элементами массива основан на использовании указателей. Такая возможность обусловлена тем, что имя массива является константой-указателем на адрес ячейки памяти, занимаемой первым элементом рассматриваемого массива.
Здесь важно усвоить, что имя массива – константа-указатель и ему нельзя
В памяти ПЭВМ элементы массива хранятся последовательно, а поскольку начальный адрес массива соответствует первому его элементу, то в соответствии с типом принимаемых значений можно указать (найти) адреса ячеек, где хранятся других элементов массива, кроме первого. Разница в адресах будет кратна количеству байт, отводимых под хранение значения данного типа. Ранее шла речь об одномерных массивах, элементы которых можно описать одной индексной переменной. По аналогии можно ввести многомерные массивы, требующие для своего описания одно имя и несколько индексных переменных. Широкое применение нашли двумерные массивы, математическим аналогом которых являются таблицы значений, или матрицы. Многомерные массивы объявляются следующим образом
<тип> имя массива [размер по 1 инд. пер.][размер по 2 инд. пер.] …[размер по k инд. пер.];
В отличие от описания одномерного массива здесь в квадратных скобках должны указываться размерности по каждой индексной переменной. Рассмотрим такие примеры:
int l [15][10];
float b [35][18][41];
В первом операторе объявляется целочисленный массив l, содержащий 150 элементов.
В первом операторе объявляется целочисленный массив l, содержащий 150 элементов.
float c [3][4];
char tx [2][3][4];
c[0]0[], c[0][1], c[0][2], c[0][3],
c[1][0], c[1][1], c[1][2], c[1][3],
c[2][0], c[2][1], c[2][2], c[2][3]
и
tx[0][0][0], tx[0][0][1], tx[0][0][2], tx[0][0][3],
tx[0][1][0], tx[0][1][1], tx[0][1][2], tx[0][1][3],
tx[0][2][0], tx[0][2][1], tx[0][2][2], tx[0][2][3],
tx[1][0][0], tx[1][0][1], tx[1][0][2], tx[1][0][3],
tx[1][1][0], tx[1][1][1], tx[1][1][2], tx[1][1][3],
tx[1][2][0], tx[1][2][1], tx[1][2][2], tx[1][2][3]
В частности, имя двумерного массива является указателем на первую строку этого массива.
Представим пример программы, иллюстрирующий работу с двумерными массивами посредством технологии, основанной на индексных переменных. Программа решает задачу о вычислении произведения двух прямоугольных матриц по правилу
Программа 23:
#include
#define N 5
#define L 4
#define M 3
void main
Программа 23:
#include
#define N 5
#define L 4
#define M 3
void main
{
int i, j, k;
float a[N][L], b[L][M], c[N][M], p;
printf ("\n");
for (i=0; i
printf ("Введите элемент a[%d][%d] = ", i, j);
scanf ("%f", &p);
a[i][j] = p;
}
printf ("\n");
for (i=0; i
printf ("Введите элемент b[%d][%d] = ", i, j);
scanf ("%f", &p);
b[i][j] = p;
}
printf ("\n");
/* Вычисляем элементы матрицы c */
for (i=0; ii++)
for (j=0; j {
c[i][j] = 0;
for (k=0; k {
c[i][j] = c[i][j] + a[i][k]*b[k][j];
printf ("Элемент матрицы c[%d][%d} = %f\n",I, j, c[i][j]);
}
}
}
В данной программе в теле первых двух вложенных циклов вводятся элементы матрицы a, вторая группа циклов осуществляет ввод элементов матрицы b, а в последней группе трех вложенных циклов насчитываются элементы матрицы c и выводятся на экран монитора.
Операции над ссылками. Обсудим основные операции над указателями, которые определены в языке СИ. Для этого введем переменные s и ps, описанные оператором
int s, *ps;
printf ("\n");
/* Вычисляем элементы матрицы c */
for (i=0; i
for (j=0; j
c[i][j] = 0;
for (k=0; k
c[i][j] = c[i][j] + a[i][k]*b[k][j];
printf ("Элемент матрицы c[%d][%d} = %f\n",I, j, c[i][j]);
}
}
}
В данной программе в теле первых двух вложенных циклов вводятся элементы матрицы a, вторая группа циклов осуществляет ввод элементов матрицы b, а в последней группе трех вложенных циклов насчитываются элементы матрицы c и выводятся на экран монитора.
Операции над ссылками. Обсудим основные операции над указателями, которые определены в языке СИ. Для этого введем переменные s и ps, описанные оператором
int s, *ps;
Здесь s – переменная целого типа, а p – переменная-указатель на
s = *ps + 3;
*ps = s;
*ps -= 2;
(*ps)++;
Таким образом, там, где по смыслу должна применяться переменная целого типа, пользователь имеет полное право употреблять вместо нее *ps. Данное правило справедливо для указателей с точностью до типа значений, применяемых переменной, на которую он (указатель) ссылается.
Над указателями также могут выполняться операции целочисленной арифметики. Данная особенность, как правило, связана с использованием массивов в программе. Мы уже знаем, что имя массива – указатель первого его элемента (хранит адрес ячейки, занимаемой им). Допустим, что программа работает с массивом
float d[28], *pd, e;
Оператор
pd = &d[0];
заносит на хранение в переменную с именем pd адрес нулевого элемента массива d, а оператор
e = *pd;
переменной e присваивает значение нулевого элемента массива, используя операцию снятия ссылки. Однако подобные операции можно проделать иначе:
pd = d;
e = *pd;
поскольку переменная pd указывает на нулевой элемент
pd = d;
e = *pd;
поскольку переменная pd указывает на нулевой элемент
e = *(pd+j);
эквивалентен следующему:
e = d[j];
аналогичным образом можно использовать имя массива, так как d+j является адресом j-го элемента массива d, и для получения его содержимого применить операцию снятия ссылки *(d+j). В силу данного правила операторы
e = *(d+j);
и
e = d[j];
эквивалентны по своему действию. Адреса различных элементов массива будут отличаться на целое число, кратное количеству байт, которые требуются для хранения одного элемента. Вообще для указателей допустимы операции, представленные в следующей таблице:
Таблица 1.2.1
Операции над указателями
Таким образом, описанная группа операций позволяет манипулировать
Таблица 1.2.1
Операции над указателями
Таким образом, описанная группа операций позволяет манипулировать
Программа 24:
#include
#define NSIZE 20 /* Максимальный размер массива */
void
Программа 24:
#include
#define NSIZE 20 /* Максимальный размер массива */
void
{
int i, n;
float s[NSIZE], sum;
printf ("\nвведите число элементов массива n = ");
l1:scanf ("%d:, &n);
if ((n <= 0) || (n > NSIZE))
{
printf ("\n0 <= n <= %d.", NSIZE);
goto l1;
}
sum = 0;
for (i=0; i
printf ("Введите элемент массива s[%d] = ", i);
scanf ("%f", s+i);
sum += *(s+i);
}
printf ("\nСумма элементов массива sum = %f\n", sum);
}
Для двумерного массива, объявленного оператором
int date0 [10][15], I, j;
имя массива date0
int date0 [10][15], I, j;
имя массива date0
*(date0+j)
задает указатель на строку, отстоящую от первой на j строк. Следовательно, выражение
*(*(date0+i)+j)
эквивалентно более привычному для нас способу обращения к элементу, стоящему в (i+1)-ой строке (j+1)-ом месте, а именно
date0[i][j]
С учетом этого представим новую версию программы 23 о вычислении произведения двух прямоугольных матриц.
Программа 25:
#include
#define N 5
#define L 4
#define M 3
void main (void)
{
int i, j, k;
float a[N][L], b[L][M], c[N][M], p;
printf ("\n");
for (i=0; i
{
printf ("Введите элемент a[%d][%d] = ", i, j);
scanf
{
printf ("Введите элемент a[%d][%d] = ", i, j);
scanf
*(*(a+i)+j) = p;
}
printf ("\n");
for (i=0; i
printf ("Введите элемент b[%d][%d] = ", i, j);
scanf ("%f", &p);
*(*(b+i)+j) = p;
}
printf ("\n");
/* Вычисляем элементы матрицы c */
for (i=0; i
*(*(c+i)+j) = 0;
for (k=0; k
*(*(c+i])+j) += *(*(a+i)+k]*(*(*b+k)+j));
printf ("Элемент матрицы c[%d][%d} = %f\n",i, j, *(*(c+i)+j));
}
}
}
Ссылки
}
}
}
Ссылки