Computer Systems Organization

Start Lecture #5

3.6: Loops—Do-While

Same as Java.

3.7: Break and Continue

Same as Java.

3.8: Goto and Labels

The syntax is

    goto label;
  
The label has the form of a variable name. A label followed by a colon can be attached to any statement in the same function as the goto. The goto transfers control to that statement.

for (...) {
  for (...) {
    while (...) {
      if (...) goto out;
    }
  }
}
out: printf("Left 3 loops\n");

Note that a break in C (or Java) only leaves one level of looping so would not suffice for the example on the right.

The goto statement was deliberately omitted from Java. Poor use of goto can result in code that is hard to understand and therefore it is rarely used in modern practice.

The goto statement was much more commonly used in the past.

Chapter 4: Functions and Program Structure

4.1 Basics of Functions

Very Simplified Unix grep

The Unix utility grep (Global Regular Expression Print) prints all occurrences of a given string (or more generally a RE) from standard input. A very simplified version is on the right.

#include <stdio.h>
#define MAXLINE 100
int getline(char line[], int max);
int strindex(char source[], char searchfor[]);
char pattern[]="x y"; // "should" be input
main() {
  char line[MAXLINE];
  int found=0;     // "should" be Boolean
  while (getline(line,MAXLINE) > 0)
    if (strindex(line, pattern) >= 0) {
      printf("%s", line);
      found++;
    }
  return found;
}
int getline(char s[], int lim) {
  int c, i;
  i = 0;
  while (--lim>0 && (c=getchar())!=EOF && c!='\n')
    s[i++] = c;
  if (c == '\n')
    s[i++] = c;
  s[i] = '\0';
  return i;
}
int strindex(char s[], char t[]) {
  int i, j, k;
  for(i=0; s[i]!='\0'; i++) {
    for (j=i,k=0; t[k]!='\0'&&s[j]==t[k]; j++,k++) ;
    if (k>0 && t[k]=='\0')
      return i;
  }
  return -1;
}

The basic program is

    while there is another line
      if the line contains the string
        print the line
  

Getting a line and seeing if there is more is getline(); a slightly revised version is on the right. Note that a length of 0 means EOF was reached; an "empty" line still has a newline char '\n' and hence has length 1.

Printing the line is printf().

Checking to see if the string is present is the new code. The choice made was to have it a function strindex() that is given two strings s and t and returns the position (the index in the array) in s where t occurs. Strindex() returns -1 if t does not occur in s.

The program is on the right; some comments follow.

Note that a function definition is of the form

    return-type function-name
    {
        declaratons and statements
    }
  
The default return type is int.

The return statement is like Java.

4.2: Functions Returning Non-integers

The book correctly gives all the defaults and explains why they are what they are (compatibility with previous versions of C) It is much simpler to always

  1. Use no defaults when defining a function.
  2. Declare all functions that are used in a file. Have this declaration early, before any function definitions.

4.3: External Variables

A C program consists of external objects, which are either variables or functions.

External vs. Internal

Variables and functions defined outside any function are called external. Variables and functions defined inside a function are called internal.

Standard C does not have internal functions, that is you cannot in C define a function inside another function. In this sense C is not a fully block-structured language (see block structure below).

Defining External Variables

As stated, a variable defined outside functions is external. All subsequent functions in that file will see the definition (unless it is overridden by an internal definition).

These can be used, instead of parameters/arguments to pass information between functions. It is sometimes convenient to not have to repeat a long list of arguments common to several functions, but using external variables has problems as well: It makes the exact information flow harder to deduce when reading the program.

4.4: Scope Rules

The scope rules give the visibility of names in a program. In C the scope rules are fairly simple.

Internal Names (Variables)

Since C does not have internal functions, all internal names are variables. Internal variables can be automatic or static. We have seen only automatic internal variables, and this section will discuss only them. Static internal variables are discussed in section 4.6 below.

