HEADER FILE-如果我将字符大小从30更改为20或更小,代码可以正常工作,但如果字符大小为30或更高,则在主代码中第5个大小写的for循环之后不会执行。
struct Data
{
int Emp_id;
char Emp_name[30];
char Emp_city[30];
};
void input(struct Data emp[], int n)
{
for (int i = 0; i < n; i++)
{
printf("enter employee id of %d employee: n", i + 1);
scanf("%d", &emp[i].Emp_id);
fflush(stdin);
printf("enter employee name of %d employee: n", i + 1);
gets(emp[i].Emp_name);
fflush(stdin);
printf("enter employee city of %d employee: n", i + 1);
gets(emp[i].Emp_city);
}
}
MAIN CODE-add函数在第五种情况下执行,如果头文件结构中的字符大小为30或更大,则循环停止。如果我减少一次迭代,使循环像这样——for(int I=n-5;I<n-1;I++),但包括最后一次迭代会终止程序,显示函数不会执行,那么代码也会工作。
#include <stdio.h>
#include <string.h>
#include "data.h"
void find(struct Data emp[], int n);
void sortid(struct Data emp[], int n);
void sortAlp(struct Data emp[], int n);
void count(struct Data emp[], int n);
void add(struct Data emp[], int n);
void display(struct Data emp[], int n);
int main(void)
{
int n;
int a;
printf("enter the number of employees:n");
scanf("%d", &n);
struct Data emp[n];
input(emp, n);
printf("enter the operation you want to perform:n");
printf("1 - to find employee record from employee idn");
printf("2 - to sort employee record on basis of employee idn");
printf("3 - to alphabetically sort array of charactersn");
printf("4 - to count the number of employees in databasen");
printf("5 - to add 5 more recordsn");
scanf("%d", &a);
switch (a)
{
case 1:
find(emp, n);
break;
case 2:
sortid(emp, n);
display(emp, n);
break;
case 3:
sortAlp(emp, n);
display(emp, n);
break;
case 4:
count(emp, n);
break;
case 5:
add(emp, n);
break;
}
return 0;
}
void find(struct Data emp[], int n)
{
int a;
int count = 0;
printf("Enter the employee id: ");
scanf("%d", &a);
for (int i = 0; i < n; i++)
{
if (a == emp[i].Emp_id)
{
printf("Employee id: %dn", emp[i].Emp_id);
printf("Employee name: %sn", emp[i].Emp_name);
printf("Employee city: %sn", emp[i].Emp_city);
count++;
}
}
if (count == 0)
{
printf("Employee id does not exist");
}
}
void sortid(struct Data emp[], int n)
{
int temp;
char temp2[30];
char temp3[30];
for (int i = 0; i < n; i++)
{
for (int j = i + 1; j < n; j++)
{
if (emp[i].Emp_id > emp[j].Emp_id)
{
temp = emp[i].Emp_id;
emp[i].Emp_id = emp[j].Emp_id;
emp[j].Emp_id = temp;
strcpy(temp2, emp[i].Emp_name);
strcpy(emp[i].Emp_name, emp[j].Emp_name);
strcpy(emp[j].Emp_name, temp2);
strcpy(temp3, emp[i].Emp_city);
strcpy(emp[i].Emp_city, emp[j].Emp_city);
strcpy(emp[j].Emp_city, temp3);
}
}
}
}
void sortAlp(struct Data emp[], int n)
{
int temp2;
char temp[30];
char temp3[30];
for (int i = 0; i < n; i++)
{
for (int j = i + 1; j < n; j++)
{
if (emp[i].Emp_name[0] > emp[j].Emp_name[0])
{
strcpy(temp, emp[i].Emp_name);
strcpy(emp[i].Emp_name, emp[j].Emp_name);
strcpy(emp[j].Emp_name, temp);
temp2 = emp[i].Emp_id;
emp[i].Emp_id = emp[j].Emp_id;
emp[j].Emp_id = temp2;
strcpy(temp3, emp[i].Emp_city);
strcpy(emp[i].Emp_city, emp[j].Emp_city);
strcpy(emp[j].Emp_city, temp3);
}
}
}
}
void count(struct Data emp[], int n)
{
int k;
for (int i = 0; i < n; i++)
{
k = i;
}
printf("number of employees: %dn", k + 1);
}
void add(struct Data emp[], int n)
{
n = n + 5;
for (int i = n - 5; i < n; i++)
{
printf("enter employee id of %d employee: n", i + 1);
scanf("%d", &emp[i].Emp_id);
fflush(stdin);
printf("enter employee name of %d employee: n", i + 1);
gets(emp[i].Emp_name);
fflush(stdin);
printf("enter employee city of %d employee: n", i + 1);
gets(emp[i].Emp_city);
}
//Program stops here without executing display
display(emp, n);
}
void display(struct Data emp[], int n)
{
for (int i = 0; i < n; i++)
{
printf("employee id of %d employee: %d n", (i + 1), emp[i].Emp_id);
printf("employee name of %d employee: %s n", (i + 1), emp[i].Emp_name);
printf("employee city of %d employee: %s n", (i + 1), emp[i].Emp_city);
}
}
对数组大小的依赖是巧合,缓冲区溢出总是在第五种情况下发生。
在程序中,读取n
,然后分配一个n
条目数组。n
的后续更改(如果有)(实际上没有,add
中的操作会更改副本)不会更改数组大小。通过这种方式,add
通过数组写入数据,这是被禁止的,可能会使程序崩溃,甚至更糟。
您需要的是动态内存分配,例如:
// create an array of n entries
struct Data *emp = malloc(n * sizeof(struct Data));
// grow it (or shrink) to new_n entries
emp = realloc(emp, new_n * sizeof(struct Data));
n = new_n;
// destroy it
free(emp);
emp = NULL; // safeguard, should be right after free
请注意,在增长数组时,需要更改代码中使用的实际emp
和n
,而函数只获得副本(在emp
的情况下是指针的副本,而不是整个数组的副本)。因此,要么在调用add
之前在main
中执行此操作,要么将其更改为接受指向这些变量的指针,让它自己执行此操作。
printf("enter the number of employees:n");
scanf("%d", &n);
struct Data emp[n];
这里有一个问题:不能只读取n,然后声明emp[n]
元素的数组。您在运行时读取它,但编译器需要知道要为多少emp
分配空间。编译器在编译时需要这些信息。它不能等到scanf()
运行。
您必须使用常数值,或使用动态分配,如下面的//1 //2
和//3
#define EMP_SIZE 42
struct Data emp1[EMP_SIZE]; // 1
struct Data emp2[42]; // 2
n = n + 5; // 3
struct Data* emp3 = (struct Data*) malloc(n*sizeof(struct Data));
出于同样的原因
void add(struct Data emp[], int n)
{
n = n + 5;
for (int i = n - 5; i < n; i++)
{}
也不起作用。我相信你假设设置n = n + 5;
会以某种方式扩展数组并分配5个新的emp
。不会的。它只会将n
(一个int
)设置为n + 5
。在for
中,您正试图重写这些新的emp
记录。
好吧,没有新的记录,你的程序很快就会崩溃。
有关代码的更多信息
你现在可能有点厌倦了到处写struct Data
。一般来说,人们习惯于写
typedef struct
{
int Emp_id;
char Emp_name[30];
char Emp_city[30];
} Data;
此命令将Data
定义为匿名struct
。它不创建一个,只是为它、为整个集合定义一个名称。
你可以写
void input(Data emp[], int n);
而不是
void input(struct Data emp[], int n);
标头的使用
通常,.h
文件包含定义和原型。这个想法是在两个文件的集合中创建代码。标头包含声明和原型。CCD_ 27文件具有用于函数的代码。
第三个文件承载main()
。为什么?因为以这种方式编写,您有许多实现main()
并测试程序不同功能的.c
文件。
当函数正常时,你可以保留(或出售)函数的编译代码,以及带有声明的.h
,这样其他人就可以在他们的程序中使用它,只需包含标题并将链接器指向编译形式的函数代码。这种编译形式被称为library
。这与通过包括CCD_ 34来使用CCD_。在Unix中,libc
是库,stdlib.h
是头。
绕过数据
void find(struct Data emp[], int n);
void sortid(struct Data emp[], int n);
void sortAlp(struct Data emp[], int n);
void count(struct Data emp[], int n);
void add(struct Data emp[], int n);
void display(struct Data emp[], int n);
缩小代码,所有函数都处理一个struct Data
数组,而n
是数组大小。它被称为容器,是继简单array
本身之后最常见的结构。
所以你可以写
typedef struct
{
int id;
char name[20];
char city[20];
} Employee;
typedef struct
{
unsigned N;
unsigned capacity;
Employee* E;
} Data;
这里,Data
是Employee
的容器。但在Data
内部是N
大小的组件,它真的很方便。这在OOP文献中被称为封装。
例如,重构代码
我将重新排列您的代码并重新实现一些函数,这样您就可以比较这两种方式。写这篇文章还有很多其他的方式,我甚至不是说这篇是好的,或者你的是坏的。
修改后的页眉
// f1-data.h
typedef struct
{
int id;
char name[20];
char city[20];
} Employee;
typedef struct
{
unsigned N;
unsigned capacity;
Employee* E;
} Data;
Data* create_Data(unsigned);
Data* delete_Data(Data*);
int add(Employee*, Data*);
int add5(Data*);
int count(Data*);
void display(Data*,const char*);
Employee* find(int,Data*);
Employee* input();
//void sortid(Data*);
//void sortAlp(Data*);
// f1-data.h end
关于变化:
Data
现在是Employee
的集合。从您的代码中可以看出,大小必须是动态的,因此Data
有一个字段capacity
,它包含当前容量,还有一个字段N
包含当前大小。E
是Employee的数组。一个简短的名字很有帮助,因为它被使用了很多次。我将编写add5()
作为扩展结构的常见方法的示例create_data()
返回指向具有要求容量的数据集的指针- CCD_ 52返回CCD_。其目的是提供一种在删除集合后使指针无效的简单方法
find()
返回指向具有给定id
或NULL
的Employee
的指针。这种方式更实用,而不是每次调用都向屏幕写入的函数add5()
被实现为如何扩展阵列的示例input()
返回一个指向Employee
的指针,因此我们可以用数据测试程序,而根本不需要交互- 在结构内部的字段中不需要CCD_ 61前缀。我们可以想象,
Employee
struct
内部的字段是Employee
数据 - 将CCD_ 65改为CCD_。建议任何程序都不要在名称开头使用一个或多个下划线。这些在某种程度上是留给库实现者的
display()
现在有一个标题字符串,因为它在示例中很有用- 文件
.c
中的函数代码命名为h.
。我将按顺序全部3个,以便更容易地剪切和粘贴您想要测试的网站案例
main()用于测试
int main(void)
{
Data* set1 = create_Data(4);
display(set1, "empty. (just testing)");
add5(set1);
display(set1, "empty. (added 5 in cap.)");
// now inserts until error
printf("inserting employees until errorn");
Employee* e = NULL;
while (set1->N < set1->capacity)
{ e = input();
add(e, set1);
free(e);
}
display(set1, "Now set should be full");
set1 = delete_Data(set1);
}
逻辑:
- 创建一个容量为4
Employee
的集合 - 调用
display()
- 该集合扩展为5条以上的记录并显示
- 调用调用
input()
并插入记录,直到出现错误 - 调用
display()
以显示现在的完整集合 - 则该集合被删除
测试输出
empty. (just testing)
0 Employees [Cap. 4]
empty. (added 5 in cap.)
0 Employees [Cap. 9]
inserting employees until error
Now set should be full
9 Employees [Cap. 9]
1 420 Jhonny 420 Jr [ City 420 ]
2 421 Jhonny 421 Jr [ City 421 ]
3 422 Jhonny 422 Jr [ City 422 ]
4 423 Jhonny 423 Jr [ City 423 ]
5 424 Jhonny 424 Jr [ City 424 ]
6 425 Jhonny 425 Jr [ City 425 ]
7 426 Jhonny 426 Jr [ City 426 ]
8 427 Jhonny 427 Jr [ City 427 ]
9 428 Jhonny 428 Jr [ City 428 ]
完整代码(3个文件)
// f1-data.h
typedef struct
{
int id;
char name[20];
char city[20];
} Employee;
typedef struct
{
unsigned N;
unsigned capacity;
Employee* E;
} Data;
Data* create_Data(unsigned);
Data* delete_Data(Data*);
int add(Employee*, Data*);
int add5(Data*);
int count(Data*);
void display(Data*,const char*);
Employee* find(int,Data*);
Employee* input();
//void sortid(Data*);
//void sortAlp(Data*);
// f1-data.h end
// f1-data.c
#include <stdio.h>
#include <stdlib.h>
#include "f1-data.h"
int add(Employee* E, Data* S)
{
// add Employee E
if (S->N == S->capacity) return -1; // full
// space we have: copy Employee
S->E[S->N] = *E;
S->N += 1; // count one
return S->N;
}
int add5(Data* Set)
{ // adds 5 records to Set
if (Set == NULL) return -1;
int n_size = 5 + Set->capacity;
Employee* temp = (Employee*)realloc(Set->E, n_size * sizeof(Employee));
if (temp == NULL) return -2; // could not extend?
Set->E = temp; // that is all
Set->capacity = n_size;
return 0;
}
int count(Data* S){ return S->N; }
Data* create_Data(unsigned cap)
{
Data* set = (Data*)malloc(cap * sizeof(Data));
set->N = 0; // empty
set->capacity = cap;
set->E = (Employee*)malloc(cap * sizeof(Employee));
return set;
}
Data* delete_Data(Data* set)
{
if (set == NULL) return NULL;
free(set->E); // frees Employees first
free(set);
return NULL;
}
void display(Data* Set, const char* msg)
{
if (msg != NULL) printf("%sn", msg);
printf("%d Employees [Cap. %u]nn", Set->N, Set->capacity);
for (unsigned i = 0; i < Set->N; i++)
printf("
%3u %8d %20s [ %s ]n",
i + 1,
Set->E[i].id,
Set->E[i].name,
Set->E[i].city
);
printf("n");
}
Employee* find(int id, Data* S)
{
for (unsigned i = 0; i < S->N; i++)
if (id == S->E[i].id)
{ // found
Employee* found = (Employee*)malloc(sizeof(Employee));
*found = S->E[i];
return found;
}
return NULL;
}
Employee* input()
{ // returns a new Employee record
static int id = 420;
Employee* new_E = (Employee*)malloc(sizeof(Employee));
new_E->id = id;
sprintf(new_E->name, "Jhonny%4d Jr", id);
sprintf(new_E->city, "City %4d", id);
id += 1;
return new_E;
}
//void sortid(Data* Set){};
//void sortAlp(Data* Set){};
// f1-data.c end
// f1.c
#include <stdio.h>
#include <stdlib.h>
#include "f1-data.h"
int main(void)
{
Data* set1 = create_Data(4);
display(set1, "empty. (just testing)");
add5(set1);
display(set1, "empty. (added 5 in cap.)");
// now inserts until error
printf("inserting employees until errorn");
Employee* e = NULL;
while (set1->N < set1->capacity)
{ e = input();
add(e, set1);
free(e);
}
display(set1, "Now set should be full");
set1 = delete_Data(set1);
}
//f1.c end
关于排序
请注意,排序函数只是更改排序字段,因此您应该只传递compare函数,并编写一个排序。或者,只调用qsort()
审阅者注意:我总是投射malloc()
返回的指针,我知道一些90年代的文本(如C-FAQ)会针对它进行写操作。无需向我指出这一点。