Show Lecture.BasicSyntax as a slide show.
CS253 Basic Syntax
The main() function
int main() {
return 0;
}
- This is a complete C program.
- main() is a function, not a method.
- Methods are functions inside of classes.
- methods ⊂ functions
- All methods are functions.
- Not all functions are methods.
- Unlike Java, C++ doesn’t have to have a class,
so main() is a function.
- We sometimes call this a free function.
- It’s not part of a class. It’s free!
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?
- They’re turned into complete programs via webserver magic.
- You still need #include <iostream>.
- You still need
using namespace std;
.
- You still need
int main() {
.
- The
return 0;
is optional in main(),
which is stoooopid, so I always include it
(unless I run out of slide space).
- You still need the
}
to close main().
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.
- 0: success
- >0: failures of various sorts.
Return different values for different failures, e.g.:
- 1: “memory too expensive”
- 2: “fish encountered in file”
- 3: “user is ugly”
- <0: That’s just weird. Only the least-significant
eight bits of the return value count, so −1 ⇒ 255.
Arguments
int main(int argc, char *argv[])
- argc: number of arguments, including the program name as
argv[0]
.
- argc stands for “argument count”.
- argv: array of C-style strings, corresponding to program arguments.
- argv stands for “argument vector”.
- argc is always ≥ 1, except in strange embedded environments.
- argv is always an array of C strings, even if you type
arguments that look like numbers or something else.
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
- Why is
argv[]
an array of char *?
Surely they should be C++ strings?
- C++ strings didn’t exist in C.
- Why is
argv[]
an array of char *?
Surely they should be const char *?
- C didn’t have const when this was all invented.
- It’s your memory, you should be able to change it.
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:
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
- The sizes of types are determined by the implementation.
- On my tiny laptop,
sizeof(long)==4
, but sizeof(long)==8
on
many computers.
- The C++ standard defines minimum sizes for types:
- short, int: 16 bits
- long: 32 bits
- long long: 64 bits
sizeof(char) == 1
sizeof(char)
≤ sizeof(short)
≤ sizeof(int)
≤
sizeof(long)
≤ sizeof(long long)
- Pointer types often vary.
sizeof(int *)==4
on a 32-bit machine
sizeof(int *)==8
on a 64-bit machine
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';
1732197881 3.14159 800000001
- const: not allowed to change this
- constexpr: compile-time constant, never changes
- static: longer-than-function lifetime, private if a global
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
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:
- Declare a variable:
int counter;
- 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: 1732197881
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: 1732197881
What type is now
? I don’t care.
It’s the type that time() returned.
AAA
- I’m part of the AAA (almost always
auto
)
school of thought.
- Don’t declare your variables until you have a value for them.
- That value will determine the type of the variable.
- Sure, sometimes you can’t. Those are the exceptions.
- It can be tricky:
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
Names
- Variable/function/method/class/type names are quite similar to Java.
- Start with a letter, continue with letters, digits, or underscore.
- Constants, by convention, are all upper case (
PI
).
- User-defined classes are often capitalized (
class Foo
);
- Avoid starting anything with an underscore (
_
),
or containing a double underscore.
- Modern C++ allows amazing Unicode variable names.
- Wish we had one o’ them modern compilers on this here website!
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
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.
- The braces are optional, for just one statement.
- Omitting them is a good way to start an argument with a programmer.
Truth & falsity
C++ is quite liberal about what if/while/for consider to
be trueish values:
Description | False values | True values |
Literals | false | true |
Non-zero integer | 0 0ULL | 42 |
Non-zero floating-point number | 0.0 0.0L | 6.022e22 |
Non-zero char | '\0' | 'x' |
Non-null pointer | nullptr NULL | "hello" |
- An if statement can contain a variable declaration.
The condition succeeds if the value is trueish.
if (const char *p = getenv("PATH"))
cout << "PATH is " << p << '\n';
PATH is /bin
- This is handy if the code computes a value that is only needed
inside the if, which is quite common.
- The scope of the new variable is restricted to the if statement,
even without braces. Smaller scope = better code!
- This works well if the desired value is trueish.
- A C++17 addition: an if statement can contain a variable declaration,
much like a for loop.
- This is helpful for the same reasons as the previous example.
- Sometimes, the useful value doesn’t correspond to a trueish value,
e.g., the error value is
-1
, not 0
.
if (auto now = time(nullptr); now != -1)
cout << "Time is " << now << '\n';
Time is 1732197881
auto now = time(nullptr);
switch (localtime(&now)->tm_hour % 12) {
case 2: case 3: case 5: case 7: case 11:
cout << "Prime time!\n"; break;
default:
cout << "Composite time!\n"; break;
case 1:
cout << "Bed time!\n";
}
Prime time!
- switch on integer types (char, short, int, long, …).
- You can’t switch on floating-point types or strings.
- The break prevents flow into the next case.
- In C++17, a declaration is allowed before the switch value,
as in if.
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
- No, C++17 doesn’t allow a variable declaration.
We already have a for loop for that.
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?
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 (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 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