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