Передача нескольких функций на аутсорсинг в несколько файлов на языке Си

#c

Вопрос:

У меня проблема с моим кодом. Я запрограммировал систему управления аптекой (2000 loc). Теперь я хочу передать на аутсорсинг несколько функций (Поставщик(6 функций), Заказчик(6 функций) и Медицина(9 функций)) в 3 файла c.

Код прекрасно работает в одной функции C, но почему-то он выдает мне ошибки при размещении его в разных файлах .c. Похоже, это как-то связано с моим заголовочным файлом, который входит в каждый файл .c. Потому что в нем говорится, что в заголовке есть несколько объявлений материала.(Это работало, когда у вас был только 1 файл .c).

Вот ошибки при компиляции:
Ошибки при компиляции.

Это мой файл заголовка:

 #ifndef MAINPROJECTNIKLASLOREK_H_
#define MAINPROJECTNIKLASLOREK_H_

//Anlegen eines const char pointer um das Format des Medizinstructs festzulegen und später
bei printf Ausgaben direkt anzuwenden
const char* medicine_format ="ntMediID:%sntMediName:%sntQuantity:%dntPrice:%.2f    
ntExp.Date:%sntCompany:%sntSupplier:%sntInfo:%sn";
//Anlegen eines Structs für die Medizin
struct Medicine{
int quantity;
float price;
char expdate[15];
char company[20];
char supplier[30];
char mediname[20];
char info[1000];
char mediid [30];
};
const char* supplier_format = "ntSupplierID:t%sntSupplierName:t%sntMobileNumber:  
t%sntCompany:t%sn";
//Anlegen eines Structs für die Zulieferer
struct Supplier{
char supplierid[30];
char suppliername[30];
char mobno[15];
char company[50];
};

const char* customer_format = "ntCustomerID:t%sntCustomerName:t%sntMobileNumber:  
t%sn";
//Anlegen eines Structs für die Kunden
struct Customer{
char customerid[30];
char customername[30];
char mobno[15];
};
// Struct um Kassenzettel einzuspeichern
struct bill{
char billid[30];
char mediname[30];
int medi_qty;
float price;
float total;
float newtotal;
float temptotal;
};

//Globale Variablen für Coupon und Prozent. Diese werden von der Main Funktion überschrieben,     
wenn sie benutzt werden.
//Sie werden nur! in der Sellmed Funktion angewendet, um einen Discount auf einen Medizin     
Verkauf anzuwenden.
#define SIZE 20
char coup[SIZE]={'n','o','c','o','u','p'};
float dis = 1.00;

//main menu Funktion:
void mainmenu();
//Kunde Funktionen Prototypen:
void customer();
void addcus();
void cuslist();
void searchcus();
void editcus();
void deletecus();
//Zulieferer Funktionen Prototypen:
void supplier();
void addsup();
void suplist();
void searchsup();
void editsup();
void deletesup();

//Medizin Funktionen Prototypen:
void medicine ();
void leave();
void addmed();
void sellmed();
void listing();
void searchmed();
void editmed();
void deletemed();
void pharwelc();

// Bestandsfunktionen Prototypen:
void searchstock();
void stock();

#endif /* MAINPROJECTNIKLASLOREK_H_ */
 

Комментарии:

1. Что непонятно? Если вы включите этот заголовок MAINPROJECTNIKLASLOREK_H_ в несколько модулей, то все переменные (как, например, переменная medicine_format), объявленные в заголовке, будут определены несколько раз.

2. Объявления функций не являются надлежащими прототипами функций. Если функция не имеет аргументов, она должна быть объявлена в void качестве списка параметров, например void mainmenu(void) . Если функция имеет аргументы, они должны быть указаны в прототипе, например void searchcus(char *customerid) .

3. Осторожно, @VladfromMoscow, если заголовок включен в несколько единиц перевода, то все переменные, определенные им, определяются несколько раз. Решение, как я знаю, вам известно, заключается в том, чтобы заголовок использовал объявления, которые не являются определениями или предварительными определениями, и включал определения ровно в один исходный файл.

Ответ №1:

const char* supplier_format =

Вы не можете поместить этот материал в заголовочные файлы. Заголовочные файлы должны содержать только extern const char* supplier_format; те места, где инициализация находится ровно в одном файле C.

Я просмотрел весь файл заголовка; предполагая, что файла заголовка достаточно для описания проблемы, есть несколько примеров именно этой проблемы и ничего больше. Объявления структуры и функции не нуждаются в исправлении.

Комментарии:

1. … и extern (как показано) является обязательным в заголовке, даже если внешняя связь используется по умолчанию для объявлений области действия файла.

2. Объявления функций действительно нуждаются в исправлении. Это недопустимые прототипы функций.

3. @user3386109: Да, это так. Либо он использует старый C, в котором они являются допустимыми прототипами для функций, которые принимают неизвестное количество аргументов, либо он использует новый C, и в этом случае они являются допустимыми прототипами для функций, которые не принимают аргументов. Учитывая очевидный способ создания этого заголовка, я предполагаю, что функции на самом деле не принимают аргументов, что в любом случае не создает проблем.

4. @Joshua Нет, они не являются прототипами функций. Это объявления функций, которые НЕ являются прототипами. И объявления функций, которые не являются прототипами, разрешены только для обратной совместимости. Они не должны использоваться в новом коде. Кроме того, прототипы функций были частью C89, поэтому, даже если OP использует старый компилятор, они все равно могут перенять лучшие практики, используя правильные прототипы функций.


Ответ №2:

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

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

