Teach Yourself C in 21 Days - KLDP

1 downloads 167 Views 3MB Size Report
Jun 13, 1991 - Here's how you access the source code from the Internet: ... Dennis Ritchie at the Bell Telephone Laborat
;-_=_Scrolldown to the Underground_=_-;

Teach Yourself C in 21 Days http://kickme.to/tiger/

Teach Yourself C in 21 Days

Table of Contents: ●

Introduction



Day 1 - Getting Started with C



Day 2 - The Components of a C Program



Day 3 - Storing \n\\a\t\tbell (alert)" ); 57: printf( "\n\\b\t\tbackspace" ); 58: printf( "\n...\t\t..."); 59: } 1 - Beep Computer 2 - Display Report 3 - Quit Enter a selection:1 Beeping the computer 1 - Beep Computer 2 - Display Report

3 - Quit Enter a selection:2 SAMPLE REPORT Sequence Meaning ========= ======= \a bell (alert) \b backspace ... ... 1 - Beep Computer 2 - Display Report 3 - Quit Enter a selection:3 You chose to quit! ANALYSIS: Listing 7.1 seems long compared with previous examples, but it offers some additions that are worth noting. The STDIO.H header was included in line 3 because printf() is used in this listing. In line 5, a constant named QUIT is defined. From Day 3, "Storing ======================"); /* Print the addresses of each array element. */ for (x = 0; x < 10; x++) printf("\nElement %d:\t%ld\t\t%ld\t\t%ld", x, &i[x], &f[x], &d[x]); printf("\n================================"); printf("======================\n"); return 0; }

Integer Float Double ============================================== Element 0: 1392 1414 1454 Element 1: 1394 1418 1462 Element 2: 1396 1422 1470 Element 3: 1398 1426 1478 Element 4: 1400 1430 1486 Element 5: 1402 1434 1494 Element 6: 1404 1438 1502 Element 7: 1406 1442 1510 Element 8: 1408 1446 1518 Element 9: 1410 1450 1526 ============================================== ANALYSIS: The exact addresses that your system displays might be different from these, but the relationships are the same. In this output, there are two bytes between int elements, four bytes between float elements, and eight bytes between double elements. (Note: Some machines use different sizes for variable types. If your machine differs, the preceding output might have different-size gaps; however, they will be consistent gaps.) This listing takes advantage of the escape characters discussed on Day 7, "Fundamentals of Input and Output." The printf() calls in lines 16 and 24 use the tab escape character (\t) to help format the table by aligning the columns. Looking more closely at Listing 9.2, you can see that three arrays are created in lines 8, 9, and 10. Line 8 declares array i of type int, line 9 declares array f of type float, and line 10 declares array d of type double. Line 16 prints the column headers for the table that will be displayed. Lines 18 and 19, along with lines 27 and 28, print dashed lines across the top and bottom of the table ; /* Wrong! String longer than array. */ char state2[10]="MN"; /* OK, but wastes space because */ /* string is shorter than array. */ If, on the other hand, you define a pointer to type char, these restrictions don't apply. The variable is a storage space only for the pointer. The actual strings are stored elsewhere in memory (but you don't need to worry about where in mem-ory). There's no length restriction or wasted space. The actual string is stored else-where. A pointer can point to a string of any length. Q Why shouldn't I just declare big arrays to hold values instead of using a memory allocation function such as malloc()? A Although it might seem easier to declare large arrays, this isn't an effective use of memory. When you're writing small programs, such as those in this chapter, it might seem trivial to use a function such as malloc() instead of arrays, but as your programs get bigger, you'll want to be able to allocate memory only as needed. When you're done with memory, you can put it back by freeing it. When you free memory, some other variable or array in a different part of the program can use it. (Day 20, "Working with Memory," covers freeing allocated memory.) Q Do all computers support the extended ASCII character set? A No. Most PCs support the extended ASCII set. Some older PCs don't, but the number of older PCs lacking this support is diminishing. Most programmers use the line and block characters of the extended set. Q What happens if I put a string into a character array that is bigger than the array? A This can cause a hard-to-find error. You can do this in C, but anything stored in the memory directly after the character array is overwritten. This could be an area of memory not used, some other \nchar %d", sizeof(char) ); printf( "\nshort %d", sizeof(short) ); printf( "\nint %d", sizeof(int) ); printf( "\nfloat %d", sizeof(float) ); printf( "\ndouble %d", sizeof(double) ); printf( "\n\nunsigned char printf( "\nunsigned short printf( "\nunsigned int return 0; }

