这是我的代码。它读取第一个头行并忽略它,但第一个头行之后的所有内容都应该输入到结构中。但是当我尝试打印出结构数组时,它不起作用。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct User {
char *name;
char *process;
int arrival;
int duration;
}User;
User userList[100];
int numOfJobs = 0;
char header[20];
int main(int argc, char **argv) {
scanf("%s %s %s %s", header, header, header, header);
while(EOF != scanf("%s %s %d %d", userList[numOfJobs].name, userList[numOfJobs].process, &userList[numOfJobs].arrival, &userList[numOfJobs].duration)) {
numOfJobs++;
}
for(int i = 0; i < numOfJobs; i++) {
printf("%s %s %d %d", userList[i].name, userList[i].process, userList[i].arrival, userList[i].duration);
}
return 0;
}
在struct User
中,您声明两个指针,name
和process
,例如
typedef struct User {
char *name;
char *process;
int arrival;
int duration;
} User;
在您可以使用任何一个之前,它们必须指向有效的存储。如果您打算将字符串复制到它们,则必须分配足够容纳每个字符串的存储块,将块的起始地址分配给每个指针,使它们指向有效内存,然后将字符串复制至分配的块,然后通过指针引用该块。
实现这一切的最简单方法是首先将name
和process
读取到一个临时字符数组中,这样您就可以获得需要存储的字符串的长度,然后分配存储空间(<1用于nul终止字符(,并将字符串复制到分配的块中。
您无法围绕scanf()
构建任何类型的健壮输入路由。对于新的C程序员来说,这充满了陷阱。相反,所有用户输入都应该使用fgets()
来完成,以确保每次都使用一行完整的用户输入,然后使用sscanf()
从填充了fgets()
的缓冲区中截取所需的值。通过这种方式,您可以验证(1(输入的读取;以及(2(分别解析来自输入的信息。最重要的是,后续读取不会因匹配失败而受阻在输入缓冲区中留下未读字符。
从你的描述中,你想阅读/放弃第一行,这就是你使用scanf("%s %s %s %s", header, header, header, header);
的原因。不要那样做。只需使用fgets()
将第一行读取到缓冲区中并忽略它。这样,如果第一行的单词少于4个,则代码不会因额外输入而阻塞。
总之,你会做:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXC 1024 /* if you need a constant, #define one (or more) */
#define MAXU 100
typedef struct User {
char *name;
char *process;
int arrival;
int duration;
} User;
int main (void) {
char buf[MAXC]; /* buffer to handle user-input */
User userList[MAXU]; /* don't declare global, pass as parameter */
int numOfJobs = 0;
if (!fgets (buf, MAXC, stdin)) /* read/discard first line */
return 1;
while (numOfJobs < MAXU && fgets (buf, MAXC, stdin)) { /* protect bounds, read lines */
char tmpname[MAXC], tmpproc[MAXC]; /* temporary storage for name, process */
/* split line with strings into tmpname, tmpproc */
if (sscanf (buf, "%s %s %d %d", tmpname, tmpproc,
&userList[numOfJobs].arrival,
&userList[numOfJobs].duration) == 4) {
size_t len = strlen (tmpname); /* get lenth of tmpname */
/* allocate / validate storage for name */
if (!(userList[numOfJobs].name = malloc (len + 1))) {
perror ("malloc-name");
break;
} /* copy tmpname to name */
memcpy (userList[numOfJobs].name, tmpname, len + 1);
len = strlen (tmpproc); /* get length of tmpproc */
/* allocate / validate storage for process */
if (!(userList[numOfJobs].process = malloc (len + 1))) {
perror ("malloc-process");
break;
} /* copy tmpproc to process */
memcpy (userList[numOfJobs].process, tmpproc, len + 1);
}
numOfJobs++; /* only increment on success */
}
for(int i = 0; i < numOfJobs; i++) {
printf ("%s %s %d %dn", userList[i].name, userList[i].process,
userList[i].arrival, userList[i].duration);
free (userList[i].name); /* free all allocated memory */
free (userList[i].process);
}
}
(注意:使用numOfJobs < MAXU && fgets (buf, MAXC, stdin)
作为读取循环条件,其中numOfJobs < MAXU
通过防止您在输入的信息行数超过MAXU
(100
(行时写入超出数组的内容来保护userList
数组边界(
(注意2:不需要声明全局变量。而是在main()
中声明它们,并将其作为参数传递给任何需要它们的函数(
由于您还没有提供一个示例输入文件,所以这段代码还没有经过测试,但根据您的描述,它应该可以按预期工作我制作了一些样本数据来检查代码,并向您展示如何检查内存使用情况中的任何错误:
示例输入文件
$ cat dat/alloc_struct_ptrs.txt
my dog has fleas -- bummer
name_1 process-1.8 2542 1542
name_2 process-2.9 2982 2982
name_3 process-3.0 1124 3124
name_4 process-4.1 1118 4118
name_5 process-5.2 4323 5323
name_6 process-6.3 2761 6761
name_7 process-7.4 6914 7914
name_8 process-8.5 2022 8022
name_9 process-9.6 9539 9539
示例使用/输出
$ ./bin/alloc_struct_ptrs < dat/alloc_struct_ptrs.txt
name_1 process-1.8 2542 1542
name_2 process-2.9 2982 2982
name_3 process-3.0 1124 3124
name_4 process-4.1 1118 4118
name_5 process-5.2 4323 5323
name_6 process-6.3 2761 6761
name_7 process-7.4 6914 7914
name_8 process-8.5 2022 8022
name_9 process-9.6 9539 9539
内存使用/错误检查
在您编写的任何动态分配内存的代码中,对于分配的任何内存块,您都有2个责任:(1(始终为内存块保留一个指向起始地址的指针,因此,(2(当不再需要时,它可以被释放。
您必须使用内存错误检查程序来确保您不会试图访问内存或在分配的块的边界之外写入,尝试读取或基于未初始化的值进行条件跳转,最后确认您释放了所有分配的内存。
对于Linux,valgrind
是正常的选择。每个平台都有类似的内存检查器。它们都很容易使用,只需通过它运行您的程序即可
$ valgrind ./bin/alloc_struct_ptrs < dat/alloc_struct_ptrs.txt
==18317== Memcheck, a memory error detector
==18317== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==18317== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==18317== Command: ./bin/alloc_struct_ptrs
==18317==
name_1 process-1.8 2542 1542
name_2 process-2.9 2982 2982
name_3 process-3.0 1124 3124
name_4 process-4.1 1118 4118
name_5 process-5.2 4323 5323
name_6 process-6.3 2761 6761
name_7 process-7.4 6914 7914
name_8 process-8.5 2022 8022
name_9 process-9.6 9539 9539
==18317==
==18317== HEAP SUMMARY:
==18317== in use at exit: 0 bytes in 0 blocks
==18317== total heap usage: 20 allocs, 20 frees, 5,291 bytes allocated
==18317==
==18317== All heap blocks were freed -- no leaks are possible
==18317==
==18317== For counts of detected and suppressed errors, rerun with: -v
==18317== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
请始终确认您已经释放了分配的所有内存,并且没有内存错误。
仔细看看,如果你还有问题,请告诉我。