Download as ppt, pdf, or txt
Download as ppt, pdf, or txt
You are on page 1of 45

C++ Fundamentals

Overview
A Couple Notes
Displaying Pointers
Freeing memory
Standard Libraries
Types
Conversion
Casting
Bitwise Operations
Constants
Assertions
Declaration versus Definition
Header Files
Static initialization
Compilation Process (Overview)
Displaying Pointers
The output of this code is garbage why?
char c = c;
char* pc = &c;
cout << pc << endl; //Want pointers value
C strings are null-terminated character arrays
char* cString = Hello!;
cout << cString << endl;
//We expect to see Hello
So the first example prints bytes from &c to the first null.
To get what we want
char c = c;
char* pc = &c;
cout << static_cast<void*>(pc) << endl;
//Pointers value printed
Not normally necessary with non-character pointers.
Freeing Memory

Remember:
Use scalar delete if you used scalar new.
Use array delete if you used array new.
Using the wrong one results in undefined
behavior!
int* scalar = new int(5);
int* arr = new int[30];
delete arr; //NO! Use delete[]
delete [] scalar; //NO! Use delete
Overview
A Couple Notes
Displaying Pointers
Freeing memory
Standard Libraries
Types
Conversion
Casting
Bitwise Operations
Constants
Assertions
Header Files
Static initialization
Compilation Process (Overview)
Standard Libraries

The Standard C Library is part of Standard C++.


Use the #include<c*> form instead of <*.h>
Dont mix the two forms.

But, some functions have C++ replacements that


should generally be used instead.
Use new and delete for memory management.
Not malloc/free!
Use iostreams for I/O.
Do not use printf!
Fundamental Types

bool
Values of true or false only.
enum
Named values
Integral
char, short, int, long
signed and unsigned

Floating-point
float, double, long double
Integral Types
For integral types, the following maximum value
hierarchy holds:
signed char unsigned char
[signed] short [int] unsigned short [int]
[signed] int unsigned int
[signed] long [int] unsigned long [int]
int is usually either a short or long.
The C++ standard defines the minimum range of each
type.
Arithmetic operators apply only to types int and long
(or their unsigned variants).
Operands smaller are promoted to the appropriate type
Integral Types (cont.)

wchar_t (the wide character) is distinct


But otherwise behaves as its underlying type
(one of the integer types).
Assigning bool variables to integers:
Results in 1 for true, 0 for false.
Integer Literals

Can give in:


Octal (prefix with zero, e.g., 018)
Hexadecimal (prefix with 0x,e.g., 0xFE)
Decimal (non-zero leading digit)
Default type is signed int.
Can specify type and sign:
For long, add suffix l or L
For unsigned, add suffix u or U
Can mix and match the two
Integral Types (cont.)

What if you need to know specific information


about the types?
#include <climits> for integral types
#include <cfloat> for floating-point types
Integral Types: char
There are three character types:
char
signed char
unsigned char
They are distinct types.
Assignment provides explicit conversion (bitwise assignment)
Explicit casting required for pointers.
char is either signed or unsigned, but its not standardized.
It matters for widening conversions (e.g., to an int)!
Every bit of an unsigned char is used in its value.
I.e., the type has range [0, 2CHAR_BIT 1].
sizeof(char) == 1
Always!
But, 1 byte is NOT always 8 bits
C++ guarantees char to be 8 bits.
Integral Types: Promotions

Also called widening conversions.


When promoted:
signed types sign-extend
unsigned type zero-extend

This means that if char is signed:


char c = 0x80; //Assuming 8-bit char
int x = c; //x == 0xFF80!
Integral Types: Promotions

enum types promote to one of:


int, unsigned int, long, unsigned long
Whichever is the smallest type large enough to
represent all enum values.
Integral Types: Demotions

Also called narrowing conversions.


When converting one type to a smaller type.
For conversions to unsigned types:
result = value mod 2n
where n is the number of bits in the destination type
For conversions to signed types:
Implementation-defined if destination type is too
small to represent the value.
Often, this results in high-order bit truncation
E.g., signed char c = 0x55FF // c == -1
Type Usage
Use the right types!
Not everything is an int!
Avoid unnecessary type conversions.
Especially narrowing conversions.
Use size_t where appropriate.
For the result of sizeof
For many Standard C++ Library functions.
Heed compiler warnings!
Compile with Wall in g++
Most warn of narrowing conversions.
Floating-Point Types
Three types:
float (single precision, f/F suffix)
double (double precision, no suffix)
long double (extended precision, l/L suffix)
Can use E or e (scientific notation) for literals.
Finite-precision floating-point numbers.
Performing arithmetic with them is likened to moving
piles of sand: you lose a little every time!
Only a small subset of (and large members of Z) are
representable.
Inherent roundoff error (loss of precision) in arithmetic.
E.g., 100 * 23.4f = 233.99999141693115
Floating-point Types:
Demotions
Demoting to integral type discards fractional part
I.e., truncates toward zero
If floating-point value is too large to fit in the integer, the
result is undefined.
Demoting to smaller floating-point types: if the source
value is
outside destinations range, the result is undefined.
in range but unrepresentable, the next highest or lowest
represtentable value is used (implementation-specific).
exactly representable, then no error or roundoff occurs.
Floating-point Types:
Warnings
Floating point computation can be surprising.
It doesnt work the way one would expect.
Never (ever) compare using ==
You can get really surprising results!
Numerical computation is a course in itself.
Arithmetic Conversions

Used when operands are of different types.


These happen implicitly!
Compiler makes the types match.
If a floating-point type is involved:
Narrower operand is promoted to the (larger)
floating type.
Arithmetic Conversions (cont.)

If no floating-point types are involved:


If both operands are smaller than int, then the
result is int.
Otherwise:
Result type will be the type of the larger operand.
Result will be unsigned only if either operand is
unsigned long or unsigned int.
Mixing signed and unsigned types is usually
a bad idea.
Casting
Explicitly change somethings type
Can save work, e.g.,
int i;
double x;
/*...*/
i = i + x; //using int(x) is faster!
Three forms
C style cast of the form (type) var, e.g.,
char* p = (char*) pointer;
Function style cast of the form type(var), e.g.,
char p = char(pointer);
Only works with single-token typenames (e.g., no pointers)
C++ style or New style casts
Use these!
New C++ Casts
All have form x_cast<dest_type>(expr)
Each has a specific purpose.
Four distinct casts
const_cast
Used for casting away const or volatile
dynamic_cast
Used for type-safe downcasting.
Dynamic, as in at runtime.
Requires RTTI information.
Incurrs runtime overhead for type check.
If cast is invalid (i.e., downcast is not safe), returns
NULL.
New C++ Casts
static_cast: used for changing type at compile time.
Allows:
Up/downcasting.
Conversion from void*
Built-in type conversions
Implicit and user-defined conversions
reinterpret_cast: used for everything else.
Which usually means voodoo and black magic.
The dont ask, dont tell cast. Allows:
Pointer to [different type of] pointer
Reference to [different type of] reference
Pointer to integral
Integral to pointer
About as unsafe as traditional casting methods but easier to
find!
New C++ Casts

Seems like a lot of extra typing. Why bother?


Clearly documents what you mean to do
Because its not a one size fits all system.
And makes them easy to spot in code reviews!
Allows easy searching for potentially buggy
casts
Bitwise Operators
Operations that work on the bit level.
Who cares about them?
Not most people.
Operating systems do!
Systems programming
Flags, etc.
Programmers working in limited memory environments
do!
E.g., embedded/mobile devices.
Can pack boolean values into an int.
E.g., std::bitset, std::vector<bool>
You do!
Program 2 requires bitwise operations.
Bitwise Operators (cont.)
| Bitwise-OR
& Bitwise-AND
^ Bitwise-XOR
~ Bitwise-NOT
<< Shift left
>> Shift right
Only work on integer types
Any size (char, short, int, long)
Bitwise Operators (cont.)

Using unsigned integers preferred


Prevents unintended arithmetic (signed) shift.
For signed integers, the result of a signed right
shift of a negative number is implementation-
defined.
x >>= n usually equivalent to x / 2
Result of any shift by a negative number is
undefined.
Overview
A Couple Notes
Displaying Pointers
Freeing memory
Standard Libraries
Types
Conversion
Casting
Bitwise Operations
Constants
Assertions
Declaration versus Definition
Header Files
Static initialization
Compilation Process (Overview)
Constants in C++

In pre-standard C, there was no const keyword.


Constants were declared like this:
#define BUFF_SZE 256
The const keyword now exists in Standard C,
but behaves differently than in C++.
Dont do this!
This (ab)use of the preprocessor is error-prone.
Use the const keyword instead:
const size_t BUFF_SZE = 256;
Constants in C++ (cont.)

Constants should be used instead of magic


numbers.
Which is clearer?
if (y < PING_TIMEOUT)
if (y < 2500)
Especially 3 weeks (or days) from now!
Gives a readable name to the value
Makes it easy to change its value later
Constants default to internal linkage.
Assertions
Around since the (good ol?) C days.
assert() macro (yes, macro):
Defined in <cassert>
Calls abort() if its argument is false.
But first prints the errant expression, file, and line
number!
Enabled by default.
Disabled if the NDEBUG preprocessor symbol is
defined.
#define NDEBUG
#include <cassert>
Assertions (cont.)

Part of good defensive programming practice.


When is assert() used?
Too infrequently!
For saying in code that something should always be true
(no matter what)!
Not for handling exceptional situations.
If an assertion fails, something should be horribly wrong.
Usually due to a software bug.
Or an erroneous assertion
Disabled in production code (usually).
The decision is slightly controversial.
Declaration versus Definition

A variable or function declaration:


Gives a name (identifier) to the compiler.
Also says what it looks like (the signature)
E.g.,
extern int a; //Definition w/o extern
void function(int, int);
Declaration versus Definition
(cont.)
A variable or function definition:
Allocates storage for a name.
E.g.,
int a;
void function(int x, int y){}
A definition can also be a declaration.
If the identifier is defined without being
separately declared.
Class Declaration versus
Definition Terminology
An incomplete class/type declaration:
Declares only the name of the class/struct, e.g.,
class MyClass;
Used to break circular name dependency.
Can only make pointers or references
Because the size of the class isnt necessary.
A class declaration:
Declares everything about the class.
Member functions, data members, friends, access specifiers, etc.
Member functions need not be defined in the declaration.
Class Declaration versus
Definition Terminology (cont.)
A class definition:
Consists of the member function implementations.
These terms are less consistent than those for
variables and functions.
But those presented here are most compatible
with the terms meanings for variables and
functions.
Header Files
Used to separate the interface (declaration)
from the implementation (definition)
Header files typically contain:
Declarations!
Of classes, functions, and variables.
Constants, enums, typedefs
Definitions of inline functions.
Used to optimize code (function body substituted for
the call).
Only #include header files (.h)!
Never, ever .cpp files.
Header Files: Standard Form
#ifndef FILENAME_H //Include Guards
#define FILENAME_H //Prevent multiple inclusion
#include <needed_headers>
#include <string>
// constants, enums, etc good to wrap in a namespace
//NO USING DIRECTIVES!
class Foo {
int i;
static std::string s;
Foo();
int funcDeclaration(int, float);
void inlineFunc(){}
};

#endif /* FILENAME_H */
Header Implementation (.cpp)

Provides the implementation of the headers


interface.
Typically contains:
Definitions for all undefined functions in the
header
Storage allocation and initialization for static
data members
Header Implementation:
Standard Form
#include filename.h
#include <other_needed_headers>
// constants, enums, etc good to wrap in a
namespace
//using directives are okay!
using namespace std;
Foo::Foo(){}
int Foo::funcDeclaration(int x, float y){//}
static string Foo::s(Ctor Arg);
Overview
A Couple Notes
Displaying Pointers
Freeing memory
Standard Libraries
Types
Conversion
Casting
Bitwise Operations
Constants
Assertions
Declaration versus Definition
Header Files
Static initialization
Compilation Process (Overview)
Compilation Process

C++ is a compiled language.


As opposed to an interpreted language.
Line is blurring (e.g., Java, Python).

Result of compilation is executable machine


code.
Compiler is run on each translation unit (.cpp
file) separately!
Linker combines the results.
Compilation Process (cont.)
Step 1: Preprocessing
Preprocessor directives (#...) are applied.
Step 2 & 3: Parsing & Code Generation
Normally some optimizing, too.
Static type checking occurs here.
Thats why the compiler needs the declarations!
Results in an object file (.o or .obj, usually)
As in, the goal (object) of the compiler
Step 4: Linking
Separate activity from compiling.
Resolves references.
Compilation Process (cont.)
This is when
.h compile errors can occur!
Compilation
Phase Processed Source Object File

.cpp Preprocessor Compiler .obj


Parsing
#directives processed
Static type checking
Declarations Needed
Linking All references Code generation
Optimizations
Phase must be resolved!
Else, linker errors occur!

Linker .exe
.obj

Object files to be linked


Executable
File

Libraries You Standard Startup


Requested Library Module

You might also like