2014 dxdy logo

Научный форум dxdy

Математика, Физика, Computer Science, Machine Learning, LaTeX, Механика и Техника, Химия,
Биология и Медицина, Экономика и Финансовая Математика, Гуманитарные науки




На страницу Пред.  1 ... 3, 4, 5, 6, 7  След.
 
 
Сообщение19.12.2005, 21:19 
Аватара пользователя
Для закрытия файла надо вызывать fclose() (а не close, как у Вас), если для открытия вызываешь fopen(). Хотя как показала практика (на моем компе, по крайней мере, и для небольших файлов) - один черт. Чего я, кстати, не понимаю... :)

 
 
 
 
Сообщение19.12.2005, 21:31 
Аватара пользователя
Более того, когда я совсем убрал (заремил) функцию закрытия файла, все осталось как прежде. Вероятно, при завершении программы все использовавшиеся ей потоки закрываются автоматически - если это так, то спасибо операционной системе! :)

 
 
 
 
Сообщение19.12.2005, 21:32 
Уже поменяла в самой программе, как Вы сказали.
Не знаю, у меня работало..
Такому специалисту как я, Ваши эксперименты надо было пояснить...

 
 
 
 
Сообщение19.12.2005, 21:59 
Вы просили замечания:)
1. Писать для определения структуры
Код:
.......
typedef struct marix_type
{
   char*   Name;
   int      Rows;
   int      Cols;
   float*   Values;
} MATRIX;
.......

ИМХО крайне не правильно. Такая запись обычно используется в купе с
Код:
} MATRIX, FAR *LPMATRIX;// - и здесь можно еще что-нить добавить,

и там она имеет смысл - мы определяем структуру marix_type, и для удобства две новых фичи - это тип и указатель на тип (MATRIX и LPMATRIX), которые на самом деле есть marix_type и marix_type*. Я очень часто так и делаю. Таким образом у нас есть MATRIX, и marix_type. И если не нужно FAR *LPMATRIX и прочей ерунды, то можно написать просто:
Код:
.......
struct MATRIX
{
   char*   Name;
   int      Rows;
   int      Cols;
   float*   Values;
};
.......


2. ИМХО, типичным признаком перехода от языков типа Паскаль к С/С++ является использование предобъявлений. В Паскале, на сколько я знаю, это было вызвано кривостью принципов, заложенных в компилятор. В С/С++ этого делать не обязательно и даже, я бы сказал, ни в коем случае не рекомендуется, так как это ухудшает читаемость кода и прочее. Переменную надо объявлять именно там, где она нужна.
Код:
   MATRIX* matrix;
   matrix = (MATRIX*)malloc(sizeof(MATRIX)); //   Зачем так сложно?
...............
//   Когда можно проще и нагляднее -
   MATRIX* matrix = (MATRIX*)malloc(sizeof(MATRIX));
//   При этом в первом случае выполняется инцилизация переменной, а затем ее использование, а во втором - только инициализация (см. описание С/С++).
...............
//   А вот это
void show_matrix(MATRIX_LIST* matrix_list)
{
   char name[10];
   MATRIX* matrix;
   int i, j;
   printf("indicate the name of variable:\n> ");
   gets(name);
   matrix = search_matrix(matrix_list, name);
   if (!matrix)
   {
      puts("Variable not found!");
      return;
   }
   printf("%s:\n", matrix->Name);
   printf("%d x %d\n", matrix->Rows, matrix->Cols);
   for(i=0; i<matrix->Rows; i++)
   {
      for(j=0; j<matrix->Cols; j++)
         printf("   %.4f", matrix->Values[i * matrix->Cols + j]);
      putchar('\n');
   }
}
............
//   можно свести к следующему
void show_matrix(MATRIX_LIST* matrix_list)
{
   printf("indicate the name of variable:\n> ");
   char name[10];
   gets(name);
   MATRIX* matrix = search_matrix(matrix_list, name);
   if (!matrix)
   {
      puts("Variable not found!");
      return;
   }
   printf("%s:\n", matrix->Name);
   printf("%d x %d\n", matrix->Rows, matrix->Cols);
   for(int i=0; i<matrix->Rows; i++)
   {
      for(int j=0; j<matrix->Cols; j++)
         printf("   %.4f", matrix->Values[i * matrix->Cols + j]);
      putchar('\n');
   }
}

