Использование strcmp () в структурах, переданных как указатели - PullRequest
3 голосов
/ 06 марта 2019

У меня есть куча практических вопросов по структурам, все из которых включают структуры, переданные в качестве указателей на аргументы функций. Теперь у меня есть конкретный вопрос, который просит меня сохранить некоторые имена и номера телефонов в структуре телефонной книги. Оно гласит:

Напишите программу на C, которая реализует следующие две функции. Функция readin() читает число имена людей и соответствующие им номера телефонов, передает данные вызывающей стороне через параметр p и возвращает количество введенных имен. Используется символ # чтобы указать конец ввода пользователя. Функция search() находит телефонный номер имени входа цель, а затем печатает имя и номер телефона на экране. Если имя не может быть найден, тогда он напечатает соответствующее сообщение об ошибке. Прототипы двух функций приведены ниже:

 int readin(PhoneBk *p);
 void search(PhoneBk *p, int size, char *target);

Определение структуры для PhoneBk приведено ниже:

typedef struct {
    char name[20];
    char telno[20];
} PhoneBk;

Шаблон программы выглядит следующим образом:

#include <stdio.h>
#include <string.h>

#define MAX 100

typedef struct {
    char name[20];
    char telno[20];
} PhoneBk;

int readin(PhoneBk *p);
void search(PhoneBk *p, int size, char *target); 

int main() {
    PhoneBk s[MAX];
    char t[20];
    int size;

    size = readin(s);
    printf("Enter search name: \n");

    gets(t);
    search(s, size, t);

    return 0;
}

int readin(PhoneBk *p) {
    /* Write your program code here */
}

void search(PhoneBk *p, int size, char *target) {
    /* Write your program code here */ 
}

Мне придется сравнивать строки дважды в моей программе: во-первых, чтобы проверить, введено ли # в качестве имени (в этом случае я бы хотел, чтобы программа перешла прямо к функции поиска, не спрашивая номер телефона). ); и два, если целевое имя совпадает с именем одной из структур телефонной книги.

Теперь мне совершенно чуждо понятие структур, которые объявляются как массивы, но передаются как указатели, поэтому я думаю, способ получить пользовательский ввод для следующей записи - это приращение переменной-указателя (здесь p). Для функции «получить записи до - # - введен» я написал (используя strcmp):

int readin(PhoneBk *p) {
    strcpy(p->name, "Test");    //To guarantee a character that is not #.
    while (strcmp(p->name, "#") != 0) {
        printf("Enter person name, # to stop.\n");
        scanf("%s", p->name);
        printf("Enter the telephone number.\n");
        scanf("%s", p->telno);
        p++;
    }
    return p;
}

Я хочу набрать p->name в виде строки, чтобы strcmp() мог работать с ней. Но предупреждение неоднократно отображается:

warning: cast from pointer to integer of different size
warning: passing argument 1 of strcmp() makes pointer from integer without a cast

Эта ошибка много раз обсуждалась на форумах (я искал), но ничто из того, что я видел, не поможет с такой комбинацией структур и strcmp().

Я полностью отказался от strcmp() для второй функции, которая отображает запись человека, если целевое имя найдено, и поэтому я использовала цикл для непосредственного сравнения строк.

void search(PhoneBk *p, int size, char *target) {
    int i, flag = 1, strCompare;
    char c1 = *(p->name), c2 = *target, p1 = p->name, p2 = target;
    while (c1 != '\0' && c2 != '\0') {  //Comparing strings.
        strCompare = (int)(c1 - c2);
        if (strCompare != 0)
            break;
        else {
            p1++;
            p2++;
        }
    }
    for (i = 0; i < size; i++, p++) {
        if (strCompare == 0) {
            flag = 0;
            printf("Name = %s, Tel. = %s\n", p->name, p->telno);
        }
        if (flag == 1)
            printf("Name not found.\n");
    }
}

Но снова я получаю ряд предупреждений.

warning: cast from pointer to integer of different size
warning: initialization makes pointer from integer without a cast

Я действительно думаю, что моя жизнь была бы проще, если бы все структуры передавались как массивы, и в этом случае я мог бы просто перебирать массив структур с помощью цикла For и покончить с этим. Это то, что я всегда делал в старшей школе, но теперь, когда я учусь в университете, я вынужден иметь дело со структурами, которые передаются как указатели.

Как мне обработать strcmp() в этом случае? Как это можно применить к любой программе со структурами, переданными как указатели?

Ответы [ 2 ]

3 голосов
/ 06 марта 2019

Шаблон программы не очень хорошо структурирован и дает очень плохой пример с использованием устаревшей и опасной функции gets().

Было бы намного лучше передать readin() количество структур в указанном массивепо аргументу p.Из шаблона кода мы должны предположить, что MAX структуры доступны через p.

