Advanced C 1992
.pdfDecimal, Binary, Hex, and Octal |
C C C |
|
C5C |
|
C C C |
|
C |
makes the decimal-number characters fully usable. Octal is used for DEC minicomputers, and C was developed on DEC minicomputers—therefore, the support for octal. Figure 5.5 shows a representation of the octal numbers, which are similar to the ones in the decimal-based systems; octal, however, doesn’t have numbers 8 or 9.
Figure 5.5. Octal numbers 0 through 7.
When the 1992 example is represented in octal, it becomes 3710. This rather misleading number is as long as its decimal equivalent (and could have been longer). Without some form of prefix (or postfix) notation, you have no way to determine whether any number is octalor decimal-based (3710 is a legitimate number).
Look at Figure 5.6 to see how that octal value for 1992 is determined. This figure shows the octal value, the value of each digit, and the decimal result.
Figure 5.6. The year 1992 in decimal and octal.
145
Part I • Honing Your C Skills
Looking at a File
In any program that writes a file that is not pure text, you must able to look at the file and determine whether the program has written the file properly. When you run the DEBUG utility, a crude debugger, on the PC, it enables you to dump programs and data files. Because the program is difficult to use, however, it is not used often.
One solution is to have a program that dumps files and provides both a hex and ASCII listing of the file’s contents (see Listing 5.1,). DUMP is a simple program that reads files of any length and lists their contents in an easy-to-use format.
Listing 5.1. DUMP.C.
/* DUMP, written 23 May 1992 by Peter D. Hipson */ /* A program that dumps files in hex and ASCII. */
#include |
|
<stdio.h> |
// Make includes first part of file |
#include |
|
<string.h> |
// For string functions. |
#include |
|
<stdlib.h> |
// Standard include items. |
#include |
|
<process.h> |
// For exit(), etc. |
#include |
|
<time.h> |
// For time information. |
#define |
|
ARG_HELP |
‘?’ |
#define |
|
ARG_SLASH |
‘/’ |
#define |
|
ARG_DASH |
‘-’ |
int main( |
|
// Define main() and the fact that |
|
int |
|
argc, |
// this program uses the passed parameters. |
char |
|
*argv[], |
|
char |
|
*envp[] |
|
); |
|
|
|
int main( |
|
|
|
int argc, |
|
||
char |
|
*argv[], |
|
char |
|
*envp[]) |
|
{ |
|
|
|
FILE |
*fpFilePointer; |
146
Decimal, Binary, Hex, and Octal
long |
lPosition; |
int |
i; |
int |
j; |
int |
nNumberBytesRead; |
unsigned |
int nHexNumber; |
char |
*pszTemp; |
/* strings for _splitpath() (which parses a file name) */
char |
szDrive[_MAX_DRIVE]; |
char |
szDir[_MAX_DIR]; |
char |
szFname[_MAX_FNAME]; |
char |
szExt[_MAX_EXT]; |
char |
szInputFile[128]; |
char |
szProgram[132]; |
char |
szBuffer[132]; |
char |
sBuffer[257]; |
time_t |
tTime; |
struct |
tm *pTM; |
_splitpath(argv[0], szDrive,
szDir,
szFname,
szExt);
strncpy(szProgram, szFname, sizeof(szProgram) - 1);
if (argc <= 1)
{
printf(“%s: - No file name given.\n”, szProgram); exit(4);
}
for (i = 1; argv[i]; i++)
{
C C C
C5C C
C C C
continues
147
Part I • Honing Your C Skills
Listing 5.1. continued
if (argv[i][0] == ‘/’ || argv[i][0] == ‘-’)
{/* You have an argument, convert to lowercase, and test. */ pszTemp = strlwr(argv[i]);
for (j = 1; j < strlen(pszTemp); j++)
{
switch(pszTemp[j])
{
case ARG_HELP:
printf(“Usage: %s filename.ext \n”, szProgram);
exit(4);
break;
case ARG_SLASH: case ARG_DASH: break;
default:
printf(“%s: - Invalid option ‘%c’.\n”, pszTemp[j],
szProgram);
break;
}
}
}
else
{/* Either a filename or width. */ strcpy(szInputFile, argv[i]);
}
}
if ((fpFilePointer = fopen(szInputFile, “r+b”)) == NULL)
{
printf(“%s: Unable to open file: %s\n”, szProgram,
szInputFile);
148
Decimal, Binary, Hex, and Octal
exit(16);
}
lPosition = 0l;
printf(“\n”);
time(&tTime);
pTM = localtime(&tTime);
/* format a time string, using strftime() (new with ANSI C) */
strftime(szBuffer,
sizeof(szBuffer),
“%A %B %d, %Y at %H:%M:%S”, pTM);
printf(“Dump of %s, %s\n\n”, szInputFile,
szBuffer);
while((nNumberBytesRead = fread((char *)sBuffer, sizeof(char), 16, fpFilePointer)) > 0)
{
printf(“ %8.8X -”, lPosition);
for (i = 0; i < 16; i++)
{
if (i == 8)
{
printf(“ - “);
}
else
{
if (i == |
0 |
|| |
i == |
4 |
|| |
i == |
12) |
|
{ |
|
|
C C C
C5C C
C C C
continues
149
Part I • Honing Your C Skills
Listing 5.1. continued
printf(“ “);
}
}
if (i < nNumberBytesRead)
{
nHexNumber = (unsigned char)sBuffer[i];
printf(“%2.2X”, (unsigned int)nHexNumber);
}
else
{
printf(“ “);
}
}
for (i = 0; i < nNumberBytesRead; i++)
{
if (sBuffer[i] < ‘ ‘ || sBuffer[i] == ‘\xFF’)
{
sBuffer[i] = ‘.’;
}
}
sBuffer[nNumberBytesRead] = ‘\0’;
printf(“ : %s”, sBuffer);
printf(“ \n”); lPosition += 16;
}
return(0);
}
150
Decimal, Binary, Hex, and Octal |
C C C |
|
C5C |
|
C C C |
|
C |
DUMP.C has few unusual parts. The first part of the program is the same command line arguments parser from Chapter 4, “Special Pointers and Their Use,” with a test for the help option (standardized as /? under DOS on the PC). DUMP has no other options and simply requires the name of the file to dump.
The file is opened and read in 16 bytes at a time (or less, if fewer than 16 bytes remain in the file). The buffer, with 16 bytes, is written out, first in hex format and then in ASCII format (with control characters, and a . character substituted for DEL.
DUMP enables you to look at a file’s output; you still must understand what the output means, however.
There are two ways to store integers in memory. The first method, in which the high-order bits are stored in the low byte or bytes, makes dumps easy to read; in the second method, the low-order bits are stored in the low byte or bytes. One method makes it easier for you to look at a dump and determine an integer’s value, and the other method makes you work a little harder. The PC, of course, makes you work harder; supposedly, it makes the CPU faster, but we’ll never know. Figure 5.7 shows both a 16-bit integer and a 32-bit integer, as they are stored in the PC’s format.
Figure 5.7. Integers in memory (16 and 32 bits).
The method your CPU uses to store integers must always be considered whenever you are viewing memory directly. If you do not know the order of the bits in storage, the simple program in Listing 5.2 tells you which method is being used.
151
Part I • Honing Your C Skills
Listing 5.2. WCHBYTE.C.
/* Program WCHBYTE, written 25 May 1992 by Peter D. Hipson */ /* Program that shows byte swapping (if present) by the CPU. */
#include |
<stdio.h> |
// Make includes first part of file |
|
#include |
<string.h> |
// For string functions. |
|
#include |
<stdlib.h> |
// Standard include items. |
|
#include |
<process.h> // For exit() etc. |
||
int main( |
|
// Define main() and the fact that this program uses |
|
int |
argc, |
// the passed parameters. |
|
char |
*argv[], |
|
|
char |
*envp[] |
|
|
); |
|
|
|
void NonPrint(const char |
chChar); |
||
void Letter(const char |
chChar); |
||
void Number(const char |
chChar); |
||
void Space(const char |
chChar); |
||
int main( |
|
|
|
int |
argc, |
|
|
char |
*argv[], |
|
|
char |
*envp[] |
|
|
) |
|
|
|
{
unsigned char cTemp[10]; unsigned char *pcTemp;
int |
nYear = 1992; |
long int lYearYear = 19921992; |
|
char |
szHello[] = “Hello”; |
pcTemp = (unsigned char *)&nYear; cTemp[0] = *(pcTemp++);
cTemp[1] = *(pcTemp);
152
Decimal, Binary, Hex, and Octal |
C C C |
|
C5C |
|
C C C |
|
C |
printf(“nYear = %d decimal, %4.4X hex, in memory %2.2X %2.2X\n”, nYear,
nYear,
cTemp[0],
cTemp[1]);
pcTemp = (unsigned char *)&lYearYear; cTemp[0] = *(pcTemp++);
cTemp[1] = *(pcTemp++); cTemp[2] = *(pcTemp++); cTemp[3] = *(pcTemp);
printf(“lYearYear = %ld decimal %8.8lX hex, in memory %2.2X %2.2X \ %2.2X %2.2X\n”,
lYearYear,
lYearYear,
cTemp[0],
cTemp[1],
cTemp[2],
cTemp[3]);
pcTemp = (unsigned char *)&szHello[0]; cTemp[0] = *(pcTemp++); // H
cTemp[1] = *(pcTemp++); // e cTemp[2] = *(pcTemp++); // l cTemp[3] = *(pcTemp++); // l cTemp[4] = *(pcTemp++); // o cTemp[5] = *(pcTemp++); // \0 (NULL)
printf(“szHello = ‘%s’ (string), in memory ‘%c’ ‘%c’ ‘%c’ ‘%c’ ‘%c’ \ ‘%c’ \n”,
szHello,
cTemp[0],
cTemp[1],
cTemp[2],
cTemp[3],
cTemp[4],
cTemp[5]);
return(0);
}
153
Part I • Honing Your C Skills
If the hex representation and the memory view of the variables are the same when you run WCHBYTE, dumps made using DUMP will be correct. If they are different, however (which is the case for all PCs), you have to swap the bytes manually when you are using a DUMP listing.
Bit Operators
Bit operators form the basis for C’s powerful bit-manipulation capabilities. Never confuse these operators with their logical counterparts, which work on different principles. In Table 5.1, the keyword TRUE signifies a true bit (or bits) that is set to one, and FALSE signifies a bit (or bits) that is set to zero.
Table 5.1. Bitwise operators.
Operator Description
&Performs a bitwise AND operation. If both operands are TRUE, the result is TRUE; otherwise, the result is FALSE.
| |
Performs a bitwise OR operation. If either operand is TRUE, the |
|
result is TRUE; otherwise, the result is FALSE. |
^Performs a bitwise exclusive OR operation. If both operands are TRUE or both operands are FALSE, the result is FALSE. The result is TRUE if one operand is TRUE and the other is FALSE. Exclusive OR is
used to test to see that two operands are different.
<<Shifts the X operand, Y operand bits to the left. For example, (1 << 4) returns a value of 8. In bits, (0001 << 4) results in 1000. New positions to the left are filled with zeroes. This is a quick way to multiply by 2, 4, 8, and so on.
>>Shifts the X operand, Y operand bits to the right. For example, (8 >> 4) returns a value of 1. In bits, (1000 >> 4) results in 0001. New positions to the right are filled with ones or zeroes, depending on the value and whether the operand being shifted is signed. This is a quick way to divide by 2, 4, 8, and so on.
~Returns the 1’s complement of the value. The 1’s complement is defined as setting all bits that are 1 to 0, and all bits that are 0 to 1.
154