Но это так, лирика.

3. Если вы делаете динамическое добавление/удаление - то почему бы не сделать связанный список для хранения матриц. Тогда не прийдется постоянно память выделять/удалять, а просто удаляем/добавляем что-то и переписываем связи.
Код:
typedef struct tagMATRIX_LIST_ITEM
{
   tagMATRIX_LIST_ITEM* m_pPrevItem;
   tagMATRIX_LIST_ITEM* m_pNextItem;
   LPMATRIX m_pMatrix;
} MATRIX_LIST_ITEM, FAR *LPMATRIX_LIST_ITEM;

struct MATRIX_LIST
{
   int m_nItemCount;
   LPMATRIX_LIST_ITEM m_pHeadItem;
   LPMATRIX_LIST_ITEM m_pTailItem;
};

и к этому правда нужно добавить функции работы со списком, типа AddTail/AddHead, RemoveTail/RemoveHead, GetAt/SetAt, RemoveAll, GetCount и т.д.
Код:
LPMATRIX_LIST_ITEM AddHead(MATRIX_LIST* list, LPMATRIX matrix)
{
   list->m_nItemCount++;
   LPMATRIX_LIST_ITEM new_item = new MATRIX_LIST;
   new_item->m_pPrevItem = NULL;
   new_item->m_pNextItem = list->m_pHeadItem;
   new_item->m_pMatrix = matrix;
   list->m_pHeadItem->m_pPrevItem = new_item;
   list->m_pHeadItem = new_item;
   return new_item;
}

bool RemoveHead(MATRIX_LIST* list)
{
   if (!list->m_nItemCount)
      return false;
   list->m_nItemCount--;
   LPMATRIX_LIST_ITEM old_item = list->m_pHeadItem;
   list->m_pHeadItem = old_item->m_pNextItem;
   if (list->m_pHeadItem)
      list->m_pHeadItem->m_pPrevItem = NULL;
   delete old_item;
   return true;
}

int GetCount(MATRIX_LIST* list)
{
   return list->m_nItemCount;
}

и т.д.

 
 
 
 
Сообщение19.12.2005, 22:54 
Аватара пользователя
:evil:
Уважаемый VLarin, r сожалению, Ваши замечания относятся больше к C++, чем к C.
В С:

1. запись
Код:
struct Т {
  int x;
};

если и законна, то весьма мало распространена - она определяет структуру, которую можно использовать в дальнейшем только как
Код:
struct T x;
void foo(struct T * y)
Поэтому многие предпочитают форму
Код:
typedef struct {
  int x;
} T;

которая определяет тип использование которого идет на общих основаниях. Обратите внимание, что я вообще не задаю тег структуры - это допустимо в стандартном C (но не в K&R С). Ловушкой является текст
Код:
struct Т {
  int x;
} y, *z;

который определяет тег структуры T и две переменных - структуру y и указатель на структуру z, а не два типа, как можно было бы подумать.

2. В блоке сначала идут все определения, потом все операторы. Смешивать порядок нельзя. Это не имеет большого значения, поскольку нет конструкторов / деструкторов, и, таким образом, время жизни все равно весь блок. (Простите, но в данном случае я не поленился проверить стандарт языка C.)

 
 
 
 
Сообщение19.12.2005, 23:02 
Аватара пользователя
:evil:
Sanyok писал(а):
Для закрытия файла надо вызывать fclose() (а не close, как у Вас), если для открытия вызываешь fopen(). Хотя как показала практика (на моем компе, по крайней мере, и для небольших файлов) - один черт. Чего я, кстати, не понимаю... :)

