Есть так много маленьких проблем с вашим кодом, что трудно понять, с чего начать.Вероятно, наиболее вопиющим является сбой проверки возврата scanf
и ваша ошибка очищать stdin
любых посторонних символов после каждого ввода, оставляя вашу программу просто в ожидании ввода бесконечного цикла при первом ошибочном нажатии клавиши.Вы не можете использовать спецификатор преобразования "%s"
для чтения имен (многие из них состоят из двух частей), и вы перестанете читать на первом пробеле, а оставшаяся часть имени будет принята как следующий ввод.То же самое относится к чтению телефонного номера (который может вызвать бесконечный цикл, если есть пробел и пунктуация).
, что приводит к большей рекомендации использовать fgets
вместо scanf
для всех строк.ориентированный ввод (например, принятие пользовательского ввода).Вы можете использовать sscanf
для анализа целочисленных значений из входных данных, и вы избежите целого ряда ловушек, присущих новым программистам на С, принимающим ввод с scanf
.Не экономьте на размере буфера.Например, #define MAXC 1024
, а затем char buf[MAXC];
будет достаточно для большинства вводимых пользователем данных.
Далее, при создании меню рассмотрите возможность использования switch () { case 1: ...; break; case 2: ...; break }
вместо длинной цепочки if (...) {...} else if (...) {...}, etc...
.Он обеспечивает более читаемый код;
В ваш код внесено много, гораздо больше исправлений, которые объясняются в комментариях ниже, например,
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define NULLSZ 4 /* if you need a constant #define one (or more) */
#define MAXNMNUM 15
typedef struct book {
char personName[MAXNMNUM]; /* use constants for array sizes */
char personLname[MAXNMNUM];
char phoneNumber[MAXNMNUM];
char null[NULLSZ]; /* no clue what this is for */
} Book;
/* helper function to empty extraneous characters from stdin */
void empty_stdin (void)
{
int c = getchar();
while (c != EOF && c != '\n')
c = getchar();
}
int main (void) /* See http://port70.net/~nsz/c/c11/n1570.html#5.1.2.2.1p1 */
{
int index = 0;
Book *ptrBook = malloc (sizeof *ptrBook); /* do NOT cast malloc return */
for (;;) { /* loop continually */
int rtn, /* return for scanf */
choice = 0;
/* you only need a single fputs - not muliple printf calls
* there is no conversion taking place, so printf not neeeded.
*/
fputs ( "\nPhone Book Application\n\n"
" 1. Add Contact 2. Remove Contact\n"
" 3. Show Contacts ... 8. Exit\n"
"\nChoice: ", stdout);
rtn = scanf ("%d", &choice); /* ALWAYS VALIDATE EVERY INPUT */
if (rtn == EOF) { /* check if user canceled input with Ctrl+d */
fputs ("(user canceled input)\n", stderr);
break;
}
else if (rtn < 1) { /* check for matching or input failure */
fputs ("error: invalid integer input.\n", stderr);
empty_stdin(); /* always empty stdin before next input */
continue;
}
empty_stdin(); /* ditto */
if (choice == 1) { /* add book */
char *p;
/* don't realloc every addition - very inefficient.
* don't realloc the pointer itself, use a temp pointer
* or you create memory leak on failure.
*/
void *tmp = realloc (ptrBook, sizeof *ptrBook * (index + 1));
if (!tmp) {
perror ("realloc-ptrBook");
break;
}
ptrBook = tmp; /* assign new block to ptrBook */
fputs ("What is the FIRST name: ", stdout);
/* don't read line-oriented input with scanf, use fgets
* if (scanf ("%s", ptrBook[index].personName) != 1) {
*/
if (!fgets (ptrBook[index].personName, MAXNMNUM, stdin)) {
fputs ("(user canceled input.)\n", stderr);
break;
}
p = ptrBook[index].personName; /* set convenience pointer */
p[strcspn (p, "\r\n")] = 0; /* trim '\n' from end of str */
fputs ("What is the LAST name: ", stdout);
if (!fgets (ptrBook[index].personLname, MAXNMNUM, stdin)) {
fputs ("(user canceled input.)\n", stderr);
break;
}
p = ptrBook[index].personLname; /* set convenience pointer */
p[strcspn (p, "\r\n")] = 0; /* trim '\n' from end of str */
fputs ("What is the number: ", stdout);
if (!fgets (ptrBook[index].phoneNumber, MAXNMNUM, stdin)) {
fputs ("(user canceled input.)\n", stderr);
break;
}
p = ptrBook[index].phoneNumber; /* set convenience pointer */
p[strcspn (p, "\r\n")] = 0; /* trim '\n' from end of str */
printf ("\nAdded to the Phone Book.\n"
"\nName: %s %s\nPhone Number: %s\n",
ptrBook[index].personName,
ptrBook[index].personLname,
ptrBook[index].phoneNumber);
index++; /* increment index */
}
else if (choice == 2) { /* remove entry */
putchar ('\n');
for (int i = 0; i < index; i++) {
printf (" %d. %s %s\n", i + 1, ptrBook[i].personName,
ptrBook[i].personLname);
}
fputs ("\nWho would you like to remove? ", stdout);
rtn = scanf ("%d", &choice);
if (rtn == EOF) {
fputs ("(user canceled input)\n", stdout);
break;
}
else if (rtn < 1) {
fputs ("error: invalid integer input.\n", stderr);
empty_stdin();
continue;
}
else if (choice - 1 < 0 || index - 1 < choice - 1) {
fputs ("error: out of range of valid indexes.\n", stderr);
empty_stdin();
continue;
}
/* remvove entry with memmove copying over removed entry */
memmove (ptrBook + choice - 1, ptrBook + choice,
(index - choice) * sizeof *ptrBook);
index -= 1; /* decrement index */
/* realloc to remove entry */
void *tmp = realloc (ptrBook, index * sizeof *ptrBook);
if (!tmp) {
perror ("realloc-index");
break;
}
ptrBook = tmp;
}
if (choice == 3) { /* output the entries */
putchar ('\n');
for (int i = 0; i < index; i++) {
printf ("%s %s\n%d. %s\n\n", ptrBook[i].personName,
ptrBook[i].personLname, i + 1,
ptrBook[i].phoneNumber);
}
} else if (choice == 4) {
//make code to sort names
} else if (choice == 5) {
//find a phone number for a given name
} else if (choice == 6) {
//random person for you to call
} else if (choice == 7) {
//delete everyone
} else if (choice == 8) {
printf ("Exiting\n");
break;
}
}
}
( примечание: использование p
в качестве временного указателя просто для удобства и удобочитаемости, а не для того, чтобы печатать, например, полный ptrBook[index].personLname
снова и снова, в результате чего команда strcspn
занимает несколько строк)
Кроме того, рассмотрите возможность удаления null
из вашей структуры (не знаю, для чего это нужно) и рассмотрите возможность добавления index
или nentries
в качестве члена, чтобы число записей всегда было частью самой структуры (что делает вещи намного более удобнымипри передаче или возврате структуры (или указателя на нее) в / из других функций)
Ниже обратите внимание, как программа теперь может восстанавливаться после неверного ввода.Попробуйте тот же самый ввод с вашим кодом в меню, например, "I don't know"
и посмотрите, что произойдет ...
Пример Использование / Вывод
$ ./bin/book_remove
Phone Book Application
1. Add Contact 2. Remove Contact
3. Show Contacts ... 8. Exit
Choice: I don't know
error: invalid integer input.
Phone Book Application
1. Add Contact 2. Remove Contact
3. Show Contacts ... 8. Exit
Choice: 1
What is the FIRST name: Mark
What is the LAST name: Twain
What is the number: (444) 555-1212
Added to the Phone Book.
Name: Mark Twain
Phone Number: (444) 555-1212
Phone Book Application
1. Add Contact 2. Remove Contact
3. Show Contacts ... 8. Exit
Choice: 1
What is the FIRST name: Samuel
What is the LAST name: Clements
What is the number: (444) 555-1213
Added to the Phone Book.
Name: Samuel Clements
Phone Number: (444) 555-1213
Phone Book Application
1. Add Contact 2. Remove Contact
3. Show Contacts ... 8. Exit
Choice: 1
What is the FIRST name: Won Hung
What is the LAST name: Lo
What is the number: (444) 555-1214
Added to the Phone Book.
Name: Won Hung Lo
Phone Number: (444) 555-1214
Phone Book Application
1. Add Contact 2. Remove Contact
3. Show Contacts ... 8. Exit
Choice: 1
What is the FIRST name: Fred
What is the LAST name: Flintstone
What is the number: (444) 555-1215
Added to the Phone Book.
Name: Fred Flintstone
Phone Number: (444) 555-1215
Phone Book Application
1. Add Contact 2. Remove Contact
3. Show Contacts ... 8. Exit
Choice: 3
Mark Twain
1. (444) 555-1212
Samuel Clements
2. (444) 555-1213
Won Hung Lo
3. (444) 555-1214
Fred Flintstone
4. (444) 555-1215
Phone Book Application
1. Add Contact 2. Remove Contact
3. Show Contacts ... 8. Exit
Choice: 2
1. Mark Twain
2. Samuel Clements
3. Won Hung Lo
4. Fred Flintstone
Who would you like to remove? 2
Phone Book Application
1. Add Contact 2. Remove Contact
3. Show Contacts ... 8. Exit
Choice: 3
Mark Twain
1. (444) 555-1212
Won Hung Lo
2. (444) 555-1214
Fred Flintstone
3. (444) 555-1215
Phone Book Application
1. Add Contact 2. Remove Contact
3. Show Contacts ... 8. Exit
Choice: 8
Exiting
Посмотрите на вещии убедитесь, что вы понимаете, что и почему были внесены изменения.Если нет, то просто попросите дальнейших разъяснений.