C for C++ Programmers

CS230/330 - Operating Systems (Winter 2001).

written by Ian Cooke, who is responsible for all errors

Modifications have been made by Maximos Nolan; the C standard has been updated and prior information listed here is out of date.

The good news is that C syntax is almost identical to that of C++. However, there are many things you're used to that aren't available in C:

We'll cover some of the basics here. The end of this document has a couple of recommended books where you can go for further information (including classic book on C written by Kernighan and Ritchie and referred to here as K&R). The man pages are also a great source of information.

Comments

Commenting in C is the exact same as C++

/* this is a comment */

/* This is a multiline
   comment */

// This is a comment 

You cannot nest comments.
 /* This is  /*nested */ comment. And is illegal. */

I/O

C doesn't have stream operators. Instead you'll want to use the functions provided in the stdio library. In particular: printf, fprintf, fgets, fputs.

Output: printf, fprintf, fputs

The most common output function in C is printf() which prints characters to the screen (or wherever standard out is directed to go). Here's a quick hello world program that illustrates its use:
 #include <stdio.h>

 int main() {
   printf("Hello World");
 }
printf() has a variable number of arguments, the first of which is a format string. The format string can contain both ordinary characters (what you want to appear on the screen like 'Hello World' above) and conversion character sequences. You can think of conversion characters as placeholders for a specific type formatted in a particular way. Conversion character sequences begin with % and end with a conversion character. An example will perhaps make this clearer. Here we're printing out the integers from 0 to 9.

 int i;
 for (i = 0; i < 10; i++) {
   printf("the variable 'i' is: %d", i);
 }

The conversion sequence is %d, which you can think of as a placeholder for an integer. Since this is the first conversion character sequence, the value of the first argument after the format string--in this case the value of 'i'-- will be placed in the held spot. Conversion characters can specify type, precision, and all sorts of other formatting information. See K&R or the man pages for all the gory details, but here are essential ones cribbed from K&R:
   sequence    type   : produces

   %d or %i  - int    : signed decimal notation
   %s        - char * : prints characters until the null character is reached
   %x        - int    : hexidecimal notation
   %p        - void * : prints as a pointer 

printf() always prints to standard out. If you want to print to another place, such as standard error, use fprintf() which takes as its first argument the stream you're printing on:

  fprintf(stderr, "Fatal Error #2212. We're hosed");
  /* or with values */
  fprintf(stderr, "Fatal Error in foo(): value of bar is %p\n", bar);
Note that we're calling standard in, out, and error slightly different names than we do in C++:
   C++   C

   cin   stdin
  cout   stdout
  cerr   stderr
Finally, fputs() will also allow us to write to a stream.
  int fputs(const char *str, FILE *stream);
fputs() takes a null-terminated string 'str' and writes it to 'stream'. It omits the null character when it does this. It returns a non-negative integer if okay and EOF on error. An example:
  if ( (fputs("Hello world", stdout)) == EOF) {
    fprint(stderr, "Whoops, something went wrong");
 }
fputs() functions similarly to printf() when it writes to stdout, but it doesn't do any conversion which probably means it's quite a bit faster.

Input

Input is a bit trickier. For reading an entire line you'll probably want to use fgets(). Here's the prototype:
  char *fgets(char *buffer, int size, FILE *stream);
fgets() reads up to size-1 characters from stream and stores them in buffer. fgets() stores the null character ('\0') after the last character read into the buffer and returns 'buffer' if everything works fine, or NULL on error or end of file. Here's an example:
  char *cptr;
  char buffer[256];
  
  printf("Enter some stuff:\n");
  cptr = fgets(buffer, 256, stdin);
  if(cptr != NULL) {
    printf("You typed : %s\n", cptr);
  } 

Here's a more complicated example. Readline() uses fgets() to read up to MAX_LINE - 1 characters into the buffer 'in'. It strips preceding whitespace and returns a pointer to the first non-whitespace character.
 char *Readline(char *in) {
   char *cptr;
 
   if (cptr = fgets(in, MAX_LINE, stdin)) {
     /* kill preceding whitespace but leave \n 
        so we're guaranteed to have something*/
     while(*cptr == ' ' || *cptr == '\t') {
       cptr++;
     }
     return cptr;    
    } else {
     return 0;
   }
 }

Memory Allocation

There are no memory management operators such as new and delete in C. All memory allocation is done with the function malloc(). Here's its prototype:
  void * malloc(int nbytes)
malloc() takes an int indicating the number of bytes you want allocated and it returns a void pointer to the start of the allocated memory.
  /* We're going to allocate enough space for an integer 
     and assign 5 to that memory location */
  int *foo;
  /* malloc call: note the cast of the return value
     from a void * to the appropriate pointer type */
  foo = (int *) malloc(sizeof(int));  
  *foo = 5;

