Perpetually In Beta.

Archive for the ‘C’ tag

Windows Serial Programming - Tips and Tricks

without comments

The last couple days I have been working on Serial Communications (RS-422 and RS-485) between a Windows PC and an Embedded FPGA Processor. From start to finish, synchronizing the data transfer between the two systems only took about half a day. When I first started using serial communications a few years ago, it took me a week to establish a good working link. Between then and now, I've noticed a few tips and tricks along the way which has helped get communication between two devices up-to-speed more quickly.

Not a Perpetual Motion Machine

What makes serial a bit difficult to work with is that it is (in general) not plug-and-play. The code used to communicate to one type of board, won't work straight-out-of-the-box with another type of board. Like snowflakes, no two serial-com links are alike. There is no perpetual motion machine. Each has individual timing constraints and quirks. Sometimes you can read about these timing constraints in the spec, but most times a good link is established only after a big battle of trial-and-error. This also makes it hard to do serial comm research on the Internet. Copying and pasting serial code from a website you find on google rarely works. It can help you get the gist of what is going on, but unfortunately, prepare to put in some legwork.

Know the baud-rate, parity, stop-bit, and byte-size

This is essential. Before even getting started make sure you know the baud-rate, parity, stop-bit, and byte-size information of the device you are trying to communicate to. If you have this information wrong, you are pretty much dead in the water from the get-go. Nothing will work. Check and double check this information. I don't know how many times I've ported serial code from one project to the other, ran it, then hit my head against a wall for a few hours before realizing that, duh, the last FPGA that I was communicating to was in 19200 the new FPGA is in 9600. These variables should be the first to be modified when programming serial in the windows environment.

DCB properties;
 
GetCommState(hCom, &properties);
properties.BaudRate = CBR_19200;
properties.Parity = ODDPARITY;
properties.StopBits = ONESTOPBIT;
properties.ByteSize = 8;
SetCommState(hCom, &properties);

Auto-Toggle or Manual? Know your serial card.

Oh Auto-Toggle. How we love thee. Let me count the ways. Auto-Toggle is a function that is built into certain serial cards that automatically toggles the transmit lines when bytes are sent then immediately toggles back to the recieve line to await bytes from the device to whom you were talking. The problem is that most serial cards don't have Auto-Toggle and sometimes even when they have it doesn't operate properly. Make sure you know to which camp you belong. Know your serial card. If your card has auto-toggle, verify that it works using an oscilloscope (we'll talk about that later). If it doesn't, know that you have to manually toggle the transmit and receive lines.

Enabling RTS_CONTROL toggles the transmit line and allows the user to transmit data over serial. When RTS_CONTROL has been enabled, the user cannot recieve any data over serial.

GetCommState(m_hCom, &properties);
properties.fRtsControl = RTS_CONTROL_ENABLE;
SetCommState(m_hCom, &properties);

Diabling RTS_Control toggles the recieve line and allows the user to recieve data over serial. When RTS_CONTROL has been disabled, the user cannot send any data over serial.

GetCommState(m_hCom, &properties);
properties.fRtsControl = RTS_CONTROL_DISABLE;
SetCommState(m_hCom, &properties);

If you are working with RS-485, and auto-toggle isn't an option, manually controlling the transmit/recieve lines is very necessary as RS-485 is half-duplex (which means that the transmit/receive lines are shared). If working with RS-422, which is full-duplex, transmit and receive are not being shared (both have separate lines) and toggling the transmit/receive lines should not be necessary. However, just this week I was working with a RS-422 and communication would not work unless I still toggled transmit/receive - go figure.

Give the WriteFile command time to breathe

After transmitting bytes using the WriteFile command, proper time must be given after executing WriteFile and before RTS_CONTROL is disabled. Unfortunately, the WriteFile command doesn't (properly) verify that all bytes are written to the transmit line. If RTS_CONTROL is disabled too quickly after WriteFile, bytes can actually be chopped off the end of your message. If you are losing bytes, simply insert something I like to call a 'for loop to nowhere' after the WriteFile command. It just wastes a little time before you disable RTS_CONTROL and makes sure all bytes are transmitted.

DCB properties;
 
GetCommState(m_hCom, &properties);
properties.fRtsControl = RTS_CONTROL_ENABLE;
SetCommState(m_hCom, &properties);
 
BOOL ok = ::WriteFile(m_hCom,(void *)buffer,bytes_to_send,&bytesSent,NULL);
 
for(int i = 0; i < 200000; i++); //waste a little time
 
GetCommState(m_hCom, &properties);
properties.fRtsControl = RTS_CONTROL_DISABLE;
SetCommState(m_hCom, &properties);

Use an Oscilloscope! ! !

Before I go any further, I'd like to urge you, no, gently prod you to use what I consider the number one best way to get serial communications up and running: the Oscilloscope. Using the Oscilloscope you can really nail down the timing of your sent and received messages over serial communications. You can measure the time it takes to send and receive one byte. You can measure the time between when the last byte is sent by the WriteFile command and when a response is seen on the receive line. You can actually SEE (I spy with my little eye) that you are disabling the RTS_CONTROL line too early and thus cutting off bytes. The Oscilloscope will save you time and thus head-banging hair-wrencing frustration. I guarantee it. I recommend setting two trigger points if using RS-422. One on the receive and one on the transmit line.

Free Serial Port Monitor

Another tool that I use constantly while working on Serial Programming is the Free Serial Port Monitor. This program allows you to monitor a specific COM port and shows you exactly what you have written and what you have read on that COM port. The tool is perfect when you are just getting started if you just want to know if you are writing anything at all. Sometimes, if you aren't using an Oscilloscope, it is difficult to tell if any bytes are being written correctly. Also, this tool shows you exactly how many bytes are being read and what bytes are being read with the ReadFile command. This helps you debug your serial read calls because you can see if you are chopping your message in half with the ReadFile command and adjust accordingly.

Reading when you know what to expect

If you are working on a command and response based serial system, one in which you issue and command and expect back a response of a certain length — reading on serial is a cinch. You just have to know three little bits (or should I say bytes?) of information. The first is the maximum time interval between the characters you are receiving in milliseconds. This can be found in your spec (if you are given a spec) or can be found with an Oscilloscope. The second is the number of bytes you expect back. The third is the time it takes to send one character (this can be found with a simple math equation). I'll tell you how to use this information below after I drop a big lump of code on you.

DWORD Serial_Controller_Class::Receive_Message(unsigned char * Msg_Ptr,
int ExpectedNumberOfBytes, int MaxTimeBetweenChar, int MaxTimeToSendSingleChar) { 
 
    DWORD bytesRead = 0;
 
   COMMTIMEOUTS comTimeOut;
   comTimeOut.ReadIntervalTimeout = MaxTimeBetweenChar;
   comTimeOut.ReadTotalTimeoutMultiplier = ExpectedNumberOfBytes;
   comTimeOut.ReadTotalTimeoutConstant = MaxTimeToSendSingleChar;
   SetCommTimeouts(m_hCom,&comTimeOut); 
 
   BOOL ok = ::ReadFile(m_hCom,Msg_Ptr, ExpectedNumberOfBytes,&bytesRead,NULL); 
 
  FlushFileBuffers(m_hCom);
  return bytesRead;
}

The Receive Message method above has four elements passed to it. The first, MsgPtr, is used to hold the message received. ExpectedNumberOfBytes contains the number of bytes you expect to be returned based on the message you sent out on serial. The variables MaxTimeBetweenChar and MaxTimeToSendSingleChar are, in my opinion, pretty self explanatory. This passed data then can be used to set things called "Comm Timeouts." Comm Timeouts are used to control the amount of time that ReadFile will sit and wait for a message to be recieved. This is called "blocking" in the biz which means that all other processes are esentially "blocked" until ReadFile has finished executing. This is why setting CommTimeouts correctly are extreamly important. We don't want to "block" for longer then necessary. Each Comm Timeout is explained in detail below.

DWORD ReadIntervalTimeout - sets max period of time (in milliseconds) allowed between two sequential characters being read from the line of commutation. During the 'read' operation the time period countdown takes its start when the first character is received. When the interval between two sequential characters exceeds the given value the 'read' operation finishes and all the data accumulated in the buffer are transmitted to the programme. Zero value of this member indicates that this timeout isn't used.

DWORD ReadTotalTimeoutMultiplier- sets the multiplier (in milliseconds) used to calculate general timeout of the 'read' operation. In every case this value is multiplied by the quantity of the characters requested for reading.

DWORD ReadTotalTimeoutConstant - sets a constant (in milliseconds) used to calculate the general timeout of the operation. In case of every 'read' operation this value is added to the result of multiplying ReadTotalTimeoutMultiplier by the quantity of the characters requested for the reading. ReadTotalTimeoutMultiplier and ReadTotalTimeoutConstant mean that general timeout for 'read' operation isn't used.

Here's how to calculate MaxTimeToSendSingleCharacter.  If we transfer one character at 19200 baud, 8 bits per byte, plus parity complement and one stop bit, then there will be 11 bits total per character.

MaxTimeToSendSingleCharacter =  11x(1/19200) = .057 milliseconds (which can be rounded up to 1 milliscond).

Imagine that we read 100 characters at a rate of 19200 bps. If 8 bits per character, parity complement and one stop bit are used, then there will be be 11 bits (including the start bit) per character in the physical line. So 100 characters at a rate 19200 bit/s will be received as

100×11x(1/19200)=0.0572916 sec

or approximately 57.2 milliseconds. If there is no interval between receiving multiple characters.   If the interval between the characters is about a half time of one character transmission, i.e. 0.25 milliseconds, the reception time is calculated as

100×11x(1/19200)+(99×0.00025)=0.00820416 sec

or give or take 82 milliseconds. If the reading process has amounted to longer then 82 milliseconds, we can suppose there's been an external device error and stop the 'read' operation to anymore program blockage.  Phew.

"No More Math!  No More Math!"  I hear you chant.  Fear not, we are done.

Reading when you have no idea

When you have no idea how many bytes to expect back, the best thing to do is to follow the steps above with one exception.  Hopefully you know the maximum number of bytes that can be sent at any given time.  If you know that, just use that number for ExpectedNumberOfBytes.

Thats it!

These are my tricks.  OK, maybe they are really tricks.  Maybe they are just a better explanation of the dry-and-boring RS-232 specification that you've been starting at and scratching your head over for the past week or so.  Regardless, I hope some of these tidbits helped.

Written by codingwithoutcomments

September 10th, 2008 at 10:43 pm

Programming Paradigms - Lecture 5 - CS107 Stanford University

with 3 comments

Linear Search with Generics

void * lserach(void * key, void * base, int n, int elemsize, int(*cmpfn)(void*, void*) )
{
 
  for(int i = 0; i < n; i++) {
      void * elemAddr = (char *) base + i * elemSize;
      if( cmpfn(key, elemAddr) == 0 )
          return elemAddr;
  }
 
  return NULL;
 
}

The linear serach takes five parameters.  Key is a pointer to a searched for item, base is a pointer to the base of the array, n is the number of elements in the array, elemsize is the size of an element in the array, and cmpfn is a function pointer to a compare function.  We loop through the array one element at a time.  The cmpfn returns zero if the elements are equal, and lsearch returns a pointer to that element.  If nothing is found, NULL is returned.

Implementing lsearch with ints

int array {} = {4, 2, 3, 7, 11, 7, 6};
int size = 6;
int number = 7;
 
int * found = lserach(&number, array, 6, sizeof(int), intcmp);
     if(found == NULL) :(
     else                     :)

When calling lsearch above, the address of 'array' doesn't have to be passed because when one sends in 'array,' it implicitly passes in the address of the zero(th) element.  The 'intcmp' function that is passed into lsearch needs to be coded for the above to work.

int IntCmp(void * elem1, void * elem2) {
   int * ip1 = elem1;
   int * ip2 = elem2;
 
  return *ip1 - *ip2;
}

The compare function returns a negative value if ip1 is less than ip2, zero if the elements are equal, or a positive value if ip1 is greater than ip2.  Because it is known that the elemnts we wish to search for are of type int, we can make the compare function passed to lserach an specific type of compare ala int. 

There are definetely better ways to implement generics in other languages.  These languages have learned from the mistakes in C.  C has been in existence for 35 years which is pretty old in computer time.

Implementing lsearch with CStrings

On this example, an array of CStrings is searched.  The array is searched for the string 'EFlat'.  All the character elements are NULL terminated ('\0′).  This example is tricky because we are working with type char star star (char **) or double pointers.

char * notes[] = { "AFlat", "FSharp", "B", "GFlat", "D" };
char * favoriteNote = "EFlat"
 
char ** found = lsearch(&favoriteNote, notes, 5, sizeof(char *), StrCmp);
 
int StrCmp(void * vp1, void * vp2)
{
   char * s1 = * (char **) vp1;
   char * s2 = * (char **) vp2;
 
   return strcmp(s1, s2);
}

Vp1 and vp2 are cast to the types they really are which are double pointers.  After that, the value of vp1 and Vp2 are derefereced to s1 and s2.   Vp1 is of type void * so it can't be deferenced as a void * so it must be cast as what it is which is a char **.  A C standard library function can then be used to compare s1 and s2 which takes two char pointers.

The difference between functions and methods

The difference between functions and methods.  Funtions are independent of Objects.  Methods are functions that exist inside Objects.  Methods have invisible (this) pointers lying around, where functions actually have to have everything passed to it.

Generic Data Structures (Stacks)

We are going to implement a stack using ints first before a generic version will be attempted.

stack.h

typedef struct {
   int * elems;
   int logicallen;
   int alloclenth;
} stack;
 
void StackNew(stack * s);
void StackDispose(stack * s);
void StackPush(stack * s, int value);
void StackPop(stack * s);
 
stack s;

This allocates a block of memory for stack, but doesn't actually initialize the data.

StackNew(&s);

A call to StackNew receives the address of the previously allocated stack s.  StackNew initializes the actual elements in stack s.  

for(int i = 0; i < 5; i++)
{
    StackPush(&s, i)}

The above call pushes the numbers zero through 4 onto the stack.

StackDispose(&s)

And finally, the stack is disposed of.  Other details need to be implemented as well, but those details were not provided in this lecture.  For example, initially the elems pointer only pointed to an array of size 4.  This array, however, gets filled up after the number '3′ is pushed onto the stack.  More memory then has to be allocated to fit the number 4.  

void StackNew(stack * s)
{
 
    s->logicallen = 0;
    s->;alloclen = 4;
    s->elems = malloc(4 * sizeof(int));
 
   assert(s->elems != NULL);
 
}

In C, malloc goes out to the heap, finds a block of memory that is 4 * sizeof(int) wide (or 16 bytes) and returns the address of that block of memory to elems.   The assert function, if evaluated to false, declares an assertion at that call (ends the program) and tells you at what line the program ended.  If malloc isn't able to find the memory on the heap, it will return NULL and the assert will be triggered.  If not caught, a segmentation fault is called, but you don't know where it was called.

Written by codingwithoutcomments

September 9th, 2008 at 9:29 pm

Standford University - Programming Paradigms - Lecture 4

without comments

The following is a brief overview of Lecture 4 of the ‘Programming Paradigms,” a programming class taught at Standford University.  Screenshots and Code are included from the lecture.  The ‘Programming Paradigms’ lectures can be found in iTunes.  The link is on the Open Standford Website.  The following lecture covers implementation of Swap using Generics.

Implementation of Swap using Templates

void swap( void * vp1, void * vp2){
  void temp = *vp1;
  *vp1 = *vp2;
  *vp2 = temp;
}

The above Swap function takes a generic pointer type (void * ).  This means that it points to something that it doesn't have any type information about, so Vp1 and Vp2 are pointing to addresses in memory that it knows nothing about.  Furthermore, there is a problem with the above implementation.  One cannot declare temp or a variable to be of type void.  Also, one isn't allowed to dereference a void * variable because it doesn't know how to go out and embrace an unknown number of bytes.

void swap (void * vp1, void * vp2, int size){
 
  char buffer[size];
  memcpy(buffer, vp1, size);
  memcpy(vp1, vp2, size);
  memcpy(vp1, buffer, size);
 
}

int x = 17, y = 37;
swap(&x, &y, sizeof(int));

The offical way to perform a swap using generics is to expect a third argument to the swap function, size, where size is the explicit size of the bytes being swapped.  A character buffer of a dynamic size is first declared.  Memcpy is like strcpy, except it doesn't stop copying at '\0′  One has to specifically tell it how many bytes to copy.  Although the above example works, true ASCI C doesn't allow you to dynamically allocate an array.

double d = ∏, e = e;
swap(&d, &e, sizeof(double))

The problem with using generics, however, is that the compiler does very little checking.  You can pass in anything you want for a void * and the compiler will not yell at you.  It might not work very nicely, but it will compile.  When you use generics, you are making the compiler do less work for you, but you run more risk when you actually run the program.

int i = 44;
short s = 5;
swap(&i, &s, sizeof(short));

Say you need to swap an int and a short, but you accidentally pass the sizeof(short) to the swap function.  Vp1 and Vp2 just have the address itself, they don't know how big the variables i and s are that they point to.  They only access the first two bytes of each address because it is specifically told to Vp1 and Vp2 to access a short.  Five will be written in the first two bytes of the int.  The first two bytes of i will be written with 5.  So zero will be written to s, 5 and 44 to i.

char * husband = strdup("Fred");
char * wife = strdup("Wilma");
 
swap(&husband, &wife, sizeof(char *));

In the above code, the husband and wife char pointers above are going to be switched.  Husband will point to Wilma and Wife will point to Fred.  We want to exchange the two things are are held by the husband and the wife variables.  If you want to swap char *, that is, the actual pointer boxes, you pass sizeof(char *).  Vp1 and Vp2 point to the pointers husband and wife.  Fred and Wilma actually stay put, the pointers are just switched.  However if the following is passed to swap:

swap(husband, wife, sizeof(char *));

This will still compile and run.  Vp1 and Vp2 this time point to the raw text "Fred\0″ and "Wilma\0."  The swap function now has two address and thinks that it is supposed to swap 4 byte figures (char *'s are four bytes).  So it copies "Wilm" from Wilma and puts it in Fred's place "Wilm\0″ and copies "Fred" and adds it to the first four letters of "Wilma" for "Freda\0″

Implementation of LSearch in Generics

First the non-generic linear search.

int lsearch(int key, int array[], int size){
 for (int0; i < size; i++)   {
 if(array[i] == key)
    return i;
 }
}

The lsearch seraches from the front to back of an array of a certain size looking for a key passed into the function.  The function will return index of the array of where the key was found.  The generic version is best understood as a generic blob of memory.  In order to advance from element zero to element one, the size information will have to be passed into the function.  A comparison function will also have to be passed in so we know how to compare the key to to i(th) element in the array because double =='s can't be used very easily. So the address of the key is passed in, the address of the beginning of the blob of memory, the width of each element in the blog of memory, the number of elements, as well as a comparison function.

void * lsearch (void * key, void * base, int n, int elem_size){
   for(int i = 0; i < n; i++)  {
       void * elemAddress = (char *) base + i * elemSize;
       if(memcmp(key, elemAddress, elemSize) == 0) return elemAddress;
   }
 
  return NULL;
}
 
void * elemAddress = (char *) base + i * elemSize;

This lsearch that results uses the char * casting hack.  Because base is (void *), the compiler can't do pointer arithmetic on a (void *) element, therefore, casting fools it into thinking that its pointing to one-byte characters.   The above function works in a few cases, but doesn't work in cases like struct.  A function pointer to a comparison function must be used in that case as seen below.

void * lsearch (void * key, void * base, int n, int elem_size, int (*cmpfn)(void *, void *))

This function will be discussed in the next class.

Written by codingwithoutcomments

September 2nd, 2008 at 10:09 pm

Standford University - Programming Paradigms - Lecture 3

without comments

The following is a brief overview of Lecture 3 of the 'Programming Paradigms," a programming class taught at Standford University.  Screenshots and Code are included from the lecture.  The 'Programming Paradigms' lectures can be found in iTunes.  The link is on the Open Standford Website.  The following lecture covers Casting Types, Structs, Arrays, Arrays in Structs, and the beginning of programming generics in C.

Casting Types in C++

The first two examples deal with casting in C.

double d = 3.1416
char ch = * (char *) &d;
cout << ch << endl;

A double of 3.14 or pi is declared then cast as a character ch. &d is a pointer to the first byte address in d.  (char *) says, "Oh, it's not a double it's a character."  The final * "de-references" that value which means it copies the value to ch not the address.

short s = 45;
double d = *(double *) &s

A two byte figure that has 4 and 5 in it.  &s points to the first byte address of s.  (double *) is a brute force interpretation of &s.  The code is essentially saying, "Oh, that never pointed to a short.  It always pointed to an eight byte double."  The first two bytes of the double "d" will be 4 and 5 and the rest will be blank.

A student asks a question about Endian(ness). When a "1″ is represented as two byte short in Big Endian, the right most bit in the right most byte is a one.  Most Linux Machines store bytes in the reverse order or "Little Endian."  When a "1″ is represented as a two byte short in Little Endian, the right most bit is set in the left most byte.

Structs in C++

struct fraction {
int num;
int denum;
};
 
fraction pi;
pi.num = 22//sets num to 22
pi.denum = 7// sets denum to 7

When a stuct is declared with two four byte ints, they are stacked like dominos in memory.  The num variable is on the bottom.  The denum is stacked on top of the num. Setting the two fields is easy enough.  One sets the num and denum fields with the (.) dot operator.

Now things become a little crazier.

((fraction *) &(pi.denum))->num = 12;

Let's break it down.

&(pi.denum)

points to the top address of the struct — the denum address.

(fraction *) &(pi.denum)

Casts the top address as a struct with two int variables.  Pi.denum becomes the bottom address (or num address).

((fraction *) &(pi.denum))->num = 12;

Sets the previous pi.denum, which is now pi.num, to "12."
Tricky Tricky.

((fraction *) &(pi.denum))->denum = 33.

Sets the space above the original pi.denum to 33 even though the address isn't owned by the struct.  There is no legal way to get to this and print it out, however, the memory is set anyway.

Arrays in C++

int array[10];

This call allocates 40 bytes of memory.  4 bytes for each of the 10 blocks in the array.

array[0] = 44;

Puts 44 in the first four bytes of memory, or the zero place in the array.

array[9] = 100;

Puts 100 in the last four bytes of memory, or the 9th place of the array.

Therom:  array ≡ &array[0]
The array is completely synonymous with a pointer to the first address of the zero(th) entry.  When one passes an array to a function, you aren't passing the array itself (and all the memory).  You are passing the address to the zero(th) entry and from there you can jump to whatever place you want in the array.

array[10] = 1;

If for some reason you mess up and try to assign the number one to the 11th entry in the array.  The computer will try to place a one in that location even though that memory isn't preallocated.

array[-4] = 77;

C++ even tolerates brute force.  You can assign a number to a negative offset in memory.  There is no boundary checking in C and C++.

Theorem:  array + k ≡ &array[k]

This is pointer arithmetic.  K is automatically scaled based on the type system.  In the case above, k is an int, therefore k is scaled in 4 byte increments.

Theorem:           *array ≡ array[0]

(array + k) ≡ array[k]

When array is de-referenced, the above is equal to the value stored in the array — not the address.

int arr[5];
arr[3] = 128;
((short *) arr[6] = 2;
cout<< arr[3] << endl;
640

Each int is compsed of four bytes.  Each short is composed of two bytes.  The second and third statement above actually set the same bytes in memory.  arr[3] sets the memory 12 bytes over (3 * 4 (size of int) = 12).  ((short *)arr[6]) (6 * 2 (size of short) = 12) also sets the memory six bytes over.  Although, 128 fits in the 15th and 16th byte, while 2 is placed in the 13th byte.  When arr[3] is printed to the screen, the value printed out is 128 + 512 = 640 ( 2^9 + 2^7 = 640).

Arrays in Structs

struct student {
char * name;
char suid[8];
int numUnits;
};
 
student pupils[4];
pupils[0].numUnits = 21;

Sets numUnits in the first instance of pupil to 21

pupils[2].name = strdup("Adam");

Creates memory on the heap with "Adam�"  name points to this memory on the heap.

pupils[3].name       = pupils[0].suid + 6;

The name character pointer in the fourth instance of pupil points at the address starting at the sixth character in the first instance of the struct pupil

strcpy(pupils[1].suid, "40415xx");

Copies "40415xx" to the suid character array starting at the first character in the second instance of pupil.

stcpy(pupils[3].name, "123456");

Since pupil[3].name points at the address starting at the sixth character in the first instance of the struct pupil, starting from that location "123456″ is copied.  This writes over the 21 which was previously assigned to pupils[0].numUnits.

pupils[7].suid[11] = 'A';

Even though an eighth pupil isn't properly allocated, the computer still tries to write 'A' to the 11th character into the suid character array.  Will it succeed?  Maybe.

How to Write Generics In C

int x = 7;
int y = 117;
swap(&x, &y)
 
void swap(int *ap, int *bp)
{
 
int temp = *ap;
*ap = *bp;
*bp = temp;
 
}

Two addresses are passed to the swap function.  Ap and bp point to x and y respectively.  The first line in the swap funtion copies 7 (the value of what ap points to (x)) to temp — so temp is set to 7.  The second line, copies the 117 (the value of what bp points to (y)) to whatever ap points to (x) - so seven is replaced by 117.  Temp, whose value is seven, is then copied to y or what bp points to.

Written by codingwithoutcomments

August 31st, 2008 at 4:30 pm