#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
файла, включающего этот заголовок, результирующий объектный файл имеет свое собственное определение этих переменных. Когда вы пытаетесь связать объектные файлы в один исполняемый файл, все эти определения переменных конфликтуют друг с другом.
Правильный ответ — не использовать глобальные переменные-функции должны взаимодействовать через параметры и возвращаемые значения, а не через глобальные. Использование глобалов уничтожает возможность модуляции кода, как вы пытаетесь это сделать.
Просмотрите все функции вашей программы и задайте два вопроса:
- Какая информация необходима этой функции для выполнения всех ее задач? — ответом на этот вопрос будут ваши входные аргументы.
- Какие данные или изменение состояния ожидает вызывающий абонент от этой функции? — ответом на этот вопрос будет ваше возвращаемое значение и/или любые выходные аргументы.
Давайте возьмем 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 выше, вы также должны включить полный список параметров с каждым прототипом функции.