Структуры с переменным размером членов - PullRequest
0 голосов
/ 03 января 2019

Я хочу написать функции для получения и печати данных, введенных пользователем. Идея состоит в том, чтобы имя и фамилия были указателями, указателями на строки переменного размера. Что не так с кодом? Что я делаю неправильно? Альтернативы?

#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");
  }

1 Ответ

0 голосов
/ 04 января 2019

Что не так с кодом?Что я делаю не так?

gets(s->name); пытается прочитать пользовательский ввод в s->name.s->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() - ваша функция для обрезки начальных / конечных / чрезмерных пробелов.См. Также Как стандартным способом обрезать начальные / конечные пробелы? .

strdup() не является стандартом C, но широко распространенной функцией дублирования строк, которая выделяет память и копии. Пример кода

validate_name() - это код заполнителя для обеспечения некоторого здравого смысла в имени.Будьте щедры в том, что приемлемо.Обычно допустимые символы в имени включают [AZ, az, '-', '' ',' ','. '] И многие другие: Каковы все допустимые символы для имен людей? .

Пример:

// 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;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...