Lecture 9: Strings and Other Things

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Be Careful When freeing Strings or Any pointer object


	*** Watch out for aliases ***

- an alias is a "synonym" of another object.  for example:

char p[100] = "hi";
char *p2;

p2 = p;  /* this doesn't copy; two pointers to the same series of chars */

- now p2 is an alias for p.  What if we try to free both p and p2?

free p;
free p2;  /* oops! */

- remember the function call free returns the memory for an object
to the system so you can no longer use it.

	*** Watch out for uninitialized pointers ***

- just like anything else, ALWAYS initialize a pointer before
using it!
- you can't do *p or p[i] until p points to something.
- consider the following incorrect example:

char *s1;
char s2[] = "hello";
int i;

for  (i=0; i<6; i++)
  s1[i] = s2[i];  /* oops: we haven't allocated space for s1 */

free(s1) /* Worse! s1 doesn't point to anything! */
free(s2) /* Also bad.  We didn't allocate s2 using malloc */

	*** Watch how you compare strings ***

- just as we can't copy a string using s1 = s2,  we can't compare
a string using s1 == s2.  This compares the pointers, not the
contents of the string.  If s1 is an alias of s2, then s1 == s2 
will return true but otherwise, it will return false:

for example:

char s1[] = "hi";
char s2[3];

scanf("%s",s2);

/* Even if the user types in "hi", the following will be false: */
s1 == s2;


In order to compare two strings use the string library ...

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The string library:

- we use strings a lot so there are library functions for
doing the most common string operations:

#include 

/* copy string t into string s, return s (remember to malloc space for
   s first! */
char *strcpy(char *s, char *t);

/* compare s to t; 
    return <0 if s0 if s>t
*/
int strcmp(char *s, char *t);                

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
A Larger Example Using Strings and Pointers
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#include 
#include 

/* Scan in the next word(sequence of non-whitespace characters)
Allocate space for the word and return a pointer to it. */

char *scan_word(void)
{
  char *s, *temp;
  char current;
  int i, maxsize = 30;
  
  scanf("%c", ¤t);

  while (current == '\t' || 
	 current == ' ' || 
	 current == '\n'){
    scanf("%c", ¤t);
  }

  s = (char *)malloc(maxsize*sizeof(char));

  i = 0;

  while(current != '\t' && 
	 current != ' ' && 
	 current != '\n'){

    /* Check to see if we have enough room in s.
       If not, then enlarge the string */
    if (i == maxsize - 2) {
      maxsize = maxsize * 2;
      temp = (char *)malloc(maxsize * sizeof(char));

      /* We must have '\0' at the end of the string for strcpy: */
      s[i] = '\0';   
      strcpy(temp, s);

      /* Once we have copied the contents of the string, we delete
         the old copy */
      free(s);
      s = temp;
    }

    s[i] = current;
    i++;
    scanf("%c", ¤t);
  }

  /* Remember to tack on the NULL character */
  s[i] = '\0';
  return s;
}

/* Addresses will be represented as a structure: */

struct address {
  int num;
  char *street;
  char *city;
  int zip;
};

/* Scan in and allocate space for an address.  Return a pointer
to that address. */

struct address *scan_address(void)
{
  struct address *addr;
  int num;
  char *s;
  addr = (struct address *)malloc(sizeof(struct address));
  
  scanf("%d", &(*addr).num);
  (*addr).street = scan_word();
  (*addr).city = scan_word();
  scanf("%d", &(*addr).zip);

  return addr;
}

/* Names */

struct name{
  char *last;
  char *first;
  char initial;
};

/* Scan and allocate space for a name.  Return a pointer to
that name. */

struct name *scan_name(void)
{
  struct name *n;
  n = (struct name *)malloc(sizeof(struct name));

  (*n).last = scan_word();
  (*n).first = scan_word();
  scanf("%c", &(*n).initial);

  return n;
}

/* Compare n1 and n2 by last name, then first name, then initial.
     If n1 < n2 return -1
     If n1 == n2 return 0
     If n1 > n2 return 1 
*/ 

int compare_name(struct name *n1, struct name *n2)
{
  int res = strcmp((*n1).last, (*n2).last);

  if (res != 0)
    return res;
  else {
    res = strcmp((*n1).first, (*n2).first);
    if (res != 0)
      return res;
    else if ((*n1).initial < (*n2).initial)
      return -1;
    else if ((*n1).initial == (*n2).initial)
      return 0;
    else 
      return 1;
  }
}

/* A phonebook entry */

struct entry {
  struct name *n;
  struct address *addr;
  char *phone;
};

/* Allocate space for and scan in a phonebook entry */
struct entry *scan_entry(void)
{
  struct entry *e = (struct entry *)malloc(sizeof(struct entry));

  (*e).n = scan_name();
  (*e).addr = scan_address();
  (*e).phone = scan_word();

  return e;
}