CS253: Software Development with C++

Spring 2021

Basic Syntax

Show Lecture.BasicSyntax as a slide show.

CS253 Basic Syntax

The main() function

int main() {
    return 0;
}

Slightly more

Here’s a complete C++ program that creates some output:

#include <iostream>

using namespace std;

int main() {
    cout << "Hello, world!\n";
    return 0;
}
Hello, world!

How Examples Work

Many examples in these slides are just snippets of code:

cout << "How do you do?" << '\n';
How do you do?

The main() function

Here’s the other valid definition of main():

int main(int argc, char *argv[]) {
    // Display all arguments, including program name.
    for (int i=0; i<argc; i++)
        cout << "argv[" << i << "]: \"" << argv[i] << "\"\n";
    return 0;
}
argv[0]: "./a.out"

That is all

Don’t even ask about void main().

It does not exist.

⚠ ☢ ☣ ☠️

Return value

main() returns an int, a success/failure code to the invoker.

Arguments

    int main(int argc, char *argv[])

Arguments example

% cat ~cs253/Example/show-args.cc
#include <iostream>
using namespace std;

int main(int argc, char *argv[]) {
    for (int i=0; i<argc; i++)
	cout << "argv[" << i << "]: " << argv[i] << '\n';
}
% g++ -Wall ~cs253/Example/show-args.cc
% ./a.out CS253: "best class ever!"
argv[0]: ./a.out
argv[1]: CS253:
argv[2]: best class ever!
% mv a.out pinkiepie
% ./pinkiepie Cupcakes
argv[0]: ./pinkiepie
argv[1]: Cupcakes

argv[0] is the real program name, as executed—nice for error messages.

Compatibility

Basic types

C++ has a number of built-in types:

Modifiers

long, short, and unsigned are modifiers, not types. However, it’s common to use them alone:

VerboseUsual style
short int a;short a;
long int b;long b;
long long int c;long long c;
unsigned int d;unsigned d;
unsigned long int e;unsigned long e;

In a bagel shop, you don’t say “I’d like a whole wheat bagel.” You just ask for a “whole wheat”, and the “bagel” is implied. Similarly, int is implied.

Sizes

Qualifiers

Qualifiers (const, constexpr, static) modify existing types.

const auto now = time(nullptr);
constexpr double PI = 3.14159265;
static long csuid = 800000000;
cout << now << ' ' << PI << ' ' << ++csuid << '\n';
1732254978 3.14159 800000001

Derived types

Via pointers & arrays, a vast number of derived types exist:

    int a[10];		// 10 ints
    int *b;		// A pointer to any number of ints
    int *c[10];		// An array of 10 pointers-to-ints
    int (*d)[10];	// A pointer to an array of 10 ints

If you find complex types confusing, build intermediate types with typedef:

    typedef float cash; // cash is now a synonym for float (note the order)
    typedef int *intp;	// New type intp, a pointer to ints
    intp e[10];		// An array of 10 such pointers

Aliases via typedef

With typedef, you can create an alias for an existing type. It does not create a new type.

typedef int counter;    // typedef old new;
counter c = 42;
cout << c << '\n';
42

To use typedef, think of it in two steps:

  1. Declare a variable: int counter;
  2. Slap typedef in front of it: typedef int counter;

Aliases via using

We’re familiar with using from using namespace std; but it can also be used to make aliases for types:

using counter = int;    // using new = old;
counter c = 43;
cout << c << '\n';
43

Lazy Programmer’s Declaration

Or, you can just declare variables with auto (which is not a type!):

auto i=4;           // an int
auto r=3.45;        // a double
cout << i+r << '\n';
7.45

But you must initialize the variable, so that the compiler knows its type:

auto foo;
foo = 'x';
c.cc:1: error: declaration of 'auto foo' has no initializer

Why auto is useful

Consider this common code:

time_t now = time(nullptr);
cout << "Seconds since the start of 1970: " << now << '\n';
Seconds since the start of 1970: 1732254978

time_t is an alias (via typedef) for short, int, long, or long long. It can hold whatever time() produces. Many functions have a related type for their return value, and they clutter up your brain.