%d", sizeof(unsigned char) ); %d", sizeof(unsigned short) ); %d\n", sizeof(unsigned int) );

Variable Type Sizes ========================= char 1 short 2 int 2 float 4 double 8 unsigned char 1 unsigned short 2 unsigned int 2 ANALYSIS: As you can see, the sizeof() operator is used to print the size in bytes of each variable type. The output shown is based on the program's being compiled on a 16-bit IBM-compatible PC with a 16-bit compiler. If compiled on a different machine or with a different compiler, the sizes might be different. For example, a 32-bit compiler on a 32-bit machine might yield four bytes rather than two for the size of an integer.

Maximum and Minimum Values If different machines have variable types that are different sizes, how do you know what values can be stored? It depends on the number of bytes that make up the Advanced Compiler Use," you learned about several preprocessor directives you can use. Several preprocessor directives have been defined in the ANSI standards. You use two of these all the time: #include and #define. Several other preprocessor directives are in the ANSI standards. The additional preprocessor directives available under the ANSI guidelines are listed in Table D.6. Table D.6. ANSI standard preprocessor directives. #define #if #elif #else

#ifdef #ifndef

#endif #include

#error #pragma Using Predefined Constants Every compiler comes with predefined constants. A majority of these are typically compiler-specific. This means that they probably won't be portable from one compiler to the next. However, several predefined constants are defined in the ANSI standards. The following are some of these constants: Constant Description __DATE__ This is replaced by the date at the time the program is compiled. The date is in the form of a literal string (text enclosed in double quotes). The format is "Mmm DD, YYYY". For example, January 1, 1998 would be "Jan 1, 1998". __FILE__ This is replaced with the name of the source file at the time of compilation. This will be in the form of a literal string. __LINE__ This will be replaced with the number of the line on which __LINE__ appears in the source code. This will be a numeric decimal value. __STDC__ This literal will be defined as 1 if the source file is compiled with the ANSI standard. If the source file wasn't compiled with the ANSI flag set, this value will be undefined. __TIME__ This is replaced with the time that the program is compiled. This time is in the form of a literal string (text enclosed in double quotes). The format is "HH:MM:SS". An example would be "12:15:03".

Using Non-ANSI Features in Portable Programs A program can use constants and other commands that aren't ANSI-defined and still be portable. You accomplish this by ensuring that the constants are used only if compiled with a compiler that supports the features used. Most compilers provide defined constants that you can use to identify them. By setting up areas of the code that are supportive for each of the compilers, you can create a portable program. Listing D.7 demonstrates how this can be done. Listing D.7. A portable program with compiler specifics. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10:

/*=======================================================* * Program: listD07.c * * Purpose: This program demonstrates using defined * * constants for creating a portable program. * * Note: This program gets different results with * * different compilers. * *=======================================================*/ #include #ifdef _WINDOWS

11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45:

#define STRING "DOING A WINDOWS PROGRAM!\n" #else #define STRING "NOT DOING A WINDOWS PROGRAM\n" #endif int main(void) { printf( "\n\n") ; printf( STRING ); #ifdef _MSC_VER printf( "\n\nUsing a Microsoft compiler!" ); printf( "\n Your Compiler version is %s\n", _MSC_VER ); #endif #ifdef __TURBOC__ printf( "\n\nUsing the Turbo C compiler!" ); printf( "\n Your compiler version is %x\n", __TURBOC__ ); #endif #ifdef __BORLANDC__ printf( "\n\nUsing a Borland compiler!\n" ); #endif return(0); }

Here's the output you'll see when you run the program using a Turbo C for DOS 3.0 compiler: NOT DOING A WINDOWS PROGRAM Using the Turbo C compiler! Your compiler version is 300 Here's the output you'll see when you run the program using a Borland C++ compiler under DOS: NOT DOING A WINDOWS PROGRAM Using a Borland compiler! Here's the output you'll see when you run the program using a Microsoft compiler under DOS:

