Advanced C 1992
.pdfPart II • Managing Data in C
A better solution is to take the offsetof() of the first member to write and the offsetof() of the member just after the last member to write. Subtract one from the other, and you have the number of bytes to save. As you can see, this method is quick and easy.
Pointers to Structures
A pointer to a structure is handled in the same way as a pointer to any other data type, except the syntax of the structure pointer operator differs. You can have a pointer to a structure, and use the pointer to access any member in the structure.
When calling functions that have structures as parameters, it is more efficient to pass a pointer to a structure rather than pass the entire structure. See Listing 7.8, STRUPTR.C.
Listing 7.8. STRUPTR.C.
/* |
STRUPTR, |
written 1992 by Peter D. Hipson |
* |
Pointers |
and structures |
*/ |
|
|
#include <stdio.h> // Make includes first part of file #include <string.h> // For string functions
#define MAX_SIZE 35
int main(void); // Define main(), and the fact that this program doesn’t // use any passed parameters.
int main()
{
int i;
typedef struct
{
char *szSaying[MAX_SIZE];
216
C Structures
int |
nLength[MAX_SIZE]; |
} SAYING; |
|
typedef struct
{
SAYING Murphy;
SAYING Peter; } OURSAYING;
OURSAYING OurSaying = {{
“Firestone’s Law of Forecasting:”,
“Chicken Little has to be right only once.”, “”, “”,
“Manly’s Maxim:”,
“Logic is a systematic method of coming to”,
“the wrong conclusion with confidence.”,
“”,
“”,
“Moer’s truism:”,
“The trouble with most jobs is the job holder’s”,
“resemblance to being one of a sled dog team. No one”,
C C C
C7C C
C C C
“gets a change of scenery except the lead dog.”, “”, “”,
“Cannon’s Comment:”,
“If you tell the boss you were late for work because you”,
“had a flat tire, the next morning you will have a flat tire.”, NULL /* Flag to mark the last saying */
}, {
“David’s rule:”,
“Software should be as easy to use as a Coke machine.”,
“”,
“”,
“Peter’s Maxim:”,
“To be successful, you must work hard, but”,
“Hard work doesn’t guarantee success.”,
“”,
“”,
“Teacher’s truism:”,
continues
217
Part II • Managing Data in C
Listing 7.8. continued
“Successful people learn.”, “”, “”,
“Player’s Comment:”,
“If you don’t play to win,”,
“you don’t win.”,
NULL /* Flag to mark the last saying */ }};
OURSAYING * pOurSaying; |
|
||
SAYING |
* pSaying; |
|
|
pOurSaying = &OurSaying; |
|
||
pSaying |
= &OurSaying.Peter; |
|
|
printf( |
|
|
|
|
“sizeof(OURSAYING) |
= %d\n” |
|
|
“sizeof(OurSaying) |
= %d\n” |
|
|
“sizeof(SAYING) |
= %d\n” |
|
|
“sizeof(pOurSaying->Murphy) |
= %d\n” |
|
|
“sizeof(pOurSaying->Peter) |
= %d\n” |
|
|
“sizeof(pSaying) |
= %d\n” |
|
|
“sizeof(*(pSaying)) |
= %d\n”, |
|
|
sizeof(OURSAYING), |
|
|
|
sizeof(OurSaying), |
|
|
|
sizeof(SAYING), |
|
|
|
sizeof(pOurSaying->Murphy), |
|
|
|
sizeof(pOurSaying->Peter), |
|
|
|
sizeof(pSaying), |
|
|
|
sizeof(*(pSaying))); |
|
for (i = 0; pOurSaying->Murphy.szSaying[i]; i++)
{
pOurSaying->Murphy.nLength[i] = strlen(pOurSaying- >Murphy.szSaying[i]);
}
for (i = 0; pOurSaying->Murphy.szSaying[i]; i++)
{
218
C Structures |
C C C |
|
C7C |
|
C C C |
|
C |
printf(“pOurSaying->Murphy %p %3d ‘%s’\n”, &pOurSaying->Murphy.szSaying[i], pOurSaying->Murphy.nLength[i], pOurSaying->Murphy.szSaying[i]);
}
printf(“\n\n”);
for (i = 0; pSaying->szSaying[i]; i++)
{
pSaying->nLength[i] = strlen(pSaying->szSaying[i]);
}
for (i = 0; pSaying->szSaying[i]; i++)
{
printf(“pOurSaying->Peter %p %3d ‘%s’\n”, &pSaying->szSaying[i], pSaying->nLength[i], pSaying->szSaying[i]);
}
printf(“\n\n”);
return (0);
}
When a structure is accessed with a pointer, theusual method of obtaining a value from memory (using the * operator) is unsatisfactory. To access a member of a structure pointed to by a pointer, you use the -> structure pointer operator rather than the . structure member operator. The -> operator is used as shown in Listing 7.8. You use the address of operator to assign the address of the structure to the pointer.
Understanding unions
If a structure is a group of related data objects, what is a union?
In a structure, each member is stored separately. Modifying one member of a structure does not change the contents of any other member.
219
Part II • Managing Data in C
In a union, all the members share the same block of storage. The block of storage is large enough to hold the largest member; smaller members use only as much storage as necessary. If you change what is stored in one member of a union, all other members are changed too.
Figure 7.1 shows the relationship between a structure and a union in memory. This figure shows the relationship between allocated memory and the members that are part of the data object.
Figure 7.1. A structure and a union in memory.
The UNION.C program in Listing 7.9 reads the database file created with the CREATEDB.C program (Listing 7.6). UNION.C places the result of the read into a union. It then checks what type of record was read and calls the correct function to process the record.
Listing 7.9. UNION.C.
/* UNION, written 1992 by Peter D. Hipson
*This program reads the CREATEDB.C database. The
*program has minimal error checking; it will fail
*if you provide a field value that is too long for the
*structure member that holds it. Use with caution!
*/
220
C Structures
#include <string.h> #include <ctype.h> #include <stdio.h> #include <process.h> #include <stdlib.h>
#define CUSTOMER_RECORD 1 #define SUPPLIER_RECORD 2
// Define the structure for the customer database.
C C C
C7C C
C C C
typedef |
struct |
_CUSTNAME { |
|
|
|
|
|
|
int |
nRecordType; |
|
|
|
|
|
|
|
char |
szName[61]; |
// |
60 |
chars for |
name; 1 for null at |
end |
||
char |
szAddr1[61]; |
// |
60 |
chars for |
address; 1 |
for null |
at end |
|
char |
szAddr2[61]; |
// |
60 |
chars for |
address; 1 |
for null |
at end |
|
char |
szCity[26]; |
// |
25 characters for city; |
1 for null at end |
||||
char |
szState[3]; |
// |
2-character state abbreviation + |
null |
||||
int |
nZip; |
// |
Use integer; |
print as %5.5d for leading 0 |
||||
int |
nRecordNumber; |
// |
Which record number? |
|
|
|||
double dSalesTotal; |
// Amount the customer has purchased |
|
||||||
} CUSTNAME; |
|
|
|
|
|
|
|
|
typedef |
CUSTNAME near *NPCUSTNAME; |
|
|
|
||||
typedef |
CUSTNAME |
*PCUSTNAME; |
|
|
|
|
|
|
typedef |
struct |
_SUPPLIERNAME { |
|
|
|
|
||
int |
nRecordType; |
|
|
|
|
|
|
|
char |
szName[61]; |
// |
60 |
chars for |
name; 1 for null at |
end |
||
char |
szAddr1[61]; |
// |
60 |
chars for |
address; 1 |
for null |
at end |
|
char |
szAddr2[61]; |
// |
60 |
chars for |
address; 1 |
for null |
at end |
|
char |
szCity[26]; |
// |
25 characters for city; |
1 for null at end |
||||
char |
szState[3]; |
// |
2-character state abbreviation + |
null |
||||
int |
nZip; |
// |
Use integer. Print as %5.5d for leading 0 |
|||||
int |
nRecordNumber; |
// |
Which record number? |
|
|
double dSalesTotal; // Amount the customer has purchased
} SUPPLIERNAME;
continues
221
Part II • Managing Data in C
Listing 7.9. continued
typedef SUPPLIERNAME near *NPSUPPLIERNAME; typedef SUPPLIERNAME *PSUPPLIERNAME;
typedef union _DBRECORD { CUSTNAME Customer; SUPPLIERNAME Supplier; } DBRECORD;
/* |
Local prototypes (use the typedef’ed names, |
|
* |
so must follow typedefs): |
|
*/ |
|
|
SUPPLIERNAME |
ProcessSupplier(NPSUPPLIERNAME); |
|
CUSTNAME |
ProcessCustomer(NPCUSTNAME); |
// main() function, the called functions
void main()
{
DBRECORD dbRecord;
FILE |
*DataFile; |
char |
szFileName[25]; |
char |
szBuffer[129]; |
int |
i; |
int |
nResult[3]; |
double |
dSales = 0.0; // Forces loading of floating-point support |
printf(“Please enter customer database name: “);
gets(szFileName);
DataFile = fopen(szFileName, “rb”);
222
C Structures |
C C C |
|
C7C |
|
C C C |
|
C |
if (DataFile == NULL)
{
printf(“ERROR: File ‘%s’ couldn’t be opened.\n”, szFileName);
exit(4);
}
nResult[0] = 1;
while (nResult[0] == 1)
{
nResult[0] = fread((char *)&dbRecord, sizeof(DBRECORD), 1, DataFile);
if (nResult[0] != 1)
{
if (!feof(DataFile))
{
printf(“ERROR: File ‘%s’, read error.\n”, szFileName);
fclose(DataFile);
exit(4);
}
else
{
printf(“End of database file ‘%s’.\n”, szFileName);
}
}
else
{
//You could test dbRecord.Supplier.nRecordType, or
switch(dbRecord.Customer.nRecordType)
{
case CUSTOMER_RECORD:
ProcessCustomer(&dbRecord.Customer);
break;
case SUPPLIER_RECORD:
continues
223
Part II • Managing Data in C
Listing 7.9. continued
ProcessSupplier(&dbRecord.Supplier);
break;
default:
printf(“ERROR: Invalid record type read from \ database \n”);
break;
}
}
}
fclose(DataFile);
}
SUPPLIERNAME ProcessSupplier(
NPSUPPLIERNAME npSupplier)
{
SUPPLIERNAME WorkSupplier;
WorkSupplier = *npSupplier;
printf(“Supplier name: %s\n”, npSupplier->szName);
//Do other processing for Supplier...
//.
//.
//.
//Return WorkSupplier to caller.
return(WorkSupplier);
}
CUSTNAME ProcessCustomer(
NPCUSTNAME npCustomer)
224
C Structures
{
CUSTNAME WorkCustomer;
WorkCustomer = *npCustomer;
printf(“Customer name: %s\n”, npCustomer->szName);
//Do other processing for customer...
//.
//.
//.
//Return WorkCustomer to caller.
return(WorkCustomer);
}
C C C
C7C C
C C C
An integer that determines the record type is the first field of each of the two structures that make up the union. Another common way to refer to a field like this is to code the definitions as
typedef union _DBRECORD {
int |
nRecordType; |
CUSTNAME |
Customer; |
SUPPLIERNAME |
Supplier; |
} DBRECORD; |
|
In this definition, you also have a record type variable as part of the union. You can check the value of the record type variable by simply using the following format,
rather than Customer or Supplier:
DBRECORD dbRecord;
/* Read a database record into dbRecord */
switch(dbRecord.nRecordType) // Rather than //
//dbRecord.Customer.nRecordType
{
With this format, the first field of each structure must still be an integer that will hold the record type. However, you can refer to the first field directly, which makes the code easier to read.
225