An automatic variable defined in a function is visible from the definition until the end of the function (but see block structure, below).

If the same variable name is defined internal to two functions, the variables are unrelated.

Parameters of a function are the same as local variables in this respect.

External Names

  int main(...) {...}
  int value;
  float joe(...) {...}
  float sam;
  int bob(...) {...}

An external name (function or variable) is visible from the point of its definition (or declaration as we shall see below) until the end of that file. In the example on the right main() cannot call joe() or bob(), and cannot use either value or sam. Joe() can call bob(), but not vice versa.

Definitions and Declarations

There can be only one definition of an external name in the entire program (even if the program includes many files). However, there can be multiple declarations of the same name.

An declaration describes a variable (gives its type) but does not allocate space for it. A definition both describes the variable and allocates space for it.

extern int X;
extern double z[];
extern float f(double y);

Thus we can put declarations of a variable X, an array z[], and a function f() at the top of every file and then X and X are visible in every function in the entire program. Declarations of z[] do not give its size since space is not allocated; the size is specified in the definition.

If declarations of joe() and bob() were placed at the top of the previous example, then main() would be able to call them.

If an external variable is to be initialized, the initialization must be put with the definition, not with a declaration.

#include <stdio.h>
double f(double x);
int main() {
    float y;
    int x = 10;
    printf("x is %f\n",x);
    printf("f(x) is %f\n",f(x));
    return 0;
}
double f(double x) {
    return x;
}
x is 0.000000
f(x) is 10.000000

4.5: Header Files

The code on the right shows how valuable having the types declared can be. The function f() is the identity function. However, main() knows that f() takes a double so the system automatically converts x to a double.

It would be awkward to have to change every file in a big programming project when a new function was added or had a change of signature (types of arguments and return value). What is done instead is that all the declarations are included in a header file.

For now assume the entire program is in one directory. Create a file with a name like functions.h containing the declarations of all the functions. Then early in every .c file write the line

    #include  "functions.h"
  
Note the quotes not angle brackets, which indicates that functions.h is located in the current directory, rather than in the standard place that is used for <>.

4.6: Static Variables

The adjective static has very different meanings when applied to internal and external variables.

  int main(...){...}
  static int b16;
  void sam(...){...}
  double beth(...){...}

If an external variable is defined with the static attribute, its visibility is limited to the current file. In the example on the right b16 is naturally visible in sam() and beth(), but not main(). The addition of static means that if another file has a definition or declaration of b16, with or without static, the two b16 variables are not related.

If an internal variable is declared static, its lifetime is the entire execution of the program. This means that if the function containing the variable is called twice, the value of the variable at the start of the second call is the final value of that variable at the end of the first call.

Static Functions

As we know there are no internal functions in standard C. If an external function is defined to be static, its visibility is limited to the current file (as for static external variables).

4.7: Register Variables

Ignore this section. Register variables were useful when compilers were primitive. Today, compilers can generally decide, better than programmers, which variables should be put in register.

4.8: Block Structure

Standard C does not have internal functions, that is you cannot in C define a function inside another function. In this sense C is not a fully block-structured language.

  #include <stdio.h>
  int main(void) {
    int x;
    x = 5;
    printf ("The value of the outer x is %d\n", x);
    {
      int x;
      x = 10;
      printf ("The value of the inner x is %d\n", x);
    }
    printf ("The value of the outer x is %d\n", x);
    return 0;
}
The value of the outer x is 5.
The value of the inner x is 10.
The value of the outer x is still 5.

The gcc compiler for C does one to define a function inside another function. These are called nested functions; many consider this gcc extension to be evil.

Of course C does have internal variables; we have used them in almost every example. That is, most functions we have written (and will write) have variables defined inside them.

Also C does have block structure with respect to variables. This means that inside a block (remember that is a bunch of statements surrounded by {}) you can define a new variable with the same name as the old one. The lifetime of the new variable is just the lifetime of this execution of the block. The program on the right produces the output shown.