Просмотрите все функции вашей программы и задайте два вопроса:

  1. Какая информация необходима этой функции для выполнения всех ее задач? — ответом на этот вопрос будут ваши входные аргументы.
  2. Какие данные или изменение состояния ожидает вызывающий абонент от этой функции? — ответом на этот вопрос будет ваше возвращаемое значение и/или любые выходные аргументы.

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

  • Пример того, как struct Customer ;
  • Выходной поток;

и ответ на вопрос 2 — «ничего», так как это строго функция вывода.

Таким образом, объявление customer в заголовочном файле становится:

 void customer( struct Customer, FILE * );
 

и его определение в исходном файле становится

 void customer( struct Customer c, FILE *stream )
{
  const char* customer_format = "ntCustomerID:t%sntCustomerName:t%sntMobileNumber:t%sn";

  fprintf( stream, customer_format, c.customerid, c.customername, c.mobno );
}
 

Где-то в вашем main файле у вас будет экземпляр struct Customer defined, поэтому вы назовете его как:

 int main( void )
{
  ...
  struct Customer c;
  ... // assume data is read into c here
  customer( c, stdout );
  ...
}
 

Точно так же мы можем посмотреть на addcus . Ответы на вопрос 1 будут следующими

  • поток, из которого считываются данные о клиентах

и ответы на вопрос 2 будут

  • новые данные о клиентах
  • указание на то, была ли операция успешной или нет

Чтобы указать, была ли операция ввода успешной, у нас будет функция возврата true или false . Функции необходимо место для хранения входных данных — вызывающий объект предоставит это, передав указатель на struct Customer объект. Таким образом, наше объявление в заголовочном файле будет

 bool addcus( struct Customer *, FILE * );
 

и определение в файле .c будет

 bool addcus( struct Customer *c, FILE *stream )
{
  if ( stream == stdin )
    fprintf( "Enter customer ID: " );
  if ( !fgets( c->customerid, sizeof c->customerid, stream ) )
    // input error - for now, we'll just return false
    return false;

  // repeat for customer name and mobile number
  
  // if everything is read successfully, return true
  return true;
}
 

Так что теперь мы можем немного обновить нашу main функцию:

 int main( void )
{
  ...
  struct Customer c;
  if ( addcus( amp;c, stdin ) )
    customer( c, stdout );
  ...
}
 

Вам нужно сделать это для каждой функции в вашей программе.

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

Ответ №3:

То, что вы хотите, — это что-то вроде этого:

 #ifndef MAINPROJECTNIKLASLOREK_H_
#define MAINPROJECTNIKLASLOREK_H_

// Anlegen eines const char pointer um das Format des 
// Medizinstructs festzulegen und später bei printf Ausgaben
// direkt anzuwenden
extern const char* medicine_format;

//Anlegen eines Structs für die Medizin
struct Medicine {
    int quantity;
    float price;
    char expdate[15];
    char company[20];
    char supplier[30];
    char mediname[20];
    char info[1000];
    char mediid [30];
};
extern const char* supplier_format;

// Anlegen eines Structs für die Zulieferer
struct Supplier{
    char supplierid[30];
    char suppliername[30];
    char mobno[15];
    char company[50];
};
extern const char* customer_format;

// Anlegen eines Structs für die Kunden
struct Customer{
    char customerid[30];
    char customername[30];
    char mobno[15];
};

// Struct um Kassenzettel einzuspeichern
struct bill {
    char billid[30];
    char mediname[30];
    int medi_qty;
    float price;
    float total;
    float newtotal;
    float temptotal;
};

// Globale Variablen für Coupon und Prozent. Diese werden von
// der Main Funktion überschrieben, wenn sie benutzt werden.
// Sie werden nur! in der Sellmed Funktion angewendet, um
// einen Discount auf einen Medizin Verkauf anzuwenden.
#define SIZE 20
char coup[SIZE] = { 'n', 'o', 'c', 'o', 'u', 'p' };
float dis = 1.00;

// Main menu Funktion:
extern void mainmenu(*actual parameters go here*);

// Kunde Funktionen Prototypen:
extern void customer(*actual parameters go here*);
extern void addcus(*actual parameters go here*);
extern void cuslist(*actual parameters go here*);
extern void searchcus(*actual parameters go here*);
extern void editcus(*actual parameters go here*);
extern void deletecus(*actual parameters go here*);

// Zulieferer Funktionen Prototypen:
extern void supplier(*actual parameters go here*);
extern void addsup(*actual parameters go here*);
extern void suplist(*actual parameters go here*);
extern void searchsup(*actual parameters go here*);
extern void editsup(*actual parameters go here*);
extern void deletesup(*actual parameters go here*);

// Medizin Funktionen Prototypen:
extern void medicine(*actual parameters go here*);
extern void leave(*actual parameters go here*);
extern void addmed(*actual parameters go here*);
extern void sellmed(*actual parameters go here*);
extern void listing(*actual parameters go here*);
extern void searchmed(*actual parameters go here*);
extern void editmed(*actual parameters go here*);
extern void deletemed(*actual parameters go here*);
extern void pharwelc(*actual parameters go here*);

// Bestandsfunktionen Prototypen:
extern void searchstock(*actual parameters go here*);
extern void stock(*actual parameters go here*);

#endif /* MAINPROJECTNIKLASLOREK_H_ */
 

Затем вы помещаете фактические инициализаторы строк в наиболее подходящий исходный файл. Например, если все функции в прототипах «Medezine» medezine.c включены , то, возможно, это должно быть в том же исходном файле:

 const char* medicine_format = "ntMediID:%sn"
           "tMediName:%sntQuantity:%dntPrice:%.2fn"
           "tExp.Date:%sntCompany:%sntSupplier:%sn"
           "tInfo:%sn";
 

Как указано @user3386109 выше, вы также должны включить полный список параметров с каждым прототипом функции.