auto now = time(nullptr);
cout << "Seconds since the start of 1970: " << now << '\n';
Seconds since the start of 1970: 1732254978

What type is now? I don’t care. It’s the type that time() returned.

AAA

string a      = "Alvin";     // a std::string
const char *s = "Simon";     // a const char *
char t[]      = "Theodore";  // an array of char
auto d        = "Dave";      // which one?
cout << sizeof(a) << '\n'
     << sizeof(s) << '\n'
     << sizeof(t) << '\n'
     << sizeof(d) << '\n';
32
8
9
8

Literals

LiteralTypeLiteralType
12int1.2Ffloat
34Llong3.4double
56LLlong long5.6Llong double
123Uunsigned int'x'char
234ULunsigned long"pdq" const char array (C string)
345ULLunsigned long long"xyz"sstd::string (C++ string object)
nullptrnullptr_ttrue, falsebool

Names

cout << __VERSION__;
8.3.1 20191121 (Red Hat 8.3.1-5)

double π = 355/113.0; cout << π;
c.cc:1: error: stray '\317' in program

Control Flow

if

int x = 42;
if (x < 100)
    cout << "This is true\n";
This is true
double pi = 3.14159265;
if (pi*pi > 10) {
    cout << "Can’t even trust math any more‽\n";
}
else
    cout << "Good--math hasn’t changed.\n";
Good--math hasn’t changed.

Truth & falsity

C++ is quite liberal about what if/while/for consider to be trueish values:

DescriptionFalse valuesTrue values
bool literalfalsetrue
Non-zero integer00ULL42
Non-zero floating-point number0.00.0L6.022e22
Non-zero char'\0''x'
Non-null pointernullptrNULL"hello"

if

if (const char *p = getenv("PATH"))
    cout << "PATH is " << p << '\n';
PATH is /bin

if

if (auto now = time(nullptr); now != -1)
    cout << "Time is " << now << '\n';
Time is 1732254978

switch

auto now = time(nullptr);
switch (localtime(&now)->tm_hour) {
  case 2: case 3: case 5: case 7:
  case 11: case 13: case 17: case 19:
    cout << "Prime time!\n"; break;
  default:
    cout << "Composite time!\n"; break;
  case 1:
    cout << "Bed time!\n";
}
Composite time!

while

char c = 'f';
while (c < 'x')
    cout << c++ << ' ';
f g h i j k l m n o p q r s t u v w 

dowhile

double age = 0.0;
do {
    age += 1.0/13.0;
    cout << age << '\n';
} while (age < 1);
0.0769231
0.153846
0.230769
0.307692
0.384615
0.461538
0.538462
0.615385
0.692308
0.769231
0.846154
0.923077
1
1.07692

Isn’t that one too many iterations?

dowhile

double age = 0.0;
cout << fixed << setprecision(16);
do {
    age += 1.0/13.0;
    cout << age << '\n';
} while (age < 1);
0.0769230769230769
0.1538461538461539
0.2307692307692308
0.3076923076923077
0.3846153846153846
0.4615384615384616
0.5384615384615385
0.6153846153846154
0.6923076923076923
0.7692307692307692
0.8461538461538460
0.9230769230769229
0.9999999999999998
1.0769230769230766

Printing more digits shows that floating-point numbers are inherently imprecise.

for

for (int i=0; i<5; i++)
    cout << i;
01234

Declare your loop variable inside the for loop, so the scope of the loop variable is just the for loop. There are exceptions, sure. Focus on the 99% case.

int j;
for (j=0; j<5; j++);
    cout << j;
5

The error (extra semicolon) was not detected.

for (int k=0; k<5; k++);
    cout << k;
c.cc:2: error: 'k' was not declared in this scope

The error was detected.

Range-based for

// C array:
int a[] = {11,22,33};
for (int v : a)
    cout << v << ", ";
11, 22, 33, 
// C++ container:
vector<short> b = {101, 202, 303, 404};
for (short &n : b)
    n *= 3;
for (const short n : b)
    cout << n << ", ";
303, 606, 909, 1212, 
// Immediate list:
for (auto q : {12, 34, 56, 78})
    cout << q << " and ";
12 and 34 and 56 and 78 and