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 home machine,
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';
1732267636 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: 1732267636
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: 1732267636
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
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 << "Good--math hasn’t changed.\n";
}
else
cout << "Can’t even trust math any more‽\n";
Good--math hasn’t changed.
- The braces are optional, for just one statement.
- Omitting them is a good way to pick an argument with a programmer.
if
An if
statement can contain a variable declaration.
The condition succeeds if the value is true
(non-zero):
if (const char *p = getenv("PATH"))
cout << "PATH environment variable is " << p << '\n';
PATH environment variable is /bin
- The scope of the new variable is restricted to the
if
statement,
even without braces.
- Smaller scope = better code!
switch
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!
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
do … while
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?
do … while
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
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