【问题标题】:Structures with variable members size具有可变成员大小的结构
【发布时间】:2019-01-03 20:12:59
【问题描述】:

我想编写函数来获取和打印用户输入的数据。想法是将名称和姓氏作为指针,指向可变大小字符串的指针。代码有什么问题?我究竟做错了什么?替代品?

#include <stdio.h>
#include <stdlib.h>
typedef struct {
   char *name;
   char *lastname;
   int marks[5];
} Student;
void setS(Student *s);
void getS(Student *s);

int main()
{
   Student st;
   getS(&st);
   setS(&st);
   return 0;
 }

 void setS(Student *s){
    int i;
    printf("Name: %s\t", s->name);
    printf("last Name: %s\t", s->lastname);
    for(i=0; i<5; i++)
        printf("%3d", s->marks[i]);
    printf("\n");
 }
 void getS(Student *s){
    int i;
    printf("Enter name\n");
    gets(s->name);
    printf("Enter last name\n");
    gets(s->lastname);
    printf("Enter marks\n");
    for(i=0; i<5; i++)    
       scanf("%d", &s->marks[i]);
    printf("\n");
  }

【问题讨论】:

  • 你永远不会初始化任何一个指向有效、足够内存的指针。您的gets 调用调用了未定义的行为
  • 另外,don't use gets
  • 太棒了,我应该...!?
  • 要么分配足够的内存并将用户输入限制为不超过你所拥有的,要么在stackoverflow.com/questions/16870485/…中执行类似的操作
  • 我明白,我想。使用 malloc 分配内存,添加从输入中收集字符和删除不需要的字符的功能......所有这些都必须在一个......

标签: c pointers structure


【解决方案1】:

代码有什么问题?我做错了什么?

gets(s-&gt;name); 尝试将用户输入读入s-&gt;names-&gt;name 是尚未分配的 char *gets() 试图将数据保存到谁知道在哪里?结果:未定义的行为 (UB)。

替代方案?

读入缓冲区,然后为副本分配内存。

提示:尽可能避免将scanf(), gets() 用于用户输入,并使用fgets()C - scanf() vs gets() vs fgets().


一些代码来提供一个想法。

#define NAME_N 100

// return 1 on success
// return EOF on end-of-file
// return 0 other errors
int getS(Student *s) {
  *s = (Student) {NULL, NULL, {0}}; // pre-fill with default values.

  char buffer[NAME_N];
  printf("Enter name\n");
  if (fgets(buffer, sizeof buffer, stdin) == NULL) {
    return EOF;
  }
  trim(buffer);
  if (!validate_name(buffer)) {
    return 0;
  }
  s->name = strdup(buffer);

  printf("Enter last name\n");
  if (fgets(buffer, sizeof buffer, stdin) == NULL) {
    return EOF;
  }
  trim(buffer);
  if (!validate_name(buffer)) {
    return 0;
  }
  s->lastname = strdup(buffer);

  printf("Enter marks\n");
  for (int i = 0; i < 5; i++) {
    if (fgets(buffer, sizeof buffer, stdin) == NULL) {
      return EOF;
    }
    if (sscanf(buffer, "%d", &s->marks[i]) != 1) {
      return 0;
    }
  }
  return 1;
}

trim() 是您修剪前导/尾随/过多空格的函数。另见How do I trim leading/trailing whitespace in a standard way?

strdup() 不是 C 标准但非常常用的字符串复制函数,它分配内存和副本。 Sample code

validate_name() 是占位符代码,以确保名称的完整性。在可以接受的事情上要慷慨。名称中通常可接受的字符包括 [A-Z, a-z, '-', ''', ' ', '.'] 等等:What are all of the allowable characters for people's names?.

例子:

// Fail a zero length name or one with controls characters in it.
bool validate_name(const char *name) {
  if (*name == '\0') return false;
  while (*name) {
    if (iscntrl((unsigned char) *name)) return false;
    name++;
  }
  return true;
}

【讨论】:

  • 非常感谢。我尝试使用固定大小的 tmp 字符串(在你的情况下为缓冲区),但我使用了 strcpy 函数,加上获取......我喜欢你处理数据的方式。我明天试试。非常感谢!
猜你喜欢
  • 1970-01-01
  • 2011-04-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-05
  • 2011-11-30
  • 2021-04-05
  • 2021-06-22
相关资源
最近更新 更多