Home CPSC 340

Files, Strings and Pointers

File Input

To use input or output files, we must first include the fstream library:



#include <fstream>

To use an input file, we must create an ifstream variable. The variable lets us do input from a file, and it works a lot like cin.

When we declare the ifstream variable, we must specify what file to read from. Below is a small example that reads a string from a file called "input.txt"


#include <iostream>
#include <fstream>
using namespace std;

int main() {
    // declare the ifstream and tell it which file
    ifstream fin("input.txt");

    // read in an integer
    int value;
    fin >> value;

    cout << "We read " << value << " from the file!" << endl;
    return 0;
}

For this to work, the file must be in the same place as we run our program from.


Checking For File Not Found

If the input file you try to open does not exist, you won't be able to read from it. You can check for this in your program by checking if the file is open:



    ifstream fin("input.txt");

    if (fin.is_open()) {
        // the file opened fine!
    } else {
        // the file was not found!
    }

It's a good practice to check that the file opened correctly. If not, you can give an error message and ext.


Using Output Files

Using an output file is similar to using an output file, except we need an ofstream variable, which works a lot like cout:



#include <iostream>
#include <fstream>
using namespace std;

int main() {
    // declare the ofstream and tell it which file
    ofstream fout("output.txt");

    // print some output
    fout << "This output is going to a file!" << endl;
    return 0;
}

You have to be careful doing file output. If you write to an existing file, it will over-write it!


C-strings

In C++, strings are simply arrays of characters, which are often called C-strings. Because C++ provides no reliable way of knowing how many elements an array has, and doesn't check if an index is within the bounds of an array, we need some way of knowing when a C-string is finished. This is done using a "null character" to mark the end of the C-string. The null character is a character with an ASCII value of 0 that is just used to mark the end of the string.

Internal view of a string

This example shows how C-Strings are used.


int main() {
    // the null character is included automatically
    char string1 [] = "Hello World!";

    // if we give a size, it must include space for the null!
    char string2 [15] = "More Text Here";

    // print the strings
    cout << string1 << endl;
    cout << string2 << endl;

    return 0;
}
Because C-strings are just arrays, we can also loop through them as an array.


String Input

There are two ways to get string input in C++. The first is using cin >> as for numerical types. When using this, the string is input as a single word - with no spaces in it. This is demonstrated in this example.


int main() {
  char name[50];

  cout << "Enter your name: ";
  cin >> name;
  cout << "Hello " << name << "!" << endl;

  return 0;
}

This will accept "Bob" for the name, but not "Bob Smith", because of the space.

If we need to accept an entire line with spaces, then we can use cin.getline which takes an entire line of text as input and can include spaces. This allows us to input a full name:


int main() {
    char name[50];

    cout << "Enter your name: ";
    cin.getline(name, 50);
    cout << "Hello " << name << "!" << endl;

    return 0;
}

There is one issue that arises if you use cin >> and cin.ignore in the same program. When we do a cin >>, and the user enters some text and hits enter, it leaves the new line character in the input stream.

If we do a cin.getline after this, then it will read the new line and return an empty string as if the user hit enter without typing anything. In order to get around this, we can put cin.ignore() between the two which will read, and ignore, the new line character.

The following program demonstrates this, and can be fixed with the cin.ignore() line:


int main() {
    int x;
    cout << "Enter an integer: ";
    cin >> x;
    cout << "You entered " << x << endl;

    char name[100];
    cout << "Enter a string: ";
    cin.getline(name, 100);
    cout << "You entered " << name << endl;

    return 0;
}

Doing string input from the terminal with cin works exactly the same as from a file with an ifstream object.


String functions

Unlike the string class, C-Strings do not support the +, =, ==, !=, < etc. operators. When using these operators, you are working with the memory location of the string, not the string itself.

In order to work with C-strings, the cstring library includes a number of functions to work with C-Strings including:

FunctionDescription
strlen(str)Calculate the length of a string.
strcpy(str1, str2)Copy str2 into str1.
strcat(str1, str2)Append str2 to the end of str1.
strcmp(str1, str2)Compare str1 to str2.

The strcmp function returns less than 0 if the string on the left is less, 0 if the strings are equal, and greater than 0 if the string on the left is greater.

This example shows the use of these functions.

C++ does not protect against overwriting the bounds of a C-String, so care must be taken when using them.


Pointers

C++ allows programmers to interact with memory more directly than most other languages. It does this through "pointers".

A pointer is a variable that holds a memory address. In order to declare a pointer variable, we first have the type of variable that the pointer will hold the address of, the * character, then the name. This code declares a pointer named ptr that can hold the address of an integer:


int* ptr;

In order to actually store an address into this variable, we could use the address of operator (&) to take the address of some integer:


int x = 12;
ptr = &x;

Now the address of the variable x is held in the pointer variable ptr. We now say that ptr "points to" x because we can use the pointer ptr to access x. If we wanted to print the value of x we could do so using ptr instead:


cout << *ptr;

This is called "dereferencing" the pointer. This program demonstrates this use of pointers.


int main() {
  // declare a pointer to an integer
  int* ptr;
  
  // declare an actual integer
  int x = 12;

  // store the address of x into ptr
  ptr = &x;

  // print both values
  cout << "ptr = " << ptr << endl;
  cout << "x = " << x << endl;

  // print x by dereferencing ptr
  cout << "*ptr = " << *ptr << endl;
  
  // change the value of x through ptr
  *ptr = 73;

  // print x again
  cout << "x = " << x << endl;

  return 0;
}

In this particular program of course, there is no need for pointers. They will be necessary to build data structures and manage memory in C++


Pointers to Objects

We can make pointers to objects just like pointers to built-in types. If we have a pointer to an object, we can use the object through the pointer with dereferencing. The "." operator for calling a function of an object has higher precedence than the "*" dereference operator, so we must use parenthesis:


Circle circle;
Circle* pointer_to_circle = &circle;

(*pointer_to_circle).getArea();
Because this is so ugly, C++ has a shorthand notation for this using the "->" operator:

pointer_to_circle->getArea();

Copyright © 2018 Ian Finlayson | Licensed under a Creative Commons Attribution 4.0 International License.