Hi derekwtp,
The baseball argument to namesort is declared to be a pointer to
baseballinfo, but the baseballinfo structure is not declared. This
confuses the compiler enough to make it ignore the declarations of the
other arguments. If you put in a declaration of baseballinfo before
the definition of namesort, the complaints about the arguments being
undeclared should go away.
Your program has other problems. You did not ask about them and you
may be able to solve them yourself, so I will not volunteer a complete
analysis, but if you need further help with this program, please ask
for a clarification and I will try to help you. Similarly, if the
answer above is too short or obscure, please ask for a clarification
and I can explain at greater length.
Search strategy: No search. I ran the posted code through the
Microsoft Visual C++ compiler and observed that all complaints about
undeclared arguments went away when I added a declaration of
baseballinfo before the definition of namesort.
--efn |
Request for Answer Clarification by
derekwtp-ga
on
16 Feb 2003 20:17 PST
I have declared array.h in the header..sorry it didnt show up. that is
why it is confusing me. There has to be some other reason it is
showing up undeclared.
I need all the help i can get on this program And looking at it, I
wish I could tell what is the matter but I cant because this is the
only problem I am getting via the compiler
struct BaseballInfo
{
char name[30];
int age;
}
typedef struct BaseballInfo baseballinfo;
|
Clarification of Answer by
efn-ga
on
16 Feb 2003 23:21 PST
OK, this looks like a problem with the order of the statements. The
compiler processes the source file from beginning to end, and at the
point where it is compiling the namesort function, it hasn't yet seen
the declaration of baseballinfo. If baseballinfo is declared in
array.h, you need to move the #include "array.h" line before the
definition of namesort. In other words, you have to tell the compiler
what a baseballinfo is before you can tell it that namesort takes an
argument that is a pointer to a baseballinfo.
In general, it's common practice in C to have all the #include
directives before any other non-comment statements in every source
file.
If that doesn't work, or you need any further help, please ask.
--efn
|
Clarification of Answer by
efn-ga
on
17 Feb 2003 10:11 PST
Thanks for your interest in hiring me. I am checking with Google
Answers on whether their policies would allow me to deal with you
directly. When I find out, I will respond to you, either directly or
here.
Meanwhile, if you would like to ask about something else here, I am
still willing to assist you further.
--efn
|
Clarification of Answer by
efn-ga
on
17 Feb 2003 11:18 PST
Sorry, Google Answers policies discourage me from dealing with you
directly.
For the price of this question, I am still willing to provide further
assistance with this program. Feel free to ask for a clarification if
you get stuck on something else.
--efn
|
Request for Answer Clarification by
derekwtp-ga
on
17 Feb 2003 11:38 PST
My assignment is to take this program, make it modular, In which you
saw me take the two functions and main already:
Write a C program that will read a list of names and ages from a data
file into an ARRAY OF STRUCTures and sort them according to age or
name, based on a flag on the command line. The name of the file should
also come from the command line, as well as the number of names that
will be read in.
Ex:
%sort.exe -f names.baseball -s age -n 20
or
%sort.exe -s name -n 33 -f names.football
or
%sort.exe -n 33 -s name
or
%sort.exe -f names.soccer -n 13
etc, etc...
The default filename should be list.dat, the default number of names
should be 50 and the default sorting order should be by name. There
should not be any restrictions on the order that the arguments appear
on the command line. Feel free to use the code from parser.c, or write
your own. You may use the file "ages.dat" or create your own input
file.
You must write a function to perform each sort and a function for
reading in the data and one more for writing the data on standard
output. You must define your structure type in an include file. This
shoud make your main function very short, except for the code that
parses the command line.
You will hand in the files containing:
1. main program
2. structure def (include file)
3. sort by name code
4. sort by age code
5. read function
6. write function
7. output from a run
In the Code directory the file "structsort.c" does essentially what
have to do, but all in main( ) and with no command line parsing.
Please use this file as an aid in coding the sorts, input, and output.
Here is the program : structsort.c
#include <string.h>
#include <stdio.h>
#define MAX 100
struct DATA {
char *name;
int age;
};
typedef struct DATA Data;
main(int argc,char **argv)
{
void swap(Data*,Data*);
Data *people;
char tmp[30];
int bottom,i,numofelem;
FILE *fp;
/* get number of names from command line and allocate space for array
*/
numofelem=atoi(argv[1]);
people=(Data *)malloc(sizeof(Data)*numofelem);
/* read in the data */
for(i=0;i<numofelem;i++){
scanf("%s %d",tmp,&people[i].age);
people[i].name=(char *)malloc(sizeof(char)*(strlen(tmp)+1));
strcpy(people[i].name,tmp);
}
/* sort by age */
for (bottom=numofelem-1;bottom>0;bottom--)
for (i=0;i<bottom;i++)
if (people[i].age > people[i+1].age)
swap(&people[i],&people[i+1]);
/* output sorted list */
for (i=0;i<numofelem;i++) printf("%s
%d\n",people[i].name,people[i].age);
/* sort by name */
for (bottom=numofelem-1;bottom>0;bottom--)
for (i=0;i<bottom;i++)
if (strcmp(people[i].name,people[i+1].name)>0)
swap(&people[i],&people[i+1]);
/* output sorted list */
for (i=0;i<numofelem;i++) printf("%s
%d\n",people[i].name,people[i].age);
}
void swap(Data *a,Data *b)
{
Data temp;
temp = *a;
*a = *b;
*b = temp;
}
------------------------------------------------------------------------------
And as you have seen, this is what I have gotten so far, and for some
reason here is my output:
[root@localhost four]# ./fog list.dat 10 1
0
0
0
0
0
0
0
0
0
0
[root@localhost four]# ./fog list.dat 10 2
0
0
0
0
0
0
0
0
0
0
------------------------------------------------------
/***************************************************************************
assignment.c - description
-------------------
begin : Sun Feb 16 2003
copyright : (C) 2003 by Derek Winchester
email : dswinchester@starpower.net
***************************************************************************/
/***************************************************************************
*
*
* This program is free software; you can redistribute it and/or
modify *
* it under the terms of the GNU General Public License as published
by *
* the Free Software Foundation; either version 2 of the License, or
*
* (at your option) any later version.
*
*
*
***************************************************************************/
#include<stdio.h>
#include"array.h"
main(int argc,char** argv)
{
int choice;
int number;
char *filename;
number= atoi(argv[2]);
choice= atoi(argv[3]);
baseballinfo *baseball;
char read (char*,baseballinfo*,int*);
char agesort (char*,baseballinfo*,int*);
char namesort (char*,baseballinfo*,int*);
baseball=(baseballinfo *) malloc(sizeof(baseballinfo));
filename=(char*)malloc(strlen(argv[1])+1);
strcpy(filename,argv[1]);
switch (choice)
{
case '1': namesort(filename,baseball,number);
break;
case '2': agesort(filename,baseball,number);
break;
default: namesort(filename,baseball,number);/*printf ("please
enter name or number for sorting preference\n");*/
}
}
--------------------------------------------------------------------------
/*namesort*/
#include "array.h"
char namesort (char *filename[],baseballinfo *baseball,int lines)
{
int bottom;
int i;
void swap(baseballinfo*,baseballinfo*);
for (bottom=lines-1;bottom>0;bottom--)
for (i=0;i<bottom;i++)
if (strcmp(baseball[i].name,baseball[i+1].name)>0)
swap(&baseball[i],&baseball[i+1]);
/* output sorted list */
for (i=0;i<lines;i++) printf("%s
%d\n",baseball[i].name,baseball[i].age);
}
-------------------------------------------------------------
|
Request for Answer Clarification by
derekwtp-ga
on
17 Feb 2003 11:43 PST
/*agesort*/
char agesort (char *filename[],baseballinfo *baseball,int lines)
{
int bottom;
int i;
void swap(baseballinfo*,baseballinfo*);
for (bottom=lines-1;bottom>0;bottom--)
for (i=0;i<bottom;i++)
if (baseball[i].age > baseball[i+1].age)
swap(&baseball[i],&baseball[i+1]);
/* output sorted list */
for (i=0;i<lines;i++) printf("%s %d\n",baseball[i].name,baseball[i].age);
}
/*swap.c*/
#include "array.h"
void swap(baseballinfo *a,baseballinfo *b)
{
baseballinfo temp;
temp = *a;
*a = *b;
*b = temp;
}
/*array.h*/
struct BaseballInfo
{
char name[30];
int age;
};
typedef struct BaseballInfo baseballinfo;
|
Clarification of Answer by
efn-ga
on
17 Feb 2003 13:37 PST
Hi Derek,
The immediate problem is that the program is not reading the input
file. That's why the output is not satisfactory.
I'll note a few other things while I am at it.
1. The program uses positional command line arguments, while the
assignment specifies that the command line arguments are to be
identified by option codes, not position. This may just be something
you haven't gotten to yet.
2. baseball in main is supposed to be an array, but the program only
allocates enough space for one baseballinfo. It's supposed to get an
array size from the command line and allocate enough memory for the
specified number of baseballinfos.
3. I suggest you develop the program incrementally. That means you
don't try to write the whole thing first, and then debug it, but
instead, you make some small part of it work, then add a little more,
and repeat until you have the whole thing. With this assignment, for
example, you might start with hard-coded values for all the
command-line arguments and a fixed-size array and just get the program
to read in a file and put out the file contents so you can see that it
worked. Then you can add the various variations under command-line
control, make the array dynamically allocated, and add the sorts as
separate later steps. One important advantage of this approach is
that if the amount of code you have added since it last worked is
always small, if it ever breaks, the amount of suspect code to examine
is small.
--efn
|
Request for Answer Clarification by
derekwtp-ga
on
17 Feb 2003 14:55 PST
any thoughts on why it will not read the file. Thanks for the advice
of static declarations.
|
Clarification of Answer by
efn-ga
on
17 Feb 2003 15:37 PST
The program will not read the file because it does not contain any
statements that do that. The main function sets the filename string
from the command line argument and passes it to the namesort and
agesort functions, but those functions don't do anything with the
filename, so the functions never get any real input data. It would
not be good style to have two copies of the input software in these
two functions; it would be better to write it as the assignment
specifies and have "a function for reading in the data." It would
also be better to call this input function once from main, but it
would also work to call it from the two sort functions.
--efn
|
Request for Answer Clarification by
derekwtp-ga
on
17 Feb 2003 16:14 PST
That's just it. The way the program worked before you would do:
a.out 2 < file.dat
-------------------
It is bazarre to me how
for(i=0;i<numofelem;i++){
scanf("%s %d",tmp,&people[i].age);
people[i].name=(char *)malloc(sizeof(char)*(strlen(tmp)+1));
strcpy(people[i].name,tmp);
wourld work for that, let alone making it read from a file. Would I
have to fopen a file for this to work?
|
Clarification of Answer by
efn-ga
on
17 Feb 2003 16:38 PST
Yes, you will have to open the file somehow. fopen is the standard
library function that does this, and you may also have
platform-specific alternatives. Probably the easiest approach is to
use the library functions fopen and fscanf. fscanf works like scanf,
except it lets you specify what file to read. You would do this by
passing it the FILE pointer that fopen returned.
In case you're interested, I'll explain how the other approach works.
Predefined files known as "standard input," "standard output," and
"standard error" are part of the run-time environment automatically
supplied to a standard C program. You can just assume they will be
there, and you don't have to open or close them.
The command-line text "< file.dat" hooks file.dat up to standard
input, so that when the program reads from standard input it reads
from that file, and the scanf function reads from standard input, so
that's how it can read the file without the program having to open it
explicitly.
--efn
|
Request for Answer Clarification by
derekwtp-ga
on
17 Feb 2003 17:47 PST
From looking at this could you tell me why this is not working. Why I
am getting an error on the fscanf line?
#include<stdio.h>
#include"array.h"
char read (baseballinfo *baseball,char fn[],int numofelem)
{
FILE *fp;
char tmp[30];
int i;
if(!(fp=fopen(fn,"r"))) {
printf("%s does not exits, starting with empty list\n",fn);
return;
}
for(i=0;i<numofelem;i++){
fscanf(fp,"%s %d",tmp,&baseball[i].age);
baseball[i].name=(char *)malloc(sizeof(char)*(strlen(tmp)+1));
strcpy(baseball[i].name,tmp);
}
fclose(fp);
printf ("this is the end of readlist\n");
}
|
Clarification of Answer by
efn-ga
on
17 Feb 2003 19:31 PST
I don't see anything wrong with the fscanf call. Did you get a
compile error or a run-time error? What did the error message say?
--efn
|
Request for Answer Clarification by
derekwtp-ga
on
18 Feb 2003 07:06 PST
The error was
read.c:23: incompatible types in assignment
gmake: *** [read.o] Error 1
|
Clarification of Answer by
efn-ga
on
18 Feb 2003 09:26 PST
That error message actually refers to the line after the fscanf call.
You can tell because the message says "in assignment" and an
assignment statement has an equals sign.
So you're getting the message about this line:
baseball[i].name=(char *)malloc(sizeof(char)*(strlen(tmp)+1));
The call to malloc allocates some memory and returns a pointer to it.
baseball[i].name is declared as an array of 30 chars. The compiler
won't let you assign a pointer to a char array. That's why it's
saying "incompatible types."
Because baseball[i].name is already a character array, you don't need
to allocate memory for the data. So you could just delete this whole
line. That would be the simplest way to get past the error.
Alternately, you could make the name member a pointer to char instead
of an array of char. Then you would be using dynamic memory
allocation and the assignment would be valid.
--efn
|
Request for Answer Clarification by
derekwtp-ga
on
18 Feb 2003 19:13 PST
Well All i can really say is thank you. I feel I am almost done. Can
you help me chuck out this last bug. The file will not write.
/***************************************************************************
assignment.c - description
-------------------
begin : Sun Feb 16 2003
copyright : (C) 2003 by Derek Winchester
email : dswinchester@starpower.net
***************************************************************************/
/***************************************************************************
*
*
* This program is free software; you can redistribute it and/or
modify *
* it under the terms of the GNU General Public License as published
by *
* the Free Software Foundation; either version 2 of the License, or
*
* (at your option) any later version.
*
*
*
***************************************************************************/
#include<stdio.h>
#include"array.h"
main(int argc,char** argv)
{
int choice;
int number;
char filename[25];
number= 10;
choice= 1;
baseballinfo *baseball[50];
void read (char*,baseballinfo*,int*);
void agesort (char*,baseballinfo*,int*);
void namesort (char*,baseballinfo*,int*);
void write (baseballinfo*);
baseball[50]=(baseballinfo *) malloc(sizeof(baseballinfo));
strcpy(filename,argv[1]);
read(*baseball,filename,&number);
switch (choice)
{
case '1': namesort(filename,*baseball,&number);
write(*baseball);
break;
case '2': agesort(filename,*baseball,&number);
write(*baseball);
break;
default: namesort(filename,*baseball,&number);
write(*baseball);
}
write(*baseball);
}
struct BaseballInfo
{
char name[30];
int age;
struct BaseballInfo *next;
};
typedef struct BaseballInfo baseballinfo;
#include "array.h"
void agesort (char *filename[],baseballinfo *baseball,int lines)
{
int bottom;
int i;
void swap(baseballinfo*,baseballinfo*);
for (bottom=lines-1;bottom>0;bottom--)
for (i=0;i<bottom;i++)
if (baseball[i].age > baseball[i+1].age)
swap(&baseball[i],&baseball[i+1]);
/* output sorted list */
for (i=0;i<lines;i++) printf("%s
%d\n",baseball[i].name,baseball[i].age);
printf ("end of agesort\n");
}
#include "array.h"
void namesort (char *filename[],baseballinfo *baseball,int lines)
{
int bottom;
int i;
void swap(baseballinfo*,baseballinfo*);
for (bottom=lines-1;bottom>0;bottom--)
for (i=0;i<bottom;i++)
if (strcmp(baseball[i].name,baseball[i+1].name)>0)
swap(&baseball[i],&baseball[i+1]);
/* output sorted list */
for (i=0;i<lines;i++) printf("%s
%d\n",baseball[i].name,baseball[i].age);
printf ("end of namesort\n");
}
#include<stdio.h>
#include"array.h"
void read (baseballinfo *baseball,char fn[],int numofelem)
{
FILE *fp;
char tmp[30];
int i;
/* try to open the file */
if(!(fp=fopen(fn,"r"))) {
printf("%s does not exits, starting with empty list\n",fn);
return;
}
for(i=0;i<numofelem;i++){
fscanf(fp,"%s %d",tmp,&baseball[i].age);
strcpy(baseball[i].name,tmp);
}
fclose(fp);
printf ("this is the end of readlist\n");
}
#include <stdio.h>
#include "array.h"
void write(baseballinfo *baseball)
{
baseballinfo* curr;
curr=baseball->next;
while (curr!=NULL) {
printf("%s %d\n",curr->name,curr->age);
curr=curr->next; /* bump the
pointer */ }
printf ("this is the end of writefile\n");
}
|
Clarification of Answer by
efn-ga
on
18 Feb 2003 21:57 PST
Hi Derek,
You're quite welcome, and congratulations on your progress so far.
I have to tell you, though, that I think you have more than one bug
left. I will note a few, not necessarily all.
1. The declaration of the read function in main does not match the
actual function, nor the parameters passed to the function in main.
My compiler complained about this.
2. The coding for the baseball array won't work.
baseballinfo *baseball[50];
declares baseball to be an array of 50 pointers to baseballinfos. But
the rest of the program seems to want baseball to be an array of
baseballinfos. So this doesn't work, because a pointer to a
baseballinfo is not the same as a baseballinfo.
The simplest approach is to make baseball a statically-allocated,
fixed-size array of baseballinfos:
baseballinfo baseball[50];
Then you can get rid of the call to malloc to allocate space for it,
you can keep all the baseballinfo* parameter declarations unchanged,
and you can just pass baseball (not *baseball) for those parameters.
This works because when you pass an array as an argument, it
automatically gets converted to a pointer to the array's first
element, and you can subscript such a point just as if it were an
array in the called function.
This design doesn't work if there is more input data than will fit in
the array. To remove this limitation, you would have to allocate the
memory for the array dynamically. In this case, you would keep just
one pointer that points to the first element of the array:
baseballinfo *baseball;
You would also have to have a call to malloc to allocate the space for
the array. The amount of space you request from malloc should not be
sizeof(baseballinfo), because that only gives you enough memory for
one baseballinfo. You have to multiply that by the array size read in
at run-time from the command line to get total number of characters to
be allocated.
With the dynamic memory allocation design, you can still keep the
baseballinfo* parameter declarations unchanged, just pass baseball as
a parameter, and subscript it within the functions.
3. The write function seems to expect the baseball array to be set up
as a linked list, but there is nothing that sets the next pointer
members. It would be simpler to communicate to the function in some
other way how many baseballinfos to write out. Passing it the number
of elements in the array as an argument would be a straightforward way
to do this.
--efn
|
Request for Answer Clarification by
derekwtp-ga
on
19 Feb 2003 08:46 PST
Number #2 just killed brain cells. Please clarify. When I made the
changes you suggested my compiler errors on it.
/***************************************************************************
assignment.c - description
-------------------
begin : Sun Feb 16 2003
copyright : (C) 2003 by Derek Winchester
email : dswinchester@starpower.net
***************************************************************************/
/***************************************************************************
*
*
* This program is free software; you can redistribute it and/or
modify *
* it under the terms of the GNU General Public License as published
by *
* the Free Software Foundation; either version 2 of the License, or
*
* (at your option) any later version.
*
*
*
***************************************************************************/
#include<stdio.h>
#include"array.h"
main(int argc,char** argv)
{
int choice;
int number;
char filename[25];
number= 10;
choice= 1;
baseballinfo baseball[50];
void read (baseballinfo*,char*,int*);
void agesort (char*,baseballinfo*,int*);
void namesort (char*,baseballinfo*,int*);
void write (baseballinfo*);
strcpy(filename,argv[1]);
read(baseball,filename,&number);
switch (choice)
{
case '1': namesort(filename,baseball,&number);
write(*baseball);
break;
case '2': agesort(filename,baseball,&number);
write(*baseball);
break;
default: namesort(filename,baseball,&number);
write(*baseball);
}
write(*baseball);
}
|
Request for Answer Clarification by
derekwtp-ga
on
19 Feb 2003 09:06 PST
Disregard the last comment. Sorry
|
Request for Answer Clarification by
derekwtp-ga
on
19 Feb 2003 09:26 PST
But efn, should this work. I have the pointer set up to go to the next
Array.
struct BaseballInfo
{
char name[30];
int age;
struct BaseballInfo *next;
};
typedef struct BaseballInfo baseballinfo;
void write(baseballinfo *baseball)
{
baseballinfo* curr;
curr=baseball->next;
while (curr!=NULL) {
printf("%s %d\n",curr->name,curr->age);
curr=curr->next; /* bump the
pointer */ }
printf ("this is the end of writefile\n");
}
|
Clarification of Answer by
efn-ga
on
19 Feb 2003 10:31 PST
Hi Derek,
I extend my condolences on the sad loss of your brain cells. Pointers
and their relationship to arrays are well known to be hard for people
learning C. You don't have enough time left on the meter for this
question for me to write a full tutorial, so I suggest you check your
textbook and resources available on the Web, such as Ted Jensen's
tutorial specifically on pointers and arrays:
http://pw1.netcom.com/~tjensen/ptr/pointers.htm
Here I will just give you a summary idea of how the syntax looks.
int a[10];
/* a is an array of 10 ints. */
void func1(int n);
void func2(int* pn);
void func3(int arrayparameter[]);
void func4(int arrayparameter[10]);
func1(a);
/* Error! The parameter is converted to &(a[0]), which is a pointer
to an int, but func1 requires an int, and a pointer to an int is not
an int. */
func1(a[2]);
/* OK */
func2(a);
/* OK. The reference to a is again converted to &(a[0]), which is a
pointer to an int, which is the correct type to pass to func2. */
func2(a[2]);
/* Error! The function wants a pointer, not an int parameter. */
/* func3 and func4 work exactly the same as func2. The array size
specification on func4's parameter is allowed, but has no effect. */
void func2(int* pn)
{
/* Within this function, we can use a subscript on pn just the same as
if it were declared as an array. */
pn[0] = 0;
/* When a is passed to func2, this affects a[0]. */
return;
}
In your latest code, you have:
void write(baseballinfo *baseball);
baseballinfo baseball[50];
write(*baseball);
*baseball refers to the same thing as baseball[0]: the first
baseballinfo in the array. So you are passing a baseballinfo to
write, but write is declared to take a pointer to a baseballinfo,
which is not the same thing. You want the write function to have
access to the whole array, so declaring write to take a pointer makes
sense. You just have to change the call so it passes baseball instead
of *baseball. Remember, passing the baseball array as a parameter
means you are passing the address of the first element, which the
function can then subscript as if it were an array.
With regard to the write function finding its way through the array,
the function you have relies on the next pointers of the baseballinfo
structures being set to make the array work as a linked list. If you
have some code somewhere that sets those pointers correctly, the write
function will be able to use them successfully, and if you don't, it
won't. I haven't seen any such code in what you have posted so far,
but of course, that doesn't guarantee that you don't have it in your
program. The compiler will not set that data structure up for you
automatically: you have to write the software to do that.
An array is a simpler data structure than a linked list, and you don't
need a linked list for this program. The usual pattern for iterating
through an array is something like this:
int index, numberofelements;
int array[100];
/* Set numberofelements somehow. */
for (index = 0; index < numberofelements; ++index)
{
/* do something with array[index] */
}
--efn
|