Функция readin должна считывать до MAX записей со стандартного ввода и сохранять их в соответствующих элементахцелевой массив с использованием индекса или приращения p.

Вот решение с индексом:

int readin(PhoneBk *p) {
    int i, n = MAX;
    for (i = 0; i < n; i++) {
        printf("Enter person name, # to stop.\n");
        if (scanf("%19s", p[i].name) != 1 || p[i].name[0] == '#')
            return i;
        printf("Enter the telephone number.\n");
        if (scanf("%19s", p[i].telno) != 1)
            return i;
    }
    return i;
}

По аналогии, функция поиска может выполнять итерацию по массиву с 0на size исключено:

void search(PhoneBk *p, int size, char *target) {
    int i, found = 0;
    for (i = 0; i < size; i++) {
        if (!strcmp(p[i].name, target) {
            printf("Name = %s, Tel. = %s\n", p[i].name, p[i].telno);
            found++;
        }
    }
    if (found == 0)
        printf("Name not found.\n");
}

Что касается предупреждений, у вас есть опечатка на strcmp((char)p->name, "#"), приведение не требуется и в этом случае преобразует указатель в один char, вызывая 2 предупреждения:один для преобразования указателя в целое число другого размера, а другой для передачи целого числа в strcmp() вместо указателя на char.

2 голосов
/ 06 марта 2019

В readin вы проверяете не последнее введенное имя, а имя (не инициализированное) следующей записи, а также не проверяете, достигли ли вы конца телефонной книги или случая EOF , Обратите внимание, что вы также предполагаете, что имя является просто словом и не может содержать пробелов, но поскольку main использует (некрасиво) получает , составленные имена допускаются.

Чтобы не мешать лишним пробелам, кажется, необходимо их удалить.

Например:

#include <ctype.h>

void normalize(char * s)
{
  /* remove unexpected 'isspace' */
  int p1 = 0, p2 = 0;

  while (s[p2] != 0) {
    if (!isspace((unsigned char) s[p2]))
      s[p1++] = s[p2];
    else if ((p1 != 0) && (s[p1 - 1] != ' '))
      s[p1++] = ' ';
    p2 += 1;
  }

  s[((p1 != 0) && (s[p1 - 1] == ' ')) ? p1 - 1 : p1] = 0;
}

int readin(PhoneBk *p)
{
  int n;

  for (n = 0; n != MAX; ++n) {
    puts("Enter person name, max length 18, # to stop.");
    if (fgets(p->name, sizeof(p->name), stdin) == NULL) {
      /* EOF */
      return n;
    }
    normalize(p->name);
    if (p->name[0] == '#')
      break;

    printf("Enter the telephone number, max length 18\n");
    if (fgets(p->telno, sizeof(p->telno), stdin) == NULL) {
      /* EOF, forget partial entry */
      return n;
    }
    normalize(p->telno);

    p += 1;
  }

  return n;
}

Ваш поиск настолько сложен, что я предпочел не пытаться его понять, извините, определение может быть очень простым:

void search(PhoneBk *p, int size, char *target)
{
  normalize(target);

  while (size--) {
    if (!strcmp(p->name, target)) {
      printf("%s found, phone number is %s\n", target, p->telno);
      return;
    }
    p += 1;
  }

  printf("unknown name %s\n", target);
}

Примечание. Я также нормализую имя чтения, чтобы оно было совместимо с прочтением книги.

Если я попытаюсь без изменения данного main и его уродливого , получим , компиляцию и выполнение:

/tmp % gcc -g -pedantic -Wextra c.c
/tmp/ccdXFM1o.o: In function `main':
/tmp/c.c:17: warning: the `gets' function is dangerous and should not be used.
/tmp % ./a.out
Enter person name, max length 18, # to stop.
 john     do
Enter the telephone number, max length 18
12   34 56
Enter person name, max length 18, # to stop.
just me
Enter the telephone number, max length 18
1 2 3 4 5
Enter person name, max length 18, # to stop.
#
Enter search name: 
john  do
john do found, phone number is 12 34 56
/tmp % 
/tmp % ./a.out
Enter person name, max length 18, # to stop.
 john     do
Enter the telephone number, max length 18
12   34 56
Enter person name, max length 18, # to stop.
just me
Enter the telephone number, max length 18
1 2 3 4 5
Enter person name, max length 18, # to stop.
#
Enter search name: 
just        me
just me found, phone number is 1 2 3 4 5
/tmp % 
/tmp % ./a.out
Enter person name, max length 18, # to stop.
 john     do
Enter the telephone number, max length 18
12   34 56
Enter person name, max length 18, # to stop.
just me
Enter the telephone number, max length 18
1 2 3 4 5
Enter person name, max length 18, # to stop.
#
Enter search name: 
me  
unknown name me
...