EEE502_L3(more_C)

advertisement
EEE502 Embedded Systems
• Lecture 3: recap and more on variables, pointers and strings (omit the
pink background slides)
•Teaching resources on on www.eej.ulst.ac.uk
•My office 5B18, telephone 028 90 366364
•My email IJ.McCrum@ulster.ac.uk
++ -- + += - -= / /= * *= % %= | |= & &= ^ ^= |= | << <<= >> >>=
.,;?:
()[]{}
/* */ //
\“‘ !
== != < <= > >= ~ &&
You should know what each of the above symbols means in C
Note several have multiple uses ( ) * . : and I have left out ->
http://www.eej.ulst.ac.uk/~ian/modules/EEE502
1
RECAP (red text is proper C, purple needs replaced with proper C)
• White space – use tabs, spaces and newlines to indent code, C is case sensitive.
• Commenting – put in 5 places, title and description, declarations, in front of
functions and any lines worthy of extra explanation. two types, // and /* … */
• Variables – Declare before use, standard types int, char, float and double
• also unsigned chars or ints, signed chars or ints, short ints and long ints).
• Examples of constants are 65, 65.0, 0x41, ‘a’, “ok” Note ‘a’ is a single char and
“ok” is an array of chars – stored with an extra byte of 0x00 at its end.
• Functions – declare before use, give the types of passed parameters, the return
value and the name. Library functions are predeclared for you in a .h file. You
use #include <stdio.h> to access these. A function body (its definition) does not
use a semicolon.
http://www.eej.ulst.ac.uk/~ian/modules/EEE502
2
RECAP (red text is proper C, purple needs replaced with proper C)
• Conditional tests –
if(test is true){do these statements;}else{do these statements;} Tests include
the equality test ==, the not equal to test != and less and greater tests <, <=,
>, >=. Avoid a semicolon after the trailing ) in the test. Non-zero values are
true.
• Conditional expression – c = (do a test) ? Assign this if true : assign this if
false;
• Loops – do{these statements;}while(test is true); note always runs once,
and note semicolon!
• while(test is true){do these statements;} note NO semicolon, statements
gets skipped if false
• for(initialisation;test;increment){do these statements} note no semicolon,
initial statements before entering the statement block, test done at top of
block, increment done at bottom.
http://www.eej.ulst.ac.uk/~ian/modules/EEE502
3
RECAP (red text is proper C, purple needs replaced with proper C)
• You can exit a loop prematurely using break; , you can go back to the
beginning of a loop prematurely with a continue; statement
• Multiple choice – the switch statement is a multi branch if .
• switch(test an int){ case value1: do this; break; case value2: dothis; break;
default: do this;}
• Preprocessor statements are a bit like search and replace in a text editor.
#include <filename> and #include filename insert the file at that point in
your program, the <> form uses the compilers include folder. The
filenames can have any extension, .h and .c are common.
• #define NAME “some text” or #define THING number does a simple
replacement of the first thing with the second. (there are more advanced
forms of this). You can do conditional compilation using #ifdef THING
…compile this #endif and there are more complicated versions of these,
we shouldn’t need them.
http://www.eej.ulst.ac.uk/~ian/modules/EEE502
4
Further thoughts on variables and assignment
When a compiler sees a variable declaration it puts an entry in a symbol table to record how
many bytes will be needed to store its value, its name and where it can be accessed. It also
stores data about the item – its storage class.
There are 4 classes of variable; auto, static and extern and register.
Auto storage is local, within a function. We normally do not add the auto keyword it is the
default.
A variable declared inside a function is only accessible within that function.
A variable declared outside a function is accessible within the file it is declared in and
inside any functions in that file, unless you have a local variable with the same name.
We can describe the variable declared outside the function as a “global” variable, there is no
global keyword in C but you can recognise it from its context.
Global variables are useful but risky; you can avoid passing values or variables to a function if
you let the function manipulate global variables instead. Harder to debug, ok with small
programs… used in embedded C a fair bit (limited stack memory)
Embedded compilers also allow a volatile keyword;
http://www.eej.ulst.ac.uk/~ian/modules/EEE502
5
Further thoughts on variables and assignment
We will avoid writing multi file C programs but you might note there is a keyword extern
used to give a variable visibility across multiple files and the keyword static is (mis)used with
global variables to disallow extern use in other files (ignore this paragraph if you like!)
Register is a hint to the compiler to use faster memory to keep the variable value – it is not
needed now as most compilers figure out how to optimise and make code run faster.
Static auto variables are placed in an area of memory where their values will be kept.
The authors of C used the static keyword twice, with different meanings depending on
context.
What you need to know;
Variables declared inside functions are only accessible within that function; they
are local
Variables declared outside functions are accessible anyway in the file, from that
point forward – they are global to the file
http://www.eej.ulst.ac.uk/~ian/modules/EEE502
6
lvalue and rvalue of a variable
In an assignment x=3; X is an lvalue expression and 3 is an rvalue expression. These do not
stand for left value and right value although most of the time that is the case.
I like to thing of lvalue as standing for “LOCATION value” – the address of X is what the
compiler used in the expression above since it placed 3 in the contents of the storage with
the address of x.
There are 4 types of Lvalue that cannot be on the left side of an assignment. An array type,
An incomplete type, A const-qualified type,A structure or union type with one of its members
qualified as a const type
The term rvalue refers to a data value that is stored at some address in memory. An rvalue is
an expression that cannot have a address assigned to it. Both a literal constant and a variable
can serve as an rvalue. When an lvalue appears in a context that requires an rvalue, the lvalue
is implicitly converted to an rvalue. The reverse, however, is not true: an rvalue cannot be
converted to an lvalue. Rvalues always have complete types or the void type.
What is sometimes called 'rvalue' is the 'value of an expression’
http://www.eej.ulst.ac.uk/~ian/modules/EEE502
7
Pointers
Every variable has an address and a value, the compiler knows the size of the region
of memory allocated to the variable. (in fact you can use the sizeof operator and it
will tell you in bytes the size of a variables storage e.g printf(“%d”, sizeof(x));
You can access the address of a normal variable using the & operator. (the & is also
used in 2 other places in C to mean bitwise AND and logical AND)
You can declare a variable to hold an address; in C this is called a pointer variable
and the compiler will also know the size of the object that lives at the address
pointed at. (the language B did not have this facility!)
Thus you can declare a variable that will hold the address of an int or you could
declare a variable that can hold the address of a char or a float. (it matters)
To declare a pointer you use the * operator (this is also used in C to mean two other
things, multiplication obviously and also to access a memory locations contents)
You need pointers because they are useful with arrays and strings, as well as just to
access and manipulate memory – buffers of data in embedded systems for example
http://www.eej.ulst.ac.uk/~ian/modules/EEE502
8
Pointers
As well as declaring pointers with the * symbol it can be a useful convention to name
pointers sensibly – prefix with ptr or p_
int *ptr1; // note this will hold the address of an int variable - a region of 2 bytes
char *ptr2; // holds the address of a char variable – a single byte region of memory
float *ptr3;// points at a region of 4 bytes in length
All these pointers must themselves live in memory. On a machine with 64k of memory
the pointers will need to be 16 bits in size, 2 bytes. In the microchip world pointers
can be 16, 24 or 32 bits in size though it depends on the compiler as well. Pointers are
not very well supported on some microcontrollers because of architectural limitations
in the hardware or instruction set. (read the fine print in the compiler manual!)
It is possible to declare a pointer to a void type as long as you convert it to a correct
type when you use it. To do this you use what is called a void pointer
void *ptr4; // there some functions that return void pointers; you “cast” these to the
correct type later (e.g the malloc function returns a pointer to a big area of memory, if
you want to store ints in it you cast the void pointer to an int pointer)
You can add and subtract from pointers, you cannot multiply or divide. Adding 1 to a
pointer will change its value by 1,2,4 or whatever the size of the pointed at object is.
http://www.eej.ulst.ac.uk/~ian/modules/EEE502
9
Simple pointer usage
int y , x=42;
int *ptrx; // initially ptrx holds random data, better to initialise it a null address.
…
ptrx=NULL; // predeclared in stdio.h, NULL is probably ((void *)0
…
ptrx = &x;// we must initialise a pointers contents, with an address – e.g 1234
y = *ptrx; // in an assignment, * means the “contents of what is pointed at”
// also called the indirection operator or the unary * operator
// so y now has the value 42.
printf(“x= %d and y = %d, x lives at address %p\n”, x , y, ptrx);
…
Why do we need these? - to use strings and memory.
Anywhere we use arrays we can convert the code to use pointers. An array name without
the brackets just holds the address of the start of the array, an array name is just a
pointer (technically a const pointer – it will always hold the array address)
http://www.eej.ulst.ac.uk/~ian/modules/EEE502
10
Pointers and arrays
Another way of accessing contiguous areas of memory is with pointers
int nam[6]={2011, 2008, 2013, 2010, 2015, -9999}; // 12 bytes of memory note terminator
int *p1;
//Bye the way an array name without an index returns the start address of the array.
p1=nam; // initialises p1 to the address of the array, nearly same as p1=&nam[0];
…
searchyear = 2010;
flag = 0; // used to indicate success
do{
if(searchyear==*p1){ flag=1;break;} // remember break exits the do loop
p1++; // move to next address, since we are using an int pointer we add 2 to the address
}while(*p != -9999);
// if flag is set then we have found the target year.
http://www.eej.ulst.ac.uk/~ian/modules/EEE502
11
Avoiding arrays
#define BUFSIZE 256
int *p; // we want to point at space for 256 ints…
…
p=(int *)malloc(2 * 256);// better would be ( sizeof(int) * BUFSIZE ) – more portable
…
for(i=0;i<BUFSIZE;i++){
*(p++)=get_temperature_reading(); // assuming someone has a written the function
}
The reason why pointers are better than arrays is that the malloc function can take a
variable – this allows the array to be resized at run time – you can run the program on
several different systems and they can allocate bigger arrays if they have more
memory; this is know as dynamic memory allocation. To use it requires you to test the
return value of malloc, (if it fails, it returns a bad/non existent address (NULL))
We won’t need malloc on our small embedded system.
Note *(p++) means get the thing that p points at, and after that increment the
address (the pointer contents). There is a difference between *(p++) and (*p)++ If we
use the unbracketed version *p++ we need to know the order of precedence. Turns
out that the unary * and the unary increment operator have equal precedence and
the rule is to associate the operators from right to left so the ++ gets done first. Always
Use brackets To avoid confusion!
http://www.eej.ulst.ac.uk/~ian/modules/EEE502
12
Arrays and pointers
We can interchange array and pointer declarations; sometimes we need to be sure things
are initialised. So the functions below operate on arrays of chars, we use pointers.
#include <stdio.h>
int ourstrlen (char*);
// returns the number up to but not including the ‘\0’
char* ourstrcpy(char* char*); // copies the second parameter into the first including ‘\0’,
// returns a pointer to the first string. There is
// no warning if you copy a big string into a little string!
char* ourstrcat (char* char*); // similar to strcpy except the 2nd string overwrites the 1st
// strings terminating zero and you get a bigger string.
int ourstrcmp(char* char*);
// return non zero if strings NOT the same, -ve if string 1 less
If you #include <string.h> in your code you can get access to these functions, it is useful to
write your own versions!
http://www.eej.ulst.ac.uk/~ian/modules/EEE502
13
Strings functions using pointers
// strlen function
int ourstrlen(char * s){
int i=0;
while(*s !=‘\0’){
i++; // bump counter
s++; // bump pointer
}
return i;
}
// strlen function
// using arrays
int ourstrlen(char [] s){
int i=0;
while(s[i] != ‘\0’){
i++; // bump counter
return (i);
}
// strcpy function
char *int ourstrcpy(char* d, char* s){
char *temp=d;
while(*sp != '\0') {*d = *s;
d++;
s++
}
*d = '\0';
return temp;
}
// strcmp function – return non-zero if diff.
int ourstrcmp(char* d, char* s){
while(TRUE){
if(*s!=*d) return (*d-*s);
if(*d == ‘\0’ || *s == ‘\0’ ) return 0;
d++;
s++;
}
}
http://www.eej.ulst.ac.uk/~ian/modules/EEE502
14
Variables type qualifiers; const (also volatile)
int i;
const int ci = 1;
//
// ci is a constant I, you need to initialise it to be useful
int *pi;
const int *pci
//
// a pointer to a const int. You can alter pci to point to a
// different const int.
int *const cpi = &I; // A constant pointer, the address must be initialised and
// can’t be changed.
const int *const cpci = *ci;
// phew…a constant pointer to a constant int.
A non standard feature in some embedded compilers is the near and far keywords.
These can be used with pointers as well as variables and you get similar syntax as that
above.
http://www.eej.ulst.ac.uk/~ian/modules/EEE502
15
Tutorials/programming exercises
Q1: experiment with the following functions; gets, strcpy and strcmp. E,g read in a
line of text and see if it is your surname.
#include <stdio.h>
#include <string.h>
#define TRUE 1
void main(void){
Char line[80],name[80],ch;
puts("input your name");
strcpy(name,"Ian McCrum"); // one way to initialise a string
do{
gets(line); // gets up to the terminating linefeed and adds a '\0'
ch=strcmp(line,name);// return non-zero if different
if(ch==0)break; // exit the do loop
printf("\nNope, you entered %d chars as %s try again\n", strlen(line),line);
}while(TRUE);
return 0;
}
http://www.eej.ulst.ac.uk/~ian/modules/EEE502
16
Tutorials/programming exercises
Q2: Write a function called getline that uses the getch () function. It should read the
keyboard until a newline is hit. It should store the entered keystrokes, replacing the
newline with a ‘\0’ (i.e your version of gets() );
Q3: Use the gets function found in the stdio.h to read in a string of text. Print out the
number of words. Call this function wordcount
Q4: A line of text is input, your program should find out if it contains the text EXIT in
any combination of upper or lower case letters, at any starting position in the
inputted string (i.e a typical input might be “£$%^£$%^SDSDFSDFeXiT34343dfgfgf”
Q5: A line of test is actually 10 numbers separated by commas. Write a program that
fills an array of integers from the inputted line. Hint, get a substring then use the
atoi() function.
Q6: Use Sprintf to fill a string with 10 numbers separated by commas
Q7: Print the biggest, smallest values of the array and its average (as a float).
http://www.eej.ulst.ac.uk/~ian/modules/EEE502
17
Summary
You should be able to write C programs in a console on a PC that can process numbers,
characters and strings
You should be familiar with printf, sprintf puts, gets, atoi, getchar, various string functions.
You should be able to work with arrays
You should know what the following is used for, and where context changes use. (e.g * )
++ -- + += - -= / /= * *= % %= | |= & &= ^ ^= = == != < <= > >= << <<= >> >>= ~ && || . , ; ? : ( ) [ ] { } /* */ // \ “ ‘ !
You have not covered, files – data stored on disks (fopen, fgets, fputs, fprintf, fclose or open,
write, read close
You have not covered structs (a collection of objects of different types) and their -> operator.
You have not covered more complex pointers, arrays of pointers, pointers to arrays, multidimensional arrays. How to pick up command line arguments, better console programs,
graphics outputs or windows programming.
The ebooks on the website are sufficient for you to learn these if you so wish
After writing/reading some code the “aide memoire” on the website should make sense
http://www.eej.ulst.ac.uk/~ian/modules/EEE502
18
C Syntax Aide Memoire (cog sheet!)
White space doesn’t matter, case does – most C is lower case.
Commenting can be /* a block with start and stop comment symbols */ or a single line // text…<cr>
Names must begin with a letter or underscore, but don’t use underscores, the libraries use them.
Names should not contain funny symbols other than underscores. Use sensible names!
Use #define for symbols used throughout your code, you need only edit them in one place
Arithmetic uses +, -, *, / also % for remainders and &, | and ^ for AND, OR and XORs of the bits.
++ and -- increment and decrement. It is so common to write x=x+c; you can write x+=c; //or -,*,/ etc
You can shift left and right using << and >>, you can do a one’s complement with ~ but be careful!
Types are char, int, float. You can declare a variable as a const type, it can’t change.
You can have signed and unsigned ints and chars, short or long ints and floats can be doubles.
Variables need declared before use, inside functions they’re local, outside they’re global
Casting forces the complier to convert a variable’s type in an expression, y=(float)x /z;//e.g x was int
Functions need declared before use, give their return type and the types in their parameter list.
Simple variables in a parameter list only have their value, a number, passed to the function.
Declare functions before use, use them in main for example like y=sqrt(x); and list their body after main (or
before if you like, but adopt a consistent style). Give no semicolon when defining body.
http://www.eej.ulst.ac.uk/~ian/modules/EEE502
19
C Syntax Aide Memoire (cog sheet!)
Conditional tests. Use == as an equality test, also !=,for not equal, <, less than etc… <=, >,>=.
You can OR,AND and NOT tests with ||,&& and ! Use brackets! E.g if( (x == 9) && (y != 5) ){…
if(test is true){do these statements;} // the simple if statement, if there is one statement omit the { }
if(test is true){do these statements;}else{do these statements;} // note no semicolon after )
Conditional expression is rarely used
c = (do a test) ? Assign this if true : assign this if false;
switch(test an int){ case value1: do this; break; case value2: dothis; break; default: do this;}
Loops; there are 3 of these, note carefully while is used twice, with or without a semicolon
do{these statements;}while(test is true); // there IS a semicolon after the while conditional test
while(test is true){do these statements;} // NO semicolon after while conditional test
for(initialisation;test;increment){do these statements;} // test done at top, increment at bottom
You can exit a loop prematurely using break; , or go back to the beginning of a loop with continue;
http://www.eej.ulst.ac.uk/~ian/modules/EEE502
20
C Syntax Aide Memoire (cog sheet!)
Loops; there are 3 of these, note carefully while is used twice, with or without a semicolon
do{these statements;}while(test is true); // there IS a semicolon after the while conditional test
while(test is true){do these statements;} // NO semicolon after while conditional test
for(initialisation;test;increment){do these statements;} // test done at top, increment at bottom
You can exit a loop prematurely using break; , or go back to the beginning of a loop with continue;
Arrays use square brackets, you can give its size as a number or string, or list of items
int x[4]; // i.e x[0] to x[3] note 4 elements it is a mistake to refer otherwise use x[4] in your code
char s[ ]=”OK”; // note s[0]=’O’, s[1]=’K’ and s[2]=0x00 (can be written as ‘\0’) strings end in a zero
float f[ ]={42.0, 42.99, 69}; // this declares 3 elements.
Pointers and addresses; get the address of a variable by using &, e.g a = &x; or scanf(“%d”,&x);
Declare a pointer by appending a  to the type (better style to move it near the variable name)
int ptr1; or char ptr2; Note we often use pointers to move inside strings and arrays
To use the contents of what a pointer is pointing at use the indirection operator, also a 
e.g if(ptr2 == 0x00){… //ptr2 is pointing at a memory location or variable containing zero
You can declare a void pointer and later “cast” it to the right type. (see how to use malloc() )
http://www.eej.ulst.ac.uk/~ian/modules/EEE502
21
C Syntax Aide Memoire (cog sheet!)
Useful functions
printf(“format string containing three things”, variable list); //format strings have plain text, special
escape characters (e,g \n, \t etc) and format placeholders can specify how a variable is to be
interpreted and information about display. %04x means print 4 hex digits with leading zeroes if
needed. %c is for a char, %s a string, %d whole decimal numbers and %6.2F is for floating point
numbers, maximum of 6 digits, 2 to right of the decimal point. Also %E for scientific format.
puts(char ); prints a string on the output console screen. scanf(“%d”,&x); reads keys for an
int<cr>
char  gets(char ); // gets a string from the keyboard. int atoi(char ); // turns string into number
int kbhit(); returns true or false if a key has been hit, then use int getch(); to get a single char from
the keyboard. You can replace some if the char with char[] but most C programmers use 
String functions use #include <string.h> then you get strlen, strcmp, strcpy, strstr, sprint see libc.pdf
malloc allocates memory, e.g to get somewhere for strings or buffers to live, if they are not
declared. E.g p = (int *)malloc (sizeof(int) * BUFSIZE); // if p is an int pointer… note use of sizeof()
++ -- + += - -= / /= * *= % %= | |= & &= ^ ^= = || . , ; ? : ( ) [ ] { } /* */ // \ “ ‘ -> !
== != < <= > >= << <<= >> >>= ~ &&
http://www.eej.ulst.ac.uk/~ian/modules/EEE502
22
Download