Вы просто не проверяете результат close(). Вопреки распространенному мнению, и close(), и fclose() возвращают результат, который следует проверять, хотя бы для обнаружения вышеупомянутых ошибок. Файлы закрываются автоматически при окончании процесса, но:
1) У Вас может наступить исчерпание ресурсов системы.
2) Если Вы пишете в файл (особенно fwrite() - буфферизованная запись) Вы рискуете потерять конец файла. Особенно, если Ваша программа закончилась аварийно.
3) Незакрытый файл может быть (весьма необоснованно) недоступен другим программам.

 
 
 
 
Сообщение19.12.2005, 23:03 
Аватара пользователя
:evil:
Sanyok писал(а):
если это так, то спасибо операционной системе! :)

Больше run-time библиотеке. Хотя OS тоже лепту вносит.

 
 
 
 
Сообщение19.12.2005, 23:15 
Аватара пользователя
Еще я бы посоветовал LynxGAV использовать для ввода строки ту же процедуру, что и в первой программе, или сделать хотя бы буфер побольше. 10 символов для имени матрицы - маловато как-то.

 
 
 
 
Сообщение20.12.2005, 00:07 
Аватара пользователя
:evil:
Sanyok писал(а):
Еще я бы посоветовал LynxGAV использовать для ввода строки ту же процедуру, что и в первой программе, или сделать хотя бы буфер побольше. 10 символов для имени матрицы - маловато как-то.

Присоединяюсь. Не говоря уж об экономии усилий - чего второй-то раз писать написанное.

Задача: вкипятиnm чайник. Физик, математик и программист решают одинаково - наливают воду в чайник и ставят его на огонь. Следующая задача - вскипятить полный чайник. Физик - ставит на огонь. Математик - выливает воду из чайника и возращается к ранее рассмотренной задаче. Программист - выделяет постановку на огонь в отдельную функцию, которую он будет теперь везде вызывать.

 
 
 
 
Сообщение20.12.2005, 00:16 
"Программируя", я не думаю о том, что мне где-то это сможет пригодиться еще. Я не пишу заготовок. Поэтому: :oops:.

 
 
 
 
Сообщение20.12.2005, 00:50 
Аватара пользователя
:evil:
LynxGAV писал(а):
"Программируя", я не думаю о том, что мне где-то это сможет пригодиться еще. Я не пишу заготовок.

Никто не пишет. "Программист - выделяет постановку на огонь в отдельную функцию, которую он будет теперь везде вызывать."

 
 
 
 
Сообщение20.12.2005, 00:57 
Да, понятно.

 
 
 
 
Сообщение20.12.2005, 00:59 
------LynxGAV

choice = atoi(str);

switch (choice)

-------bekas

Спрашивается, зачем использовать посредник 'choice', когда
далее он не используется (даже для печати по default о том,
что ввели неправильное число)?

switch (atoi(str))

Если же необходима печать об ошибке 'choice', опять же можно

switch (choice = atoi(str))
{
...

default:
printf("Wrong choice %d!\n", choice);
}

А такая печать необходима, если вы попадали в ситуацию, когда, например,
чужая программа печатает сообщение "Файл не найден" и не сообщает,
какой именно, тем более, если программа работает с несколькими файлами.

------LynxGAV
printf("%s:\n", matrix->Name);
printf("%d x %d\n", matrix->Rows, matrix->Cols);

-------bekas (оптимизация)
printf("%s:\n%d x %d\n", matrix->Name, matrix->Rows, matrix->Cols);

------LynxGAV
for(i=0; i<matrix->Rows; i++)
{
for(j=0; j<matrix->Cols; j++)
printf(" %.4f", matrix->Values[i * matrix->Cols + j]);
putchar('\n');
}

-------bekas (оптимизация)
for(i=0; i<matrix->Rows; i++)
{
work = i * matrix->Cols;
for(j=0; j<matrix->Cols; j++)
printf(" %.4f", matrix->Values[work + j]);
putchar('\n');
}

------LynxGAV
gets(dimensions);
ptr = strchr(dimensions, ' ');
if (!ptr)
{
puts("Wrong dimensions!");
return;
}
ptr++;
rows = atoi(dimensions);
cols = atoi(ptr);
if ((rows < 1) || (cols < 1))
{
puts("Wrong dimensions!");
return;
}

-------bekas (упрощение)

gets(dimensions);
if(sscanf(dimensions, "%d %d", &rows, &cols) != 2 || rows < 1 || cols < 1)
{
puts("Wrong dimensions!");
return;
}

------LynxGAV

for(i=0; i<rows; i++)
for(j=0; j<cols; j++)
{
ptr = strchr(ptr1, ';');
if (!ptr)
continue;
matrix->Values[i * cols + j] = atof(ptr1);
ptr1 = ptr + 1;
}

-------bekas

Достаточно здесь иметь ситуацию, когда нет ни одного символа ';'
(или их менее rows * cols), и мы будем иметь ситуацию с мусорными
данными в matrix->Values[]. Это связано с тем, что new_matrix()
выделяет память под массив, но не инициализирует его начальными
значениями, поэтому в new_matrix() необходимо выполнить хотя бы

matrix->Values = (float*)malloc(rows * cols * sizeof(float));
memset(matrix->Values, 0, rows * cols * sizeof(float));

И вообще, если преподаватель потребует контроль ввода данных,
то программу придется переделать

 
 
 
 
Сообщение20.12.2005, 19:54 
To незванный гость:
Да, замечания больше относятся к С++, в котором struct == class и можно приписать структуре member-функции. Поэтому запись
Код:
struct Т {
  int x;
};

вполне законна и правильна. И поэтому можно писать
Код:
struct T x;
void foo(struct T * y)

а можно и просто
Код:
T x;
void foo(T* y)


Текст
Код:
typedef struct Т {
  int x;
} y, *z;

на самом деле эквивалентен
Код:
struct Т {
  int x;
};
typedef struct Т y, *z;

Т.е. мы сначала определяем структуру, а затем два собственных типа - это y (== T) и z (== T*). Разбор строки такой получачается. И тоже самое для
Код:
typedef struct {
  int x;
} T;

- где мы сначала определяем безымянную TEMP-структуру, а потом говорим, что T - это она самая. Кроме того, попробуйте с помошью последней записи сделать список с перекрестными ссылками - ничего не выйдет, так как имя структуры T не определено в момент разбора структуры компилятором и нужен тег.
Код:
typedef struct {
  int x;
  T* p;
} T;

Это я по поводу записи без тега.

А по поводу предобъявлений - если код большой, и/или переменная нужна только во вложенном блоке, то ее нужно объявлять там, где она нужна. Переменная живет только в блоке и возможно ее повторное использование в другом блоке. А самое главное - это исключает ошибки с инициализацией. Если мы используем одну глобальную по отношению к нескольким блокам переменную, а в каждом блоке нужны свои значения, то пропуск инициализации перед некоторым блоком может привести к ошибке и это является одним из часто встречающихся багов.
А так - нужна нам переменная внутри блока - создаем ее именно там, где она впервые понадобилась, что значительно повышает читаемость. Так как в противном случае Вам нужно смотреть в начале функции, какие имена уже были использованы и какой тип.
ИМХО, предобъявление - это беда старых языков и от нее нужно отказываться.

Просто зачем в С/С++ писать
Код:
int i, j, k;
.............
for (i = 0; i < 10; i++)
{
     for (j = 0; j < 10; j++)
     {
          .....
          k = i*j;
          .....
     }
}

Когда можно проще и нагляднее -
Код:
for (int i = 0; i < 10; i++)
{
     for (int j = 0; j < 10; j++)
     {
          .....
          int k = i*j;
          .....
     }
}

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

 
 
 
 
Сообщение20.12.2005, 20:08 
Аватара пользователя
:evil:
Я (почти всегда) согласен с Вами, коли мы согласимся на C++.

С и С++ - два разных языка. Для меня Ваше обозначение C/C++ почти также режет глаз, как C/C#, Pascal/Oberon, или латинский/португальский. Что-то общее есть, но языки разные и практика программирования весьма различна. Quod licet C++ non licet C.

 
 
 [ Сообщений: 105 ]  На страницу Пред.  1 ... 3, 4, 5, 6, 7  След.


Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group