Файл: Programming Microcontrollers in C, 2-nd edit (Ted Van Sickle, 2001).pdf
ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 15.06.2025
Просмотров: 4030
Скачиваний: 1
8 Chapter 1 Introduction to C
of course, is less than 11, so the statement following the while will be executed again and new values will be printed to the screen. This sequence will be repeated until the incremented value for i equals 11, at which time i<11 will be FALSE. At that point in the pro gram, the statement following the while will be skipped, and the program will have reached its end. The result of executing the above program is shown in the following table:
i |
i |
i |
squared |
cubed |
|
1 |
1 |
1 |
2 |
4 |
8 |
3 |
9 |
27 |
4 |
16 |
64 |
5 |
25 |
125 |
6 |
36 |
216 |
7 |
49 |
343 |
8 |
64 |
512 |
9 |
81 |
729 |
10 |
100 |
1000 |
EXERCISES
1.Write, compile, and execute each of the example programs shown in this section.
2.Write a program to calculate the Fahrenheit temperature for the Cel sius values between 0° degrees and 100° in steps of 10° each. The conversion formula is F=9*C/5+32. Use integer variables, and ex amine the result when you use F=C*(9/ 5) + 32. What went wrong?
Names
Variables, constants and functions in C are named, and the pro gram controls operations on these named variables and constants. Variables and constants are called operands. Names can be as many as 31 characters long. The characters that make up the name can be the upper and the lower case letters, the digits 0 through 9, and the underscore character ‘_’. There are several defined constants and functions that are used by the compiler. All of these names begin
Names 9
with an underscore. Because of this convention, you should avoid the use of an underscore as the first character for either function or variable names in your code. This approach will completely avoid name conflict with these hidden or unexpected names. Compilers usually allow the names to be unique in the first 31 characters. Un fortunately, some linkers used to link various program modules require that the names be unique in the first six or eight characters, depend ing on the linker.
C has a collection of keywords that cannot be used for names. These keywords are listed below:
KEYWORDS
auto |
double |
int |
struct |
break |
else |
long |
switch |
case |
enum |
register |
typedef |
char |
extern |
return |
union |
const |
float |
short |
unsigned |
continue |
for |
signed |
void |
default |
goto |
sizeof |
volatile |
do |
if |
static |
while |
Types and Type Declarations
C has only a few built-in types. Here they are:
char—is usually eight bits. The character is the smallest stor age unit.
int—an integer is usually the size of the basic unit of storage for the machine. An int must be at least 16 bits wide.
float—a single precision floating-point number.
double—a double precision floating-point number.
Additional qualifiers are used to modify the basic types. These qualifiers include:
short—modifies an int, and is a variable whose width is no greater than that of the int. For example, with a compiler with a 32 bit int a short int could be 16 bits. You will find examples where short and int are the same size.
10 Chapter 1 Introduction to C
long—modifies an int, and is a variable size whose width is no less than that of an int. For example, on a 16-bit machine, an int might be 16 bits, and a long int could be 32 bits. long can also modify a double to specify an extended precision floating-point number. You will find examples where a long and an int are the same size.
signed—modifies all integral numbers and produces a range of numbers that contains both positive and negative numbers. For example, if the type char is 8 bits, a signed char can contain the range of numbers –128 to +127. Default for char and int is signed when they are declared.
unsigned—modifies all integral numbers and produces a range of numbers that are positive only. For example, if the type char is 8 bits, an unsigned char can contain the range of numbers 0 to +255. It is not necessary to include the type int with the qualifiers short or long. Thus, the following statements are the same:
long int a,c; short int d;
and
long a,c; short d;
When a variable is defined, space is allocated in memory for its storage. The basic variable size is implementation dependent, and especially for microcontrollers, you will find that this variability will show up when you change from one microcomputer to another.
Each variable must be defined prior to being used. A variable may be defined at the beginning of any code block, and the variable’s scope is the block in which it is defined. When the block in which the variable is defined is exited, the variable goes out of existence. There is no problem with defining variables with the same name in differ ent blocks. The compiler will make certain that these variables do not get mixed up in the execution of the code.
An additional qualifier is const. When const is used as a quali fier on the declaration of any variable, an initialization value must be declared. This value cannot be changed by the program. Therefore the declaration
Types and Type Declarations |
11 |
const double PI = 3.14159265;
will create the value for the mathematical constant pi and store it in the location provided for PI. Any attempt to change the value of PI by the program will cause compiler error.
Conventions for writing constants are straightforward. A simple number with no decimal point is an int. To make a number long, you must suffix it with an l or an L. For example, 6047 is an int and 6047L is a long. The u or U suffix on a number will cause creation of a proper unsigned number.
A floating-point number must contain a decimal point or an ex ponent or both. The numbers 1.114 and 17.3e-5 are examples of floating point numbers. All floating point numbers are of the type double unless a suffix is appended to the number. Any number suffixed with an f or an F is a single precision floating-point num ber, and a suffix of l or L on a floating-point number will generate a type long double. Octal (base 8) and hexadecimal (base 16) numbers can be created. Any number that is prefixed with a 0—a leading zero—is taken to be an octal number. Hexadecimal numbers are prefixed with a 0x or a 0X. The rules above for L and U also apply to octal and hexadecimal numbers.
The final type qualifier is volatile. The qualifier volatile instructs the compiler to NOT optimize any code involving the vari able. In execution of an expression, a side effect refers to the fact that the expression alters something. The side effect of the following state ment
a=b+c;
is that the stored value of a is changed. A sequence point is a point in the code where all side effects of previous evaluations are completed and no side effects from subsequent evaluations will have taken place. An important consideration of the optimization is that if an expression has no side effects, it can be eliminated by the compiler. Therefore, if a statement involves no sequence point, or alters no memory, it is sub ject to being discarded by the compiler. This operation is not particularly bad when writing normal code, but when working with microcontrollers where events can occur as a result of hardware operations, not the program, this optimization can utterly destroy a program. For example, whenever the hardware can alter a stored value, the compiler should
12 Chapter 1 Introduction to C
be able to discard accesses to that value because the program never alters the value. In such a circumstance, if you had an analog-to-digital converter peripheral in your system, the program would never be re quired to read its return value more than once. “The program did not change the value stored in the input location subsequent to the first read, therefore its value has not changed and it is not necessary to read the location again.” This will always produce wrong results. The key word volatile indicates to the program that a variable must not be optimized. Therefore, if the input location is identified as a vola tile variable, it will not be optimized and the problem will go away. As a point of interest, accessing a volatile object, modifying an object, modifying a file, or calling a function that does any of those operations are all defined as side effects by the standard.
Storage Classes, Linkage, and Scope
Additional modifiers are called storage classes and designate where a variable is to be stored and how it is initialized. These stor age classes are auto (for automatic), static, and malloced. The first two storage classes are described in the following sections. The storage class malloc provides dynamic memory allocation and is discussed in detail in Chapter 2.
Automatic variables
For local variables defined within a function, the default storage class is auto. An automatic variable has the scope of the block in which it is defined, and it is uninitialized when it is created. Auto matic variables are usually stored on the program stack, so space for the variable is created when the function is entered. When the stack is cleaned up prior to the return at the end of the function, all vari ables stored on the stack are deleted.
As we saw in our first program example, variables can be initial ized at the time of declaration by assigning the variable an initial value:
int rupt=17;
An automatic variable will be assigned its initial value each time the block in which it is declared is entered. If the variable is not initialized at declaration, it will contain the contents of uninitialized memory, which can be any value.
Storage Classes, Linkage, and Scope |
13 |
Another class of variable is register. A register class variable is automatic, i.e., it comes into being at the beginning of the block in which it is defined and it goes out of scope at the end of the block. If a register is available in the computer, a register variable will be stored in a register. To define a register variable, you should use the form
register int roger=10;
These variables can be long, short, int, or char.
When a register is not available, a register variable will be stored just like any other automatic variable. A programmer might consider the use of register variables in code that contains “tight loops” to save the time of memory accesses while executing the loop. A bit of advice. Compilers have improved continuously over the past years. With today’s compilers, the optimizers are so efficient that the compiler can probably do a better job of assigning register vari ables than the programmer. Therefore, it makes little sense to specify a lot of register variables just to improve the efficiency of your code.
Static variables
Sometimes you might want to assign a value to a variable and have it retain that value for later function calls. Such a variable can be created by calling it static at its definition. There are two groups of static variables: Local static variables which have a scope of the function in which they are defined, and global or external static class variables. Unless otherwise declared, all static class variables are initialized to 0 when they are created.
There are two groups of external static variables. Any exter nal variable is a static class variable. It is automatically initialized to the value 0 when the program is loaded unless the value is other wise declared in the definition statement. An external variable that is declared as static in its definition statement like
static int redoubt;
will have file scope. Remember normal external variables can be accessed from any module in the entire program. A static exter nal variable can be accessed only from within the file in which it is defined. Note that static variables are not stored on the stack, but rather stored in a static data memory area.
14 Chapter 1 Introduction to C
Inside of a function, the following declaration is made:
static int keep = 1;
When the program is loaded and executed, the value 1 is assigned to keep. Thereafter, each time the function is entered, keep will not be initialized but will retain the value assigned to it the last time the function was executed.
Global variables can be designated as static. A global vari able that is static is similar to a conventional global variable with the exception that it can be accessed only from the file in which it is declared.
If there is an external variable that is declared in one file that is to be accessed by a function defined in another file, the function must notify the compiler that the variable is external with the use of the keyword extern. The following is an example of such an access.
In file 1:
int able;
int main(void)
{
long quickstart(void); long r;
.
.
.
able=17;
l=quickstart();
.
.
}
In file 2:
long quickstart(void)
{
extern int able;
.
/* do something with able */
.
Character Constants |
15 |
return result;
}
When the file 1 is compiled, the variable able is marked as external, and memory is allocated for its storage. When the file 2 is compiled, the variable able is recognized to be external because of the extern keyword, and no memory is allocated for the variable. When the link phase of the compilation is completed, all address references to able in file 2 will be assigned the address of able that was defined in file 1. The example above in which the declaration
extern int able;
allowed access to able from the file 2 will not work if able had been declared as follows in file 1:
static int able;
Character Constants
Character constants or escape sequences are data that can be stored in memory locations designated as char. A character constant is identified by a backslash preceding the character. We have seen the use of the character constants ‘\n’ and ‘\t’ in previous examples. Several of these escape sequences shown in the following table have predefined meanings.
Escape |
Meaning |
Sequence |
|
\a |
bell character |
\b |
backspace |
\f |
form feed |
\n |
new line |
\r |
carriage return |
\v |
vertical tab |
\t |
horizontal tab |
\? |
question mark |
\\back slash
\’ |
single quote |
\” |
double quote |
\ooo |
octal number |
\xxx |
hexadecimal number |