【问题标题】:Can't print data from struct in C无法从 C 中的结构打印数据
【发布时间】:2022-01-02 14:28:06
【问题描述】:

我的目标是打印struct中包含的学生数据

我的问题是为什么我无法从结构中打印出数据?

这是我的代码:

typedef struct _STUDENT{
    char *fullname;
    int ID;
    float scores[6];
} STUDENT;
void input(STUDENT* student){
    fflush(stdin);
    printf("\nInput Student Fullname: ");
    scanf("%s", &student->fullname);
    printf("\nInput ID: ");
    scanf("%s",&student->ID);
    for(int i = 0; i < 6; i++){
        printf("\nInput point for course %d: ", i+1);
        scanf("%f",&student[i].scores);
    }
    
}
void output(STUDENT* student){
    
    printf("\nStudent Fullname: %s", student->fullname);
    printf("\nStudent ID: %d", student->ID);
    for(int i = 0; i < 6; i++){
        printf("\nStudent Score: %f", student[i].scores);   
    }
    
}

int main(){
    
    STUDENT* students;
    int size;
    printf("Enter number of student: ");
    scanf("%d",&size);
    
    students = (STUDENT*)calloc(size,sizeof(STUDENT));
    
    for(int i =0;i<size;i++){

        input(students+i);
    }

    
    for(int i =0;i<size;i++){
        output(students+i);
    }
    return 0;
}

这是输出:

Student Fullname:?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
Student ID: 50
Student Score: 0.000000
Student Score: 0.000000
Student Score: 0.000000
Student Score: 0.000000
Student Score: 0.000000
Student Score: 0.000000

在输出功能下,我尝试打印出一些数据,但没有一个被打印出来

【问题讨论】:

  • 您不能将scanf 字符串转换为&amp;student-&gt;fullname,这是零指针的地址(通过calloc 归零)。您必须在读入字符串之前为其分配空间,最好使用fgets 而不是scanf 以避免潜在的缓冲区溢出问题。
  • 使用 -Wall 作为编译器选项来启用许多警告,包括 printf 和 scanf 的参数输入错误(假设您使用的是 gcc 或 clang)。
  • 您正在尝试将 ID 扫描为字符串,但您正在为 int 提供存储空间,并将结果作为 int 读回。
  • @PaulHankin:您可以使用%&lt;maxlen&gt;s 指定最大字段宽度,以防止使用scanf 的缓冲区溢出
  • &amp;student[i].scores 是属于ith 学生的分数数组的地址。相反,您似乎想要&amp;student-&gt;scores[i]。 (或者,student-&gt;scores + i 表示相同的意思。)

标签: arrays c struct


【解决方案1】:

我的问题是为什么我无法从结构中打印出数据?

在某种程度上,由于您的 input() 函数中存在大量错误,学生数据并非您所期望的。您可以通过在调试器的控制下运行程序来自己发现这一点,并且您的编译器可能会警告您一些错误,至少在您提高警告级别时是这样。 始终注意编译器警告。

input() 函数的问题包括:

  • 不要fflush()标准输入。刷新是关于输出,而不是输入。如果fflush(stdin) 碰巧产生了您认为理想的效果,那么它是不可移植的。如果您想忽略某些输入,请读取并丢弃它。

    但是,只要您使用 scanffscanf 进行输入,请注意大多数字段指令会跳过前导空格,包括换行符。但是,在这种情况下,还要注意这些功能很难正确使用。 fgets() 结合 sscanf() 可能会更好地为您服务,但这并不是万能的。

  • 您不检查scanf() 调用的返回值。这本身并不是一个错误本身,但它意味着程序将无法识别输入错误或格式错误的输入(相对于您的scanf 调用序列)。

  • 这...

        scanf("%s", &student->fullname);
    

    ... 尝试将名称读入指针的表示。如果名称(以 null 结尾)长于指针的表示(可能是四个或八个字节),则会导致未定义的行为。您可能打算将数据读入指针指向的位置:

        scanf("%s", student->fullname);
    

    ...但这也是有问题的,因为指针没有指向有效的存储。您可以通过将fullname 成员声明为数组而不是指针来解决后一个问题(使用更正的代码),在这种情况下,您需要注意避免超出该数组的长度。或者,您可以将名称读入本地临时数组,然后为学生分配一个副本以指向:

    char temp[1024];
    int result = scanf("%1023s", temp);
    if (result != 1) /* ... handle input failure ... */;
    student->fullname = malloc(strlen(temp) + 1);
    strcpy(student->fullname, temp);
    

    如果您有一个与 POSIX 兼容的 strdup() 函数,那么您可以使用它来代替显式的 mallocstrcpy

  • 此外,“fullname”表示可能包含空格字符的字符串,但%s 会扫描 空格分隔 字符串。您可能需要一个 %[ 指令,但请注意,这是少数几个不忽略前导空格的指令之一。

  • 这...

        scanf("%s",&student->ID);
    

    ... 是错误的,因为&amp;student-&gt;ID 是指向int 的指针,而%s 需要指向char 的指针。此外,这将尝试将结果存储为字符串,而不是 int

  • 这...

                scanf("%f",&student[i].scores);
    

    ... 是错误的,因为您传递的是指向数组(float)的指针,而不是指向 float 的指针。这本身在实践中可能不会导致错误,但对于i != 0,它也是指向错误位置的指针。 student[i].scores 是偏移量为i 的学生的scores 数组,但您似乎想要偏移量为0 的学生分数数组的ith 元素:

                scanf("%f", &student->scores[i]);
    

    类似的错误也会出现在您的output() 函数中。

    此外,如果您进行检查,您可能会发现这些调用中的一些或全部未能扫描任何内容。使用您当前的代码,可能会出现由三部分组成的全名(例如,“Cento T. Programmer”)。

【讨论】:

  • Alternatively, you could read the name into a local temporary array, and then allocate a copy for the student to point to. 我尝试创建临时数组,但似乎仍然无法运行它,你能举一些例子吗?
  • 我添加了一些示例代码@Cento,但请注意您需要更正所有描述的问题。仅更正其中一些输出后,您可能会或可能不会看到不同的输出。
猜你喜欢
  • 2016-09-23
  • 1970-01-01
  • 2012-08-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多