Even more complicated structures like arrays are allocated through malloc():
  /* Here we're allocating space for 5 integers */
  int *foo;
  foo = (int *) malloc(sizeof(int) * 5);
  foo[0] = 17;
  foo[4] = 22;
Freeing memory is done with the function free(). The prototype:
  void free(void *);
Here's an example of it being used:
  free(foo);
Pretty simple. However, note that it's disastrous to free a piece of memory that's already been freed.

More information is available in the man pages.

Variable Declaration

In older versions of C (prior to the C99 standard), you could only declare variables at the beginning of your program. However, for EECS 370,
we use the C99 standardmeaning you can declare variables at any point in the program. You might see references online that contradict this.
These references are refering to older standards (C89).

Constants

The standard way to declare a constant in C is to use #define.
  #define MAX_LEN 1024
  #define NUM_CALLS 20
This is also valid C++ code, though the use of #define is usually discouraged in C++. Like all preprocessor directives, #defines usually appear at the top of the source file.

Note that a #define is called a macro and is technically not a variable. Macros are used in the compilation phase of a program and all
references to a macro are replaced with its literal value.

Types

structs

C has no classes, but you can create composite types with struct:

Note that a class and a struct are almost identical; the only differences between the two is the default access level (structs are public by default,
classes are private).

  struct point {
    int x;
    int y;
  };

You would then use it like so:

  struct point p;
  p.x = 0;
  p.y = 0;

While this is acceptable, the preferred approach is to use pointers when using structures:

  struct point *p;
  p = (struct point *) malloc(sizeof(struct point));
  p->x = 0;
  p->y = 0;

The struct qualifier is required any time you're referring to a struct. You can see this above in the cast of the return value from malloc() and in the sizeof expression. In other words, having declared the struct point, we can't simply write:
 /* this code won't work */
 point p;
 p.x = 0;

as we would with a C++ class. We can get rid of this repetition of the struct qualifier by using typedefs. The typedef keyword essentially creates an alias. Thus we can typedef struct point to Point (or to lowercase 'point', but we're using uppercase to remind ourselves it's a struct).
  typdef struct point Point;
  Point *p;
  p = (Point *) malloc(sizeof(Point);
  p->x = 0;

booleans

There is no bool keyword in C. You can get the same behavior by importing the stdbool library OR creating an enumerated type.
Instead booleans in C are the same as integers with a value of 0 for false or 1 for true. Thus the following is an infinite loop:
  while(1) {
    ; /* do nothing */
  }

Libraries

Libraries functions are included by specying the name of the appropriate header file in an include statement:
  #include <stdlib.h>
  #include <string.h>
This is similar to C++ but the .h is required.

Here are some of the most useful C libraries:

You should know what most of these functions do, especially strtok() which you'll be using at a crucial point in the Yash shell.

Other References

More information is available in the man pages and also in the best book on C: The C++ Programming Language by Kernighan and Ritchie (referred to as K&R by nearly everyone). Expert C Programming by Peter Van Der Linden is also an excellent book on all kinds of advanced C topics such as linking, the differences between pointers and arrays, and how to make sense of stuff like this:

 int *(*foo[20]) (char *c);

It also has a pretty good description of program memory.

Common C functions in EECS 370

Note: for any c standard function, you may see its definition by opening terminal or ubuntu and typing in `man __CFUNCTION__`. For example,
man strcmp
will provide the linux programmer manual (man means manual) definition for strcmp
1)
  int strcmp(const char * c1, const char * c2);

  Returns whether the contants of c1 is equal to c2. If the value returned by strcmp is equivilent to 0, this means the two strings are identical.
  Otherwise, the values are not identical
2)
  char * strcpy(char * dst, const char * src); 

  copy source (src) string content to destination (dst) memory buffer. Ensure that you have allocated enouch space to copy dst into stc.
The following are `optional`. If you are interested in learning more about C and how to create a vector from scratch, read the below.

3)
 void * malloc(size_t size);

 allocate memory on the heap with size `size`. The call to malloc returns a pointer to the begining of the allocated memory buffer.
4)
 void free(void * mem);

 Unallocates memory on the heap for buffer pointed to by `mem`. 
How to go about creating your own vector in C
+ Allocate a buffer (buffer is a fancy word for region) in memory for a number of elements (typically you'll start with size 2)

+ If you want to insert a new element into the buffer but you are at capacity, you will need to allocate a bigger buffer! Consider how you can copy over the contents
of the old buffer to the new, larger buffer and free the older buffer. Don't forget to update the size! 

+ At the end of execution of your program, be sure to free the vector you created! Run valgrind! 

+ Consider how you can use size_t; what happens if you try to access an element that is out of bounds of your vector? How will you treat that behavior?


This site was last updated on 1/8/2023.