📄 c517.txt
字号:
发信人: reflection (似水流年), 信区: EEtechnology
标 题: C51 Primer (6) Pointers of C51
发信站: 南京大学小百合站 (Wed Nov 24 11:59:30 1999), 转信
6 Pointers In C51
Whilst pointers can be used just as in PC-based C, there are several importa
nt extensions to the way they are used in C51. These are mainly aimed at get
ting more efficient code
6.1 Using Pointers And Arrays In C51
One of C's greatest strengths can also be its greatest weakness - the pointe
r. The use and, more appropriately, the abuse of this language feature is la
rgely why C is condemned by some as dangerous!
6.1.1 Pointers In Assembler
For an assembler programmer the C pointer equates closely to indirect addres
sing. In the 8051 this is achieved by the following instructions
MOV R0,#40 ; Put on-chip address to be indirectly
MOV A,@RO addressed in R0
MOV R0,#40 ; Put off-chip address to be indirectly
MOVX A,@RO addressed in R0
MOVX A,@DPTR ; Put off-chip address to be indirectly
addressed in DPTR
CLR A
MOV DPTR,#0040 ; Put off-chip address to be indirectly
MOVC A,@A+DPTR addressed in DPTR
In each case the data is held in a memory location indicated by the value in
registers to the right of the '@'.
6.1.2 Pointers In C51
The C equivalent of the indirect instruction is the pointer. The register ho
lding the address to be indirectly accessed in the assembler examples is a n
ormal C type, except that its purpose is to hold an address rather than a va
riable or constant data value.
It is declared by:
unsigned char *pointer0 ;
Note the asterisk prefix, indicating that the data held in this variable is
an address rather than a piece of data that might be used in a calculation e
tc..
In all cases in the assembler example two distinct operations are required:
Place address to be indirectly addressed in a register.
Use the appropriate indirect addressing instruction to access data held at c
hosen address.
Fortunately in C the same procedure is necessary, although the indirect regi
ster must be explicitly defined, whereas in assembler the register exists in
hardware.
/* 1 - Define a variable which will hold an address */
unsigned char *pointer ;
/* 2 - Load pointer variable with address to be accessed*/
/*indirectly */
pointer = &c_variable ;
/* 3 - Put data '0xff' indirectly into c variable via*/
/*pointer */
*pointer = 0xff ;
Taking each operation in turn...
Reserve RAM to hold pointer. In practice the compiler attaches a symbolic na
me to a RAM location, just as with a normal variable.
Load reserved RAM with address to be accessed, equivalent to 'MOV R0,#40'. I
n English this C statement means: "take the 'address of' c_variable and put
it into the reserved RAM, i.e, the pointer" In this case the pointer's RAM c
orresponds to R0 and the '&' equates loosely to the assembler '#'.
Move the data indirectly into pointed-at C variable, as per the assembler 'M
OV A,@R0'.
The ability to access data either directly, x = y, or indirectly, x = *y_ptr
, is extremely useful. Here is C example:
/* Demonstration Of Using A Pointer */
unsigned char c_variable ; // 1 - Declare a c variable unsigned char *ptr
; // 2 - Declare a pointer (not
pointing at anything yet!)
main() {
c_variable = 0xff ; // 3 - Set variable equal to 0xff
directly
ptr = &c_variable ; // 4 - Force pointer to point at
c_variable at run time
*ptr = 0xff ; // 5 - Move 0xff into c_variable
indirectly
}
Note: Line 4 causes pointer to point at variable. An alternative way of doin
g this is at compile time thus:
/* Demonstration Of Using A Pointer */
unsigned char c_variable; //1-Declare a c variable
unsigned char *ptr = &c_variable; //2-Declare a pointer,
intialised to pointing at
c_variable during
compilation
main() {
c_variable = 0xff ; // 3 - Set variable equal to 0xff
directly
*ptr = 0xff // 5 - Move 0xff into c_variable
indirectly
}
Pointers with their asterisk prefix can be used exactly as per normal data t
ypes. The statement:
x = y + 3 ;
could equally well perform with pointers, as per
char x, y ;
char *x_ptr = &x ;
char *y_ptr = &y ;
*x_ptr = *y_ptr + 3 ;
or:
x = y * 25 ;
*x_ptr = *y_ptr * 25 ;
The most important thing to understand about pointers is that
*ptr = var ;
means "set the value of the pointed-at address to value var", whereas
ptr = &var ;
means "make ptr point at var by putting the address of (&) in ptr, but do no
t move any data out of var itself".
Thus the rule is to initialise a pointer,
ptr = &var ;
To access the data indicated by *ptr ;
var = *ptr ;
6.2 Pointers To Absolute Addresses
In embedded C, ROM, RAM and peripherals are at fixed addresses. This immedia
tely raises the question of how to make pointers point at absolute addresses
rather than just variables whose address is unknown (and largely irrelevant
).
The simplest method is to determine the pointed-at address at compile time:
char *abs_ptr = 0x8000 ; // Declare pointer and force to
//0x8000 immediately
However if the address to be pointed at is only known at run time, an altern
ative approach is necessary. Simply, an uncommitted pointer is declared and
then forced to point at the required address thus:
char *abs_ptr ; // Declare uncommitted pointer
abs_ptr = (char *) 0x8000 ; // Initialise pointer to 0x8000 *abs_ptr = 0xff
; // Write 0xff to 0x8000
*abs_ptr++ ; // Make pointer point at next
location in RAM
Please see sections 6.8 and 6.9 for further details on C51 spaced and generi
c pointers.
6.3 Arrays And Pointers - Two Sides Of The Same Coin?
6.3.1 Uninitialised Arrays
The variables declared via
unsigned char x ;
unsigned char y ;
are single 8 bit memory locations. The declarations:
unsigned int a ;
unsigned int b ;
yield four memory locations, two allocated to 'a' and two to 'b'. In other p
rogramming languages it is possible to group similar types together in array
s. In basic an array is created by DIM a(10).
Likewise 'C' incorporates arrays, declared by:
unsigned char a[10] ;
This has the effect of generating ten sequential locations, starting at the
address of 'a'. As there is nothing to the right of the declaration, no init
ial values are inserted into the array. It therefore contains zero data and
serves only to reserve ten contiguous bytes.
6.3.2 Initialised Arrays
A more usual instance of arrays would be
unsigned char test_array [] = { 0x00,0x40,0x80,0xC0,0xFF } ;
where the initial values are put in place before the program gets to "main()
". Note that the size of this initialised array is not given in the square b
rackets - the compiler works-out the size automatically.
Another common instance of an array is analogous to the BASIC string as per:
A$ = "HELLO!"
In C this equates to:
char test_array[] = { "HELLO!" } ;
In C there is no real distinction between strings and arrays as a C array is
just a series of sequential bytes occupied either by a string or a series o
f numbers. In fact the realms of pointers and arrays overlap with strings by
virtue of :
char test_array = { "HELLO!" } ;
char *string_ptr = { "HELLO!" } ;
Case 1 creates a sequence of bytes containing the ASCII equivalent of "HELLO
!". Likewise the second case allocates the same sequence of bytes but in add
ition creates a separate pointer called *string_ptr to it. Notice that the "
unsigned char" used previously has become "char", literally an ASCII charact
er.
The second is really equivalent to:
char test_array = { "HELLO!" } ;
Then at run time:
char arr_ptr = test_array ; // Array treated as pointer
or;
char arr_ptr = &test_array[0] ; // Put address of first
// element of array into
// pointer
This again shows the partial interchangeability of pointers and arrays. In E
nglish, the first means "transfer address of test_array into arr_ptr". Stati
ng an array name in this context causes the array to be treated as a pointer
to the first location of the array. Hence no "address of" (&) or '*' to be
seen.
The second case reads as "get the address of the first element of the array
name and put it into arr_ptr". No implied pointer conversion is employed, ju
st the return of the address of the array base.
The new pointer "*arr_ptr" now exactly corresponds to *string_ptr, except th
at the physical "HELLO!" they point at is at a different address.
6.3.3 Using Arrays
Arrays are typically used like this
/* Copy The String HELLO! Into An Empty Array */
unsigned char source_array[] = { "HELLO!" } ;
unsigned char dest_array[7];
unsigned char array_index ;
unsigned char
array_index = 0 ;
while(array_index < 7) { // Check for end of array
dest_array[array_index] = source_array[array_index] ;
//Move character-by-character into destination array
array_index++ ;
}
The variable array_index shows the offset of the character to be fetched (an
d then stored) from the starts of the arrays.
As has been indicated, pointers and arrays are closely related. Indeed the a
bove program could be re-written thus:
/* Copy The String HELLO! Into An Empty Array */
char *string_ptr = { "HELLO!" } ;
unsigned char dest_array[7] ;
unsigned char array_index ;
unsigned char
array_index = 0 ;
while(array_index < 7) { // Check for end of array
dest_array[array_index] = string_ptr[array_index] ; // Move character-by-ch
aracter into destination array.
array_index++ ;
}
The point to note is that by removing the '*' on string_ptr and appending a
'[ ]' pair, this pointer has suddenly become an array! However in this case
there is an alternative way of scanning along the HELLO! string, using the *
ptr++ convention:
array_index = 0 ;
while(array_index < 7) { // Check for end of array
dest_array[array_index] = *string_ptr++ ; // Move character-by-character in
to destination array.
array_index++ ;
}
This is an example of C being somewhat inconsistent; this *ptr++ statement d
oes not mean "increment the thing being pointed at" but rather, increment th
e pointer itself, so causing it to point at the next sequential address. Thu
s in the example the character is obtained and then the pointer moved along
to point at the next higher address in memory.
6.3.4 Summary Of Arrays And Pointers
To summarise
Create An Uncommitted Pointer
unsigned char *x_ptr ;
Create A Pointer To A Normal C Variable
unsigned char x ; unsigned char *x_ptr = &x ;
Create An Array With No Initial Values
unsigned char x_arr[10] ;
Create An Array With Initialised Values
unsigned char x_arr[] = { 0,1,2,3 } ;
Create An Array In The Form Of A String
char x_arr[] = { "HELLO" } ;
Create A Pointer To A String
char *string_ptr = { "HELLO" } ;
Create A Pointer To An Array
char x_arr[] = { "HELLO" } ; char *x_ptr = x_arr
Force A Pointer To Point At The Next Location
*ptr++ ;
6.4 Structures
Structures are perhaps what makes C such a powerful language for creating ve
ry complex programs with huge amounts of data. They are basically a way of g
rouping together related data items under a single symbolic name.
6.4.1 Why Use Structures?
Here is an example: A piece of C51 software had to perform a linearisation p
rocess on the raw signal from a variety of pressure sensors manufactured by
the same company. For each sensor to be catered for there is an input signal
with a span and offset, a temperature coefficient, the signal conditioning
amplifier, a gain and offset. The information for each sensor type could be
held in "normal" constants thus:
unsigned char sensor_type1_gain = 0x30 ;
unsigned char sensor_type1_offset = 0x50 ;
unsigned char sensor_type1_temp_coeff = 0x60 ;
unsigned char sensor_type1_span = 0xC4 ;
unsigned char sensor_type1_amp_gain = 0x21 ;
unsigned char sensor_type2_gain = 0x32 ;
unsigned char sensor_type2_offset = 0x56 ;
unsigned char sensor_type2_temp_coeff = 0x56 ;
unsigned char sensor_type2_span = 0xC5 ;
unsigned char sensor_type2_amp_gain = 0x28 ;
unsigned char sensor_type3_gain = 0x20 ;
unsigned char sensor_type3_offset = 0x43 ;
unsigned char sensor_type3_temp_coeff = 0x61 ;
unsigned char sensor_type3_span = 0x89 ;
unsigned char sensor_type3_amp_gain = 0x29 ;
As can be seen, the names conform to an easily identifiable pattern of:
unsigned char sensor_typeN_gain = 0x20 ;
unsigned char sensor_typeN_offset = 0x43 ;
unsigned char sensor_typeN_temp_coeff = 0x61 ;
unsigned char sensor_typeN_span = 0x89 ;
unsigned char sensor_typeN_amp_gain = 0x29 ;
Where 'N' is the number of the sensor type. A structure is a neat way of con
densing this type is related and repeating data.
In fact the information needed to describe a sensor can be reduced to a gene
ralised:
unsigned char gain ;
unsigned char offset ;
unsigned char temp_coeff ;
unsigned char span ;
unsigned char amp_gain ;
The concept of a structure is based on this idea of generalised "template" f
or related data. In this case, a structure template (or "component list") de
scribing any of the manufacturer's sensors would be declared:
struct sensor_desc {unsigned char gain ;
unsigned char offset ;
unsigned char temp_coeff ;
unsigned char span ;
unsigned char amp_gain ; } ;
This does not physically do anything to memory. At this stage it merely crea
tes a template which can now be used to put real data into memory.
This is achieved by:
struct sensor_desc sensor_database ;
This reads as "use the template sensor_desc to layout an area of memory name
d sensor_database, reflecting the mix of data types stated in the template".
Thus a group of 5 unsigned chars will be created in the form of a structure
.
The individual elements of the structure can now be accessed as:
sensor_database.gain = 0x30 ;
sensor_database.offset = 0x50 ;
sensor_database.temp_coeff = 0x60 ;
sensor_database.span = 0xC4 ;
sensor_database.amp_gain = 0x21 ;
6.4.2 Arrays Of Structures
In the example though, information on many sensors is required and, as with
individual chars and ints, it is possible to declare an array of structures.
This allows many similar groups of data to have different sets of values.
struct sensor_desc sensor_database[4] ;
This creates four identical structures in memory, each with an internal layo
ut determined by the structure template. Accessing this array is performed s
imply by appending an array index to the structure name:
/*Operate On Elements In First Structure Describing */
/*Sensor 0 */
sensor_database[0].gain = 0x30 ;
sensor_database[0].offset = 0x50 ; sensor_database[0].temp_coeff = 0x60 ; se
nsor_database[0].span = 0xC4 ;
sensor_database[0].amp_gain = 0x21 ;
/* Operate On Elements In First Structure Describing */
/*Sensor 1 */
sensor_database[1].gain = 0x32 ;
sensor_database[1].offset = 0x56 ;
sensor_database[1].temp_coeff = 0x56 ;
sensor_database[1].span = 0xC5 ;
sensor_database[1].amp_gain = 0x28 ;
and so on...
6.4.3 Initialised Structures
As with arrays, a structure can be initialised at declaration time
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -