Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: Передача по ссылке структуры в процедуры или функции  (Прочитано 25686 раз)
0 Пользователей и 4 Гостей смотрят эту тему.
eugrita
Помогающий

ru
Offline Offline

« : 14-03-2013 04:16 » 

Правильно ли передавать структуру по ссылке так.
Код:
typedef struct BigNum {
    int sign; // Знак числа, 1 или -1.
    int *data;
   size_t size;
} BigNum;
void BigNum_add(BigNum * bn1, BigNum * bn2) {
if (bn1->sign==bn2->sign) {
 ....
                                           }
else {
  ...
   BigNum_sub(bn1,bn2);
   .....
         };
                                                                                                          }

void BigNum_sub(BigNum * bn1, BigNum * bn2)         {
.........
                                                                                                      }
из процедуры BigNum_add вызывается процедура BigNum_sub(bn1,bn2);
которая внутри себя изменяет bn1.Параметр bn1 изменяется как внутри
BigNum_add  так и в BigNum_sub.
Cледовало бы ожидать что измененное значение передастся в вызывающую
процедуру BigNum_add и еще выше в вызывающую BigNum_addфункцию
Но этого не происходит. Отладчик показывает что вычисленное внутри
BigNum_sub значение  bn1 портится
. Хотя в варианте кодв с одноуровневым
вызовом (т.е. когда в коде BigNum_add  нет вызова  процедуры BigNum_sub(bn1,bn2);
в самый верх измененный в BigNum_add параметр bn1 отдично передается.
Почему? Вроде ставить амперсанд перед именем структуре при вызове не надо.
« Последнее редактирование: 14-03-2013 04:18 от eugrita » Записан
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #1 : 14-03-2013 05:10 » 

Код:
struct BigNum
{
    int sign; // Знак числа, 1 или -1.
    int *data;
    size_t size;
  
    BigNum()
    {
      sign=1; // Знак числа, 1 или -1.
      data=0;
      size=0;
    }
};

ты передаёшь по указателю - вроде всё нормально должно быть. Давай конкретные примеры, что не работает ?


Код:
void BigNum_add(BigNum* bn1, const BigNum* bn2)
{
    if (bn1->sign==bn2->sign)
    {
       // ....
    }
    else
    {
        BigNum_sub(bn1,bn2);
    };
}

void BigNum_sub(BigNum* bn1, const BigNum*  bn2)
{
}
 

Добавлено через 1 минуту и 23 секунды:
Вроде ставить амперсанд перед именем структуре при вызове не надо.

путаем C++ и шарп ?  Про какой язык речь ?
« Последнее редактирование: 14-03-2013 05:11 от Алексей1153 » Записан

Dale
Блюзмен
Команда клуба

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #2 : 14-03-2013 20:16 » 

Параметр bn1 изменяется как внутри
BigNum_add  так и в BigNum_sub.
Cледовало бы ожидать что измененное значение передастся в вызывающую
процедуру BigNum_add и еще выше в вызывающую BigNum_addфункцию
Но этого не происходит.

И не должно происходить.

Параметры в языке C передаются в функции только по значению (как локальные копии оригинального аргумента). Изменяя параметры, вы меняете лишь значение этой копии; сам оригинал остается неизменным. После выхода из функции копия прекращает существование, и от изменения не остается и следа.
Записан

Всего лишь неделя кодирования с последующей неделей отладки могут сэкономить целый час, потраченный на планирование программы. - Дж. Коплин.

Ходить по воде и разрабатывать программное обеспечение по спецификациям очень просто, когда и то, и другое заморожено. - Edward V. Berard

Любые проблемы в информатике решаются добавлением еще одного уровня косвенности – кроме, разумеется, проблемы переизбытка уровней косвенности. — Дэвид Уилер.
RXL
Технический
Администратор

Offline Offline
Пол: Мужской

WWW
« Ответ #3 : 15-03-2013 05:30 » 

... и потому в Си изменяемые объекты передают по указателю.

Ссылки появились только в C++ и, при синтаксисе соответствующем работе с переменной, по сути являются неявными указателями.
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
eugrita
Помогающий

ru
Offline Offline

« Ответ #4 : 15-03-2013 14:14 » 

Параметр bn1 изменяется как внутри
BigNum_add  так и в BigNum_sub.
Cледовало бы ожидать что измененное значение передастся в вызывающую
процедуру BigNum_add и еще выше в вызывающую BigNum_addфункцию
Но этого не происходит.

И не должно происходить.

Параметры в языке C передаются в функции только по значению (как локальные копии оригинального аргумента). Изменяя параметры, вы меняете лишь значение этой копии; сам оригинал остается неизменным. После выхода из функции копия прекращает существование, и от изменения не остается и следа.

А вот тут позвольте не согласиться
Код:
void display_values(int *a, int *b) {
*a=1000; *b=1                                                           
                                                      }
void main() {
int a= 2002, b = 0;
display_values(&a, &b);
pruntf(“%i %i”,a,b);  //стало a=1000; b=1;
Это есть и было в С


Добавлено через 11 минут и 40 секунд:
Это прога попытка реализации арифметики длинных чисел. Каждое число -структура Bignum есть знак sign, массив data - четверок 10-ичных чисел
вот даже вставил отладочную печать функция BigNum_add в случае когда знаки слагаемых разные вызывает
BigNum_sub и в той и в другой изменяется переданное по ссылке bn1. Тест 1+ (-10)  должен быть=-9
Перед выходом из BigNum_sub bn1=-9 точнее bn1->sign=-1, bn1->data[0]=9 а сразу же после возврата в BigNum_add -  bn1->sign=1, bn1->data[0]=1 т.е=1
Вроде передача структуры по ссылке не противоречит правилам С. Тогда это может быть вызвано другими причинами смещением памяти или что-то вроде?
Код:
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#define MAXVAL 10000
typedef struct BigNum {
    int sign; // Знак числа, 1 или -1.
    int *data; // Массив с частями числа.
        // Каждая часть хранит числа от 0 до 9999 включительно,
        // что соответствует записи числа в системе с основанием
        // 10000.
        // data[0] хранит в себе младший разряд числа.
    size_t size; // Размер массива data.
} BigNum;

// Фукнции максимума и минимума для удобства.
int MIN(int a, int b)
{ return (a < b) ? a : b;}

int MAX(int a, int b)
{ return (a < b) ? b : a;}

BigNum *BigNum_make(char *source)
{   int i, k = 0;int power;
    BigNum *bn = (BigNum *) malloc(sizeof(BigNum));
    if (source[0] == '-') {
bn->sign = -1;source++; // пропустить знак '-'
                          }
else {bn->sign = 1;}
    // Чтобы при целочисленном делении a / n сделать оругление
    // в большую сторону, используем (a + (n-1)) / n.
    bn->size = (strlen(source) + 3) / 4;
    bn->data = (int *) malloc(sizeof(int) * bn->size);
    // Считываем из массива все разряды по осн 10000,
    // начиная с младшего. Старший разряд обработаем позже.
    for (i = strlen(source) - 1; i >= 4; i -= 4) {
bn->data[k] = 1000*(source[i - 3]-'0') +
   100*(source[i-2] - '0')+10*(source[i-1]-'0')+(source[i]-'0');
k++;
    }
    //Так как в старшем разряде может быть менее 4 десятичных цифр,обработаем его отдельно.
    bn->data[k] = 0;
    power = 1;
    for (; i >= 0; i--) {
bn->data[k] = bn->data[k] + (source[i] - '0') * power;
        power *= 10;
    }
    return bn;
}

BigNum *BigNum_copy(BigNum * bn)
{
    // Надо выделить память под новую структуру BigNum и
    // под её внутренний массив, и скопировать в них  соотв содерж из bn.
int i;
    BigNum *bn1 = (BigNum *) malloc(sizeof(BigNum));
    bn1->size=bn->size;bn1->sign=bn->sign;
bn1->data=(int *)malloc(bn1->size*sizeof(int));
    for(i=0;i<bn1->size;i++)  
bn1->data[i]=bn->data[i];
}

void BigNum_free(BigNum * bn)
{
    free(bn->data);
    free(bn);
}

void BigNum_print(BigNum * bn)
{   int i;
    if (bn->sign < 0)  printf("-\n");
    // Печатаем разряды, начиная со старшего.
    for (i = bn->size - 1; i >= 0; i--)
{printf("%04d", bn->data[i]);}
    printf("\n");
}

void BigNum_set_size(BigNum * bn, size_t new_size)
{  // Функция для установки нового размера внутреннего массива.
    // если размер массива увеличился, нужно занулить новые элементы.
   int i;
   if (new_size> bn->size) {
  for(i=bn->size;i<new_size;i++)
   bn->data[i]=0;
                           };
  bn->size=new_size;
}

void BigNum_add(BigNum * bn1, BigNum * bn2) // +=
{// 1 2312 3123 + 9 8798 7987  // 1 +  9999 9999   //9999 9999 9999 + 1
    // Функия неправ работает при сложении положит и отриц числа.
    // Дораб её, чтобы она раб корректно и добавьте соотв тесты в run_tests() в main.c
int i, x = 0;
if (bn1->sign==bn2->sign) {//знаки совпадают
BigNum_set_size(bn1,MAX(bn1->size,bn2->size)+1);
     for (i = 0; i < bn2->size; i++) {
  bn1->data[i] += bn2->data[i] + x;
  x = bn1->data[i] / MAXVAL; // Получаем 1 (при переполнении) или 0 для переноса в следующий разряд.
  bn1->data[i] %= MAXVAL; // Убираем всё лишнее.
                                    }
    for (; i < bn1->size && x != 0; i++) // продолжаем распространять единицу, если она есть.
{  bn1->data[i] += x; x = bn1->data[i] / MAXVAL; bn1->data[i] %= MAXVAL;}
if (bn1->data[bn1->size-1]==0) bn1->size--;
                          }//знаки совпадают
else {//знаки не совпадают
bn2->sign=-bn2->sign;BigNum_sub(bn1,bn2);
           [b]  printf("otl2 bn1->data[0]= %i\n",bn1->data[0]);[/b]
bn2->sign=-bn2->sign;
    };
}
void BigNum_sub(BigNum * bn1, BigNum * bn2)
{//Операция -=
 int i, x = 0;BigNum *w1;
 if (bn1->sign==bn2->sign)          {//знаки совпадают
  if(BigNum_gt(bn1,bn2)==1){//bn1>=bn2
   BigNum_set_size(bn1,bn1->size);
   for (i =0; i<bn2->size;i++)  {
    bn1->data[i]-= bn2->data[i];
if ( bn1->data[i]<0)
  {bn1->data[i]+=MAXVAL; bn1->data[i+1]--;};
                                }
                           }//bn1>=bn2
  else {//bn2>bn1
    w1=BigNum_copy(bn1);BigNum_set_size(bn1,bn2->size);
bn1=BigNum_copy(bn2);bn1->sign=-1;
     for (i =0; i<w1->size;i++)  {
    bn1->data[i]-= w1->data[i];
if ( bn1->data[i]<0)
  {bn1->data[i]+=MAXVAL; bn1->data[i+1]--;};
                                  }
       };
                                    }//знаки совпадают
 else {//знаки не совпадают
bn2->sign=-bn2->sign;BigNum_add(bn1,bn2);
 };
 [b] printf("otl1 bn1->data[0]= %i\n",bn1->data[0]);[/b]
}
int BigNum_gt(BigNum * bn1, BigNum * bn2)//>
{
if (bn1->size<bn2->size) return 0;
if (bn1->size>bn2->size) return 1;
if (bn1->data[bn1->size-1]>=bn2->data[bn2->size-1])return 1;
else return 0;
}
int BigNum_eq(BigNum * bn1, BigNum * bn2) // ==
{// Функция проверки на равенство.
    //числа разной длины тоже м б равны если в числе большего разм в "лишних" разрядах стоят 0
 int i,mn;
if (bn1->sign != bn2->sign)
          {return 0;}
if(bn1->size<bn2->size) {
 mn=bn1->size;
 for(i=bn1->size;i<bn2->size;i++)
if (bn2->data[i] != 0) return 0;
                        };
if(bn2->size<bn1->size) {
 mn=bn2->size;
 for(i=bn2->size;i<bn1->size;i++)
if (bn1->data[i] != 0) return 0;
                        }
else mn=bn1->size;
 for (i=0;i<mn;i++)
   if (bn1->data[i] !=bn2->data[i]) return 0;
 return 1;
}

void BigNum_check_add(char *bn1_str, char *bn2_str, char *result_str)
{ // Создаём большие числа из строк.
    BigNum *bn1 = BigNum_make(bn1_str), *bn2 = BigNum_make(bn2_str),
           *result = BigNum_make(result_str);
    // Делаем копию bn1, чтобы иметь неизменённое число,
    // если нужно будет вывести сообщение об ошибке.
    BigNum *bn1_copy = (BigNum *) BigNum_copy(bn1);
    BigNum_add(bn1_copy, bn2); // Складываем числа, результат в bn1_copy.
    if (!BigNum_eq(bn1_copy, result)) { // Если результат перемножения не равен заданному,
                                // напечатаем ошибку и выйдем из функции.
printf("Can not add\n  ");BigNum_print(bn1);printf("  to\n  ");BigNum_print(bn2);
printf("  Expected\n  ");BigNum_print(result);printf("  got\n  ");BigNum_print(bn1_copy);
return;
    }
    // Освободим память, чтобы не было утечек.
    BigNum_free(bn1),  BigNum_free(bn2), BigNum_free(result);
//BigNum_free(bn1_copy);
}


void BigNum_mul(BigNum * bn1, BigNum * bn2)
{// Операция *=
}
#include "bignum.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#define MAXVAL 10000
// Фукнции максимума и минимума для удобства.
int MIN(int a, int b)
{ return (a < b) ? a : b;}

int MAX(int a, int b)
{ return (a < b) ? b : a;}

BigNum *BigNum_make(char *source)
{   int i, k = 0;int power;
    BigNum *bn = (BigNum *) malloc(sizeof(BigNum));
    if (source[0] == '-') {
bn->sign = -1;source++; // пропустить знак '-'
                          }
else {bn->sign = 1;}
    // Чтобы при целочисленном делении a / n сделать оругление
    // в большую сторону, используем (a + (n-1)) / n.
    bn->size = (strlen(source) + 3) / 4;
    bn->data = (int *) malloc(sizeof(int) * bn->size);
    // Считываем из массива все разряды по осн 10000,
    // начиная с младшего. Старший разряд обработаем позже.
    for (i = strlen(source) - 1; i >= 4; i -= 4) {
bn->data[k] = 1000*(source[i - 3]-'0') +
   100*(source[i-2] - '0')+10*(source[i-1]-'0')+(source[i]-'0');
k++;
    }
    //Так как в старшем разряде может быть менее 4 десятичных цифр,обработаем его отдельно.
    bn->data[k] = 0;
    power = 1;
    for (; i >= 0; i--) {
bn->data[k] = bn->data[k] + (source[i] - '0') * power;
        power *= 10;
    }
    return bn;
}

BigNum *BigNum_copy(BigNum * bn)
{
    // Надо выделить память под новую структуру BigNum и
    // под её внутренний массив, и скопировать в них  соотв содерж из bn.
int i;
    BigNum *bn1 = (BigNum *) malloc(sizeof(BigNum));
    bn1->size=bn->size;bn1->sign=bn->sign;
bn1->data=(int *)malloc(bn1->size*sizeof(int));
    for(i=0;i<bn1->size;i++)  
bn1->data[i]=bn->data[i];
}

void BigNum_free(BigNum * bn)
{
    free(bn->data);
    free(bn);
}

void BigNum_print(BigNum * bn)
{   int i;
    if (bn->sign < 0)  printf("-\n");
    // Печатаем разряды, начиная со старшего.
    for (i = bn->size - 1; i >= 0; i--)
{printf("%04d", bn->data[i]);}
    printf("\n");
}

void BigNum_set_size(BigNum * bn, size_t new_size)
{  // Функция для установки нового размера внутреннего массива.
    // если размер массива увеличился, нужно занулить новые элементы.
   int i;
   if (new_size> bn->size) {
  for(i=bn->size;i<new_size;i++)
   bn->data[i]=0;
                           };
  bn->size=new_size;
}

void BigNum_add(BigNum * bn1, BigNum * bn2) // +=
{// 1 2312 3123 + 9 8798 7987  // 1 +  9999 9999   //9999 9999 9999 + 1
    // Функия неправ работает при сложении положит и отриц числа.
    // Дораб её, чтобы она раб корректно и добавьте соотв тесты в run_tests() в main.c
int i, x = 0;
if (bn1->sign==bn2->sign) {//знаки совпадают
BigNum_set_size(bn1,MAX(bn1->size,bn2->size)+1);
     for (i = 0; i < bn2->size; i++) {
  bn1->data[i] += bn2->data[i] + x;
  x = bn1->data[i] / MAXVAL; // Получаем 1 (при переполнении) или 0 для переноса в следующий разряд.
  bn1->data[i] %= MAXVAL; // Убираем всё лишнее.
                                    }
    for (; i < bn1->size && x != 0; i++) // продолжаем распространять единицу, если она есть.
{  bn1->data[i] += x; x = bn1->data[i] / MAXVAL; bn1->data[i] %= MAXVAL;}
if (bn1->data[bn1->size-1]==0) bn1->size--;
                          }//знаки совпадают
else {//знаки не совпадают
bn2->sign=-bn2->sign;BigNum_sub(bn1,bn2);
             printf("otl2 bn1->data[0]= %i\n",bn1->data[0]);
bn2->sign=-bn2->sign;
    };
}
void BigNum_sub(BigNum * bn1, BigNum * bn2)
{//Операция -=
 int i, x = 0;BigNum *w1;
 if (bn1->sign==bn2->sign)          {//знаки совпадают
  if(BigNum_gt(bn1,bn2)==1){//bn1>=bn2
   BigNum_set_size(bn1,bn1->size);
   for (i =0; i<bn2->size;i++)  {
    bn1->data[i]-= bn2->data[i];
if ( bn1->data[i]<0)
  {bn1->data[i]+=MAXVAL; bn1->data[i+1]--;};
                                }
                           }//bn1>=bn2
  else {//bn2>bn1
    w1=BigNum_copy(bn1);BigNum_set_size(bn1,bn2->size);
bn1=BigNum_copy(bn2);bn1->sign=-1;
     for (i =0; i<w1->size;i++)  {
    bn1->data[i]-= w1->data[i];
if ( bn1->data[i]<0)
  {bn1->data[i]+=MAXVAL; bn1->data[i+1]--;};
                                  }
       };
                                    }//знаки совпадают
 else {//знаки не совпадают
bn2->sign=-bn2->sign;BigNum_add(bn1,bn2);
 };
  printf("otl1 bn1->data[0]= %i\n",bn1->data[0]);
}
int BigNum_gt(BigNum * bn1, BigNum * bn2)//>
{
if (bn1->size<bn2->size) return 0;
if (bn1->size>bn2->size) return 1;
if (bn1->data[bn1->size-1]>=bn2->data[bn2->size-1])return 1;
else return 0;
}
int BigNum_eq(BigNum * bn1, BigNum * bn2) // ==
{// Функция проверки на равенство.
    //числа разной длины тоже м б равны если в числе большего разм в "лишних" разрядах стоят 0
 int i,mn;
if (bn1->sign != bn2->sign)
          {return 0;}
if(bn1->size<bn2->size) {
 mn=bn1->size;
 for(i=bn1->size;i<bn2->size;i++)
if (bn2->data[i] != 0) return 0;
                        };
if(bn2->size<bn1->size) {
 mn=bn2->size;
 for(i=bn2->size;i<bn1->size;i++)
if (bn1->data[i] != 0) return 0;
                        }
else mn=bn1->size;
 for (i=0;i<mn;i++)
   if (bn1->data[i] !=bn2->data[i]) return 0;
 return 1;
}

void BigNum_check_add(char *bn1_str, char *bn2_str, char *result_str)
{ // Создаём большие числа из строк.
    BigNum *bn1 = BigNum_make(bn1_str), *bn2 = BigNum_make(bn2_str),
           *result = BigNum_make(result_str);
    // Делаем копию bn1, чтобы иметь неизменённое число,
    // если нужно будет вывести сообщение об ошибке.
    BigNum *bn1_copy = (BigNum *) BigNum_copy(bn1);
    BigNum_add(bn1_copy, bn2); // Складываем числа, результат в bn1_copy.
    if (!BigNum_eq(bn1_copy, result)) { // Если результат перемножения не равен заданному,
                                // напечатаем ошибку и выйдем из функции.
printf("Can not add\n  ");BigNum_print(bn1);printf("  to\n  ");BigNum_print(bn2);
printf("  Expected\n  ");BigNum_print(result);printf("  got\n  ");BigNum_print(bn1_copy);
return;
    }
    // Освободим память, чтобы не было утечек.
    BigNum_free(bn1),  BigNum_free(bn2), BigNum_free(result);
//BigNum_free(bn1_copy);
}

void main()
{
  BigNum_check_add("1", "-10", "-9");
}
« Последнее редактирование: 15-03-2013 14:26 от eugrita » Записан
darkelf
Молодой специалист

ua
Offline Offline

« Ответ #5 : 15-03-2013 14:29 » 

Параметр bn1 изменяется как внутри
BigNum_add  так и в BigNum_sub.
Cледовало бы ожидать что измененное значение передастся в вызывающую
процедуру BigNum_add и еще выше в вызывающую BigNum_addфункцию
Но этого не происходит.

И не должно происходить.

Параметры в языке C передаются в функции только по значению (как локальные копии оригинального аргумента). Изменяя параметры, вы меняете лишь значение этой копии; сам оригинал остается неизменным. После выхода из функции копия прекращает существование, и от изменения не остается и следа.

А вот тут позвольте не согласиться
Код:
void display_values(int *a, int *b) {
*a=1000; *b=1                                                          
                                                      }
void main() {
int a= 2002, b = 0;
display_values(&a, &b);
pruntf(“%i %i”,a,b);  //стало a=1000; b=1;
Это есть и было в С

eugrita,  Вы тут адреса передаёте по значению и дальше по этим адресам меняете значения самих переменных a и b.
Записан
eugrita
Помогающий

ru
Offline Offline

« Ответ #6 : 15-03-2013 14:32 » 

хорошо, как не называй но цель достигнута - display_values вернула в main  измененные значения
Записан
darkelf
Молодой специалист

ua
Offline Offline

« Ответ #7 : 15-03-2013 14:37 » 

просто я про то, как сказал Dale, что все параметры таки передаются по значению, и в случае вызова
Код:
display_values(&a, &b);
считайте, что выполняется следующий псевдокод:
Код:
display_values(addressof(a), addressof(b));
и внутри display_values тоже знают, что входные параметры a и b это не int, а вовсе даже и указатели.

И ещё - в C передачи по ссылке - нет, есть либо передача по указателю, либо по значению. А по ссылке это уже в C++ (которая, имхо, на самом деле передача по указателю, замаскированная под передачу по значению).
« Последнее редактирование: 15-03-2013 14:42 от darkelf » Записан
eugrita
Помогающий

ru
Offline Offline

« Ответ #8 : 15-03-2013 14:58 » 

хорошо тогда по моему исходному примеру - я дважды передаю по указателю сначала в bignam_add затем
оттуда в bignum_sub . Правильно ли что при такой передаче для структур (в отличие от простых переменных)  можно не писать перед именем параметра амперсанд? т.е например в bigNum_check_add можно было написать
Код:
BigNum_add(&bn1_copy, &bn2); 
но это неправильно?
Записан
darkelf
Молодой специалист

ua
Offline Offline

« Ответ #9 : 15-03-2013 15:03 » 

eugrita, кстати, заметил - у Вас в BigNum_copy() ошибка - Вы забыли сказать
Код:
return bn1;
Вполне возможно, что из-за этого функция просто возвращает какой-то мусор.
Записан
eugrita
Помогающий

ru
Offline Offline

« Ответ #10 : 15-03-2013 16:50 » new

да эту ошибку давно исправил. Но на результат она не повлияла.
Почему в bignam_add перед вызовом bignam_sub bn1->data[0]=1; bn1->sign=1
внутри вызова bignam_sub перед возвратом bn1->data[0]=9; bn1->sign=-1 (как и должно быть)
а потом в  bignam_add  на следующем операторе после вызова bignam_sub опять старое значение
bn1->data[0]=1; bn1->sign=1  ?
Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #11 : 15-03-2013 21:45 » 

Цитата: eugrita
Правильно ли что при такой передаче для структур (в отличие от простых переменных)  можно не писать перед именем параметра амперсанд?
Правильно так, как работает. Если не взять адрес там, где он нужен - будет ошибка компиляции или времени исполнения (для указателей на указатели). Если взять лишний раз адрес - будет ошибка времени исполнения. Вопрос бессмысленный. Вернее, за ним стоит отказ уяснить себе, что указатель - это совершенно отдельный самостоятельный тип, значением которого является адрес, а не какой-то значок-довесок к типу структуры.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
eugrita
Помогающий

ru
Offline Offline

« Ответ #12 : 16-03-2013 05:02 » 

ладно. Ошибку эту я так и не понял но ее исправил отказавшись от модификации 1-го параметра процедуры.
Вместо этого переделал ее на функцию возвращающую значение. Так работает
Код:
BigNum * BigNum_add(BigNum * bn1, BigNum * bn2)	// +=
{// 1 2312 3123 + 9 8798 7987  // 1 +  9999 9999   //9999 9999 9999 + 1
int i,mn, x = 0;BigNum *w;
w=BigNum_copy(bn1);
if (bn1->sign==bn2->sign) {//знаки совпадают
BigNum_set_size(w,MAX(bn1->size,bn2->size)+1);
mn=MIN(bn1->size,bn2->size);
     for (i = 0; i < mn; i++) {
   w->data[i]=bn1->data[i] + bn2->data[i] + x;
   x = w->data[i] / MAXVAL; // Получаем 1 (при переполнении) или 0 для переноса в следующий разряд.
   w->data[i] %= MAXVAL; // Убираем всё лишнее.
                                    }
    for (; i < bn1->size && x != 0; i++) // продолжаем распространять единицу, если она есть.
{w->data[i] += x; x =w->data[i] / MAXVAL; w->data[i] %= MAXVAL;}
if (w->data[bn1->size-1]==0) w->size--;
                           }//знаки совпадают
else //знаки не совпадают
  {bn2->sign=-bn2->sign;w=BigNum_sub(bn1,bn2);};
  return w;
}
BigNum * BigNum_sub(BigNum * bn1, BigNum * bn2)
{//Операция -=
 int i, x = 0,mn,mx;BigNum *w1,*w;
 mn=MIN(bn1->size,bn2->size);mx=MAX(bn1->size,bn2->size);
 if (bn1->sign==bn2->sign)          {//знаки совпадают
  if(BigNum_gt(bn1,bn2)==1){//bn1>=bn2
  w=BigNum_copy(bn1);
   for (i =0; i<mn;i++)  {
     w->data[i]=bn1->data[i]- bn2->data[i];
if (w->data[i]<0)
   {w->data[i]+=MAXVAL;w->data[i+1]--;};
                                }
                           }//bn1>=bn2
  else {//bn2>bn1
    w=BigNum_copy(bn2);
     for (i =0; i<mn;i++)  {
     w->data[i]=bn2->data[i]- bn1->data[i];
if (w->data[i]<0)
   {w->data[i]+=MAXVAL;w->data[i+1]--;};
                           }
       };
  w->sign=-bn2->sign;
                                    }//знаки совпадают
 else //знаки не совпадают
   {bn2->sign=-bn2->sign;w=BigNum_add(bn1,bn2);};
 return w;
}
BigNum * BigNum_mul(BigNum * bn1, BigNum * bn2)
{// Операция *=
 int i,j,k, x1,x2;BigNum *w;
 w=BigNum_make("0");
 BigNum_set_size(w,bn1->size+bn2->size);
  for (i =0; i<bn1->size;i++)
  for (j =0; j<bn2->size;j++) {
   k=bn1->data[i]*bn2->data[j];
   w->data[i+j+1]=k / MAXVAL; w->data[i+j]=k % MAXVAL;
                              }
  w->sign=bn1->sign*bn2->sign;
  return w;
}
А с той ошибкой разобраться хотелось бы - выходит пока ее не преодолеешь нельзя реализовывать
операции в виде процедур с возвратом результата через аргумент
Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #13 : 16-03-2013 07:24 » 

Цитата: eugrita
Ошибку эту я так и не понял но ее исправил отказавшись от модификации 1-го параметра процедуры.
Чтобы понять, надо объяснить, почему в одном случае записывается так, а в другом - иначе.

Ещё раз: указатель на структуру и сама структура - это разные типы данных. И поскольку это так, в программе необходимо выполнять операции преобразования этих типов:
- разыменование "*" для преобразования указателя в структуру и
- взятие адреса "&" для преобразования структуры в указатель.
Для наглядности.
Код: (C)
#include <stdio.h>

struct S {
        int x;
};

typedef struct S *P;

void f(P p) {
        struct S s = *p; /* Преобразование типов: извлечение структуры из памяти */
        s.x = 2;
        *p = s; /* Запись структуры в память */
}

void g(P p) {
        f(p); /* Ты не спрашиваешь, почему тут нет & - это очевидно: p и так нужного типа. */
}

int main() {
        struct S s;
        s.x = 1;
        P p = &s; /* Преобразование типов: взятие адреса памяти структуры */
        g(p);
        printf("%i\n", s.x); /* 2 */
        return 0;
}
Здесь же видно то, что выше говорил Dale: все параметры передаются по значению.

На практике такие явные преобразования никто не пишет, поскольку они более громоздки как по записи, так и по операциям (лишние копирования структуры). На практике со структурой по указателю работают через комбинацию операторов "(*)." или его эквивалента - оператора "->".

Но это не значит, что указателя как отдельного типа не существует.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
eugrita
Помогающий

ru
Offline Offline

« Ответ #14 : 16-03-2013 21:11 » 

Хорошо. И как зная все это 1)оценить правильность реплики Даркелла
что измененный внутри процедуры Bignam_sub параметр bn1 не передастся наверх т.е. в вызвавшую его функцию
Bignam_add ?
2)Если допустить что он прав как можно поправить исходный код не прибегая к преобразованию процедуры в функцию (как сделал я)?
По поводу 1) могу лишь сказать что такая же схема передачи параметров действовада не только из Bignam_add в Bignam_sub но и из BigNum_check в Bignam_add только там все происходило ОК. (сам проверял с отладчиком)
Дефект проявился лишь в связке
Bignam_add в Bignam_sub
Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #15 : 17-03-2013 00:50 » 

eugrita, плохо. Ты два раза полностью проигнорировал мною сказанное.

1) Ты нигде не меняешь параметр bn1 внутри Bignam_add и Bignam_sub, и он никак не возвращается в вызывающую функцию.
2) Если тебе кажется обратное, см. пункт 1, а также сказанное мною в предыдущих постах - и так до полного просветления.

И какая ещё процедура? В C нет процедур, есть только функции.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines