我正在尝试做一个简单的结构示例,但我不明白为什么它不打印 C 中的内容

I am trying to do a simple struct example but I don't understand why it is not printing the contents in C

这是我的代码。它读取第一个 header 行并忽略它,但应该在结构中输入第一个 header 行之后的所有内容。但是当我尝试打印出结构数组时,它不起作用。

#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中声明了两个指针,nameprocess,例如

typedef struct User {
    char *name;
    char *process;
    int arrival;
    int duration;
} User;

在您可以使用任何一个之前,它们必须指向有效的存储。如果您打算将字符串复制到它们,那么您必须分配足以容纳每个字符串的存储块,将块的起始地址分配给每个指针,以便它们指向有效内存,然后将您的字符串复制到分配的块然后你可以通过指针引用。

最简单的方法就是先把nameprocess读入一个临时字符数组,这样就可以得到需要存储的字符串的长度,然后分配存储(nul-terminating 字符 +1)并将字符串复制到分配的块。

您无法围绕 scanf() 构建任何类型的稳健输入路由。对于新的 C 程序员来说,它充满了陷阱。相反,所有用户输入都应使用 fgets() 来完成,以确保您每次都使用完整的 user-input 行,然后使用 [=23] 从填充有 fgets() 的缓冲区中解析您需要的值=].这样你就可以验证(1)输入的读取; (2) 分别解析来自输入的信息。最重要的是,后续读取不会因 匹配失败 而在输入缓冲区中留下未读字符而受阻。

根据您的描述,您想要 read/discard 第一行,这就是导致您使用 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  %d\n", 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) 作为你的 read-loop 条件,其中 numOfJobs < MAXU 保护你的 userList 数组边界如果您输入的信息超过 MAXU (100) 行信息)

,则阻止您写入数组之外

(note2:不需要声明全局变量。而是在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

例子Use/Output

$ ./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

内存Use/Error检查

在您编写的任何动态分配内存的代码中,您对分配的任何内存块负有 2 责任:(1) 始终保留指向内存块的起始地址 因此,(2) 当不再需要它时可以释放

您必须使用内存错误检查程序来确保您不会尝试访问内存或写入 beyond/outside 您分配的块的边界,尝试读取或基于未初始化的条件跳转值,最后,确认您释放了所有已分配的内存。

对于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)

始终确认您已释放所有分配的内存并且没有内存错误。

检查一下,如果您还有其他问题,请告诉我。