Reputation: 55
I have a bunch of practice questions in Structures, all of which involve structures passed as pointers to function arguments. Now, I have a specific question at hand, which asks me to store some names and telephone numbers in a phone book structure. It reads as follows:
Write a C program that implements the following two functions. The function
readin()
reads a number of persons’ names and their corresponding telephone numbers, passes the data to the caller via the parameterp
, and returns the number of names that have entered. The character#
is used to indicate the end of user input. The functionsearch()
finds the telephone number of an input name target, and then prints the name and telephone number on the screen. If the input name cannot be found, then it will print an appropriate error message. The prototypes of the two functions are given below:
int readin(PhoneBk *p);
void search(PhoneBk *p, int size, char *target);
The structure definition for PhoneBk
is given below:
typedef struct {
char name[20];
char telno[20];
} PhoneBk;
The program template is as follows:
#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 */
}
I'll have to compare strings twice in my program: one, to check if #
is entered as the name (in which case I'd like the program to jump straight to the search function without asking the phone number); and two, if the targeted name matches that in one of the phone book structures.
Now I am completely alien to the concept of structures being declared as arrays but passed as pointers, so I think the way to get the user input for the next record is by incrementing the pointer variable (here p
). For the get-the-records-until-#-is-entered function, I wrote this (using 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;
}
My intention is to typecast p->name
as a string so that strcmp()
can work on it. But a warning is repeatedly shown:
warning: cast from pointer to integer of different size
warning: passing argument 1 of strcmp() makes pointer from integer without a cast
This error has been discussed many times on the forums (I searched), but nothing that I've seen really helps with this combination of structures and strcmp()
.
I gave up on strcmp()
entirely for the second function, the one that displays the record of a person if the targeted name is found, and so I used a loop to compare the strings directly.
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");
}
}
But again I get a number of warnings.
warning: cast from pointer to integer of different size
warning: initialization makes pointer from integer without a cast
I really think my life would be easier if the structures were all passed as arrays, in which case I could simply iterate through the array of structures using a For loop and be done with it. This is what I've always done throughout high school, but now that I'm in university, I am forced to deal with structures passed as pointers.
How do I handle strcmp()
in this case? How can this be applied to any program with structures passed as pointers?
Upvotes: 3
Views: 1470
Reputation: 32586
In readin you do not test the last entered name but the name (not initialized) of the next entry, and you do not also check you reach the end of the phone book, nor the EOF case. Note you also suppose the name is a just a word and cannot contain spaces, but because the main uses the (ugly) gets may be composed names are allowed.
To not be perturbed by extra spaces it seems needed to remove them.
For instance:
#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;
}
Your search is so complicated I preferred to not try to understand it, sorry, the definition can be very simple :
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);
}
Note I also normalize the read name, to be compatible with the read of the book.
If I try without changing the given main and its ugly gets, compilation and executions :
/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
Upvotes: 2
Reputation: 144550
The program template is not well structured and gives a very bad example by using obsolete and dangerous function gets()
.
It would be much better to pass readin()
the number of structures in the array pointed to by the p
argument. From the code template, we must assume that MAX
structures are accessible through p
.
The readin
function should read up to MAX
entries from standard input and stores the into the corresponding elements of the destination array, using an index or incrementing p
.
Here is a solution with an index:
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;
}
Simlarly, the search function can iterate through the array from 0
to size
excluded:
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");
}
Regarding the warnings, you have a typo on strcmp((char)p->name, "#")
, the cast is unnecessary and in this case converts the pointer to a single char
, causing 2 warnings: one for converting a pointer to an integer of a different size and another one for passing an integer to strcmp()
instead of a pointer to char
.
Upvotes: 3