NOT DOING A WINDOWS PROGRAM Using a Microsoft compiler! Your compiler version is >> ANALYSIS: This program takes advantage of defined constants to determine information about the compiler being used. In line 9, the #ifdef preprocessor directive is used. This directive checks to see whether the following constant has been defined. If the constant has been defined, the statements following the #ifdef are executed until an #endif preprocessor directive is reached. In the case of line 9, a determination is made as to whether _WINDOWS has been defined. An appropriate message is applied to the constant STRING. Line 22 then prints this string, which states whether or not this listing has been compiled as a Windows program. Line 24 checks to see whether _MSC_VER has been defined. _MSC_VER is a constant that contains the version number of a Microsoft compiler. If a compiler other than a Microsoft compiler is used, this constant won't be defined. If a Microsoft compiler is used, this will be defined with the compiler's version number. Line 27 will print this compiler version number after line 26 prints a message stating that a Microsoft compiler was used. Lines 31 through 36 and lines 38 through 42 operate in a similar manner. They check to see whether Borland's Turbo C or Borland's professional compiler were used. The appropriate message is printed based on these constants. As you can see, this program determines which compiler is being used by checking the defined constants. The object of the program--to print a message stating which compiler is being used--is the same regardless of which compiler is used. If you're aware of the systems that you will be porting, you can put compiler-specific commands into the code. If you do use compiler-specific commands, you should ensure that the appropriate code is provided for each compiler.

ANSI Standard Header Files Several header files that can be included are set by the ANSI standards. It's good to know which header files are ANSI standard, because these can be used in creating portable programs. Appendix E, "Common C Functions," contains the ANSI header files along with a list of their functions.

Summary This appendix exposed you to a great deal of material. This information centered on portability. C is one of the most portable languages--if not the most portable. Portability doesn't happen by accident. ANSI standards have been created to ensure that C programs can be ported from one compiler to another and from one computer system to another. You should consider several areas when writing portable code. These areas include variable case, choosing which character set to use, using portable numerics, ensuring variable sizes, comparing characters, using structures and unions, and using preprocessor directives and preprocessor constants. This appendix ended with a discussion of how to incorporate compiler specifics into a portable program.

Q&A Q How do you write portable graphics programs? A ANSI doesn't define any real standards for programming graphics. Graphics pro-gramming is more machine-dependent than other programming areas, so it can be somewhat difficult to write portable graphics programs. Q Should you always worry about portability? A No, it's not always necessary to consider portability. Some programs you write will be used only by you on your system. In addition, some programs won't be ported to a different computer system. Because of this, some nonportable functions, such as system(), can be used that wouldn't be used in portable programs. Q Are comments portable if they are done with // instead of /* and */? A No. Forward-slash comments come from C++. Many C programmers now use these comments. Although they will most likely be a standard in the future, you might find that some current C compilers don't support them.

Workshop The Workshop provides quiz questions to help you solidify your understanding of the material covered and exercises to provide you with experience in using what you've learned. Answers are not provided for the following quiz questions and exercises.

Quiz 1. Which is more important: efficiency or maintainability? 2. What is the numeric value of the letter a? 3. What is guaranteed to be the largest unsigned character value on your system? 4. What does ANSI stand for? 5. Are the following variable names valid in the same C program? int lastname, LASTNAME, LastName, Lastname; 6. What does isalpha() do? 7. What does isdigit() do? 8. Why would you want to use functions such as isalpha() and isdigit()? 9. Can structures be written to disk without worrying about portability? 10. Can __TIME__ be used in a printf() statement to print the current time in a program? Here's an example:

printf( "The Current Time is:

%s", __TIME__ );

Exercises 1. BUG BUSTER: What, if anything, is wrong with the following function? void Print_error( char *msg ) { static int ctr = 0, CTR = 0; printf("\n" ); for( ctr = 0; ctr < 60; ctr++ ) { printf("*"); } printf( "\nError %d, %s - %d: %s.\n", CTR, __FILE__, __LINE__, msg ); for( ctr = 0; ctr < 60; ctr++ ) { printf("*"); } } 2. Write a function that verifies that a character is a vowel. 3. Write a function that returns 0 if it receives a character that isn't a letter of the alphabet, 1 if it is an uppercase letter, and 2 if it is a lowercase letter. Keep the function as portable as possible. 4. For your compiler, determine what flags must be set to ignore variable case, allow for byte alignment, and guarantee ANSI compatibility. 5. Is the following code portable? void list_a_file( char *file_name ) { system("TYPE " file_name ); } 6. Is the following code portable? int to_upper( int x ) { if( x >= `a' && x 0 if the value of element 1 is greater than element 2, and