Program Development using Unix C/C++ Programming

Contents

Prerequisites
C++ to C
Errors
Structures
Parameter Passing Mechanisms
System Specific Sizes
Arrays and Pointers
Strings

Prerequisites

It is assumed that you have done C++ in Software Technology 1, and are continuing this in Software Technology 2.

If you have done C, but not C++ then this is ok too.

If you haven't done C or C++, then you need to learn this before you go any further.

C++ to C

Most legal C programs are also legal C++ programs. The converse is not true. C is a standard procedural language, not a mixed O/O + procedural language like C++. So to program in ``pure C'' means that all O/O stuff has to be thrown out.

C and C++ have different standard libraries. The most notable is the I/O libraries. All I/O calls have to be re-written from C++ to C.

However, C functions can be used in C++ programs. So you can use the C Systems API functions in C++ without any problems This approach will be used from now on: C++ programs using C system calls. That means you can continue to write programs in C++, just adding in the extra C calls as needed.

The different nature of C to C++ means that some things are done differently in C to C++. These are:

Some other things that are common to C and C++ have not been used much in ST1 and ST2 (for good reason!). These are

Errors

In C, everything is a function, sometimes returning no value (void functions). Systems calls usually use this value to signal errors

Structures

Structures are like C++ classes but really primitive.

Many system calls take a structure as parameter, or return a structure as value. If the structure will be modified by the call then its address needs to be passed; if it isn't modified, then its address is passed anyway since this is more efficient.

Parameter Passing Mechanisms

Function parameters are all value parameters. The normal way of changing a value is by using the function value
ch = tolower(ch);
There are no C++ reference parameters. To change the value of a parameter you have to do your own ``call by reference'' and pass the address of the variable as the parameter. Example: The sum of two ints as a procedure rather than a function:
void sumof(int x, int y, int *z)
{
    *z = x + y;
}
and use it by
int n;
sumof(1, 2, &n);
#include <iostream.h>

void sumof(int x, int y, int *z)
{
    *z = x + y;
}

int main(int argc, char *argv[])
{ int n;

  sumof(1, 2, &n);
  cout << "1+2 = " << n << '\n';
  exit(0);
}


System Specific Sizes

The size of basic types varies according to the underlying hardware. The 8086 family had 16-bit integers, leading to a maximum integer value of 64k. Many modern processors, such as the 80586 and Sun Sparc have 32-bit integers, leading to a maximum integer size of 4G. Advanced processors such as the DEC Alpha have 64-bit integers.

The size of characters varies according to the character set used. Ascii uses 7-bit characters (always taking up 8-bits, though). ISO 8859 uses the full 8-bits. Unicode uses 16-bits. ISO 10646 uses upto 32-bits.

The sizeof() function will return the size of an object in bytes. This can be used to test the size of data types. Only low-level stuff should need to do this.

#include <iostream.h>

int main(int argc, char *argv[])
{
  cout << "char size (bytes): " <<
        sizeof(char) << endl;
  cout << "short int size: " <<
        sizeof(short int) << endl;
  cout << "int size: " <<
        sizeof(int) << endl;
  cout << "long int size: " << 
        sizeof(long int) << endl;
  cout << "int pointer size: " << 
        sizeof(int *) << endl;
  exit(0);
}


Arrays and Pointers

The name of an array - just by itself - is the address of the base of the array

array

a == &a[0]
*a == a[0]
In general.
a + n == &a[n]
*(a + n) == a[n]

A ``spread'' function to find the distance between largest and smallest elements of an array is

int spread(int b[], int size)
{
  int lo, hi, i;

  if (size == 0) 
    return 0;
  lo = hi = b[0];
  for (i = 0; i < size; i++) {
    if (b[i] > hi)
      hi = b[i];
    if (b[i] < lo)
      lo = b[i];
  }
  return (hi - lo);
}
The ``spread'' function could have been written
int spread(int b[], int size)
{ int lo, hi, i;

  if (size == 0)
    return 0;
  lo = hi = b[0];
  for (i = 0; i < size; i++) {
    if (*(b+i) > hi)
      hi = *(b+i);
    if (*(b+i) < lo)
      lo = *(b+i);
  }
  return (hi - lo);
When an array is passed as a parameter to a function, its address is passed. This is a pointer value. The function, instead of declaring the parameter as an array could instead have declared it as a pointer.

This is a very common practice. Many library functions declare their arguments as pointers, but you have to pass in an array.

Once a parameter is a pointer, you can do pointer manipulations instead of costly array indexing. Here is another ``spread'':

int spread(int *b, int size)
{ int hi, lo, i;

  if (size == 0)
    return 0;
  lo = hi = *b;
  for (i = 0; i < size; i++) {
    if (*b > hi)
      hi = *b;
    if (*b < lo)
      lo = *b;
    b++;
  }
  return (hi - lo);
}
#include <iostream.h>;
#define MAX 100

int spread(int *b, int size);

int
main(int argc, char *argv[])
{
  int n;
  int count = 0;
  int a[MAX];

  while (count < MAX &&
        cin >> n) {
    a[count++] = n;
  }
  cout << "spread was " << 
          spread(a, count) << endl;
  exit(0);
}

int spread(int *b, int size)
{ int hi, lo, i;

  if (size == 0)
    return 0;
  lo = hi = *b;
  for (i = 0; i < size; i++) {
    if (*b > hi)
      hi = *b;
    if (*b < lo)
      lo = *b;
    b++;
  }
  return (hi - lo);
}


This style of coding is very common. Compare these two program fragments
int a[] = {1, 2, 3};

void printit(int *a)
{ int n;

  for (n = 0; n < 3; n++)
  {
    printf("%d\n", *a);
    a++;
  }
}

int main(int arc, char *argv[])
{
  printit(a);
}
versus
int a[] = {1, 2, 3};

int main(int argc, char *argv[])
{
  for (n = 0; n < 3; n++)
  {
    printf("%d\n", *a);
    a++;
  }
}
The second is wrong, the first is good C style.

Strings

A string is an array of chars, terminated by a null character. These are equivalent:
char str[] = "hello";
char str[] = {'h', 'e', 'l',
                    'l', 'o', '\0'};
You can always count on the null char being at the end of a string (except sometimes). If you create a string you should ensure that you null-terminate it. Otherwise, everything breaks. Because strings end in null, a string walk looks like
while (*str != '\0')
   str++;
Here is strlen
int strlen(char *str)
{ int length = 0;

  while (*str != '\0') {
    str++;
    length++;
  }
  return length;
}
Now this is where you get to see some of the special lurks that C has. You can combine the increment into the loop:
int strlen(char *str)
{ int length = 0;

  while (*str++ != '\0')
    length++;
  return length;
}
Indeed, since 0 = '\0' = False,
int strlen(char *str)
{ int length = 0;

  while (*str++)
    length++;
  return length;
}
Here is the compact form of strcpy
void strcpy(char *from, 
            char *to)
{
  while (*to++ = *from++)
    ; /* empty body */
}
You do eventually get used to this. However, you could always use the more readable versions!

Jan Newmarch (http://jan.newmarch.name)
jan@newmarch.name
Last modified: Wed Nov 19 18:57:29 EST 1997
Copyright ©Jan Newmarch