I'm expecting to put out lecture size workshops 2 times a week, maybe
Mondays and Thursdays. They will be posted to the NYLUG mailing list,
NYLXS mailing list (hangout), to comp.lang.C++. I'll try to cross post
to all three mediums, and I'll be putting up edited versions of
everything at http://www.nylxs.com/docs/workshops/ . Sunday nights I
hope to meet with everyone on line on IRC on Freenode, #nylug during
normal Sunday night NYLXS technite events. Finally, I hope that we can
meet every two weeks in order to work together on code and
exercises...although admittedly, getting that done once a month might be
a more doable goal. Times and Places we will discuss and work out. We
might be able to dove tail this with the NYLUG workshop if they don't
view that as an invasion.
As a roadmap, after the first three months, I hope to move into studying
serious Unix based systems libraries, such as networking libraries, file
system libraries, unix sockets, and all that wonderful stuff many of us
know nothing about. Then I hope to spend a 6 months on the Linux Kernel
and Device drivers. I know that the Kernel is largely C and not C++ but
I think we can still pull this off, and after about 6 months of this, I
hope to move on to X11 libraries, and application building. Perhaps we
can touch GTK libraries, QT libraries after the core X11 set is
explored.
Its a lot of work and it takes a serous commitment. I tried to do this
once before and it failed on its face. So I'm hoping we can start this
up again with more success.
In my mind, it is very easy to get involved in one of the advanced
scripting languages in order to have fun, and even make a few bucks, but
everything always comes back to C and C++ for the kind of advanced work
and employment that separates the unemployed from the well employed in
times of recession.
Obviously, we will be using GCC, Make, Autoconf, and Tags and personally
I'll be using VIM, and I'll discuss it much at the beginning. No editor
wars. Use whatever editor you enjoy. I don't care. Just please don't
distract the program with a parallel track of study.
So who is still game? This kind of study could cost someone 10 grand at
NYU and you might never learn as much.
Ruben
In organizing the concepts of C++ which are being introduced we can view
each of them separately, but studying them it is essential to keep in
mind that they are designed to function together in the construction of
programs which are easier to debug, which allow for cleaner overall
syntax, and which encourage creation of reusable code.
We shall explore:
Data Types (build in and user created)
Pointers and References (and their subtle deference)
Manual Memory Allocation and the 'new' and delete keywords
Class Declaration
Class Definition
Private data
Public data
Object instantiation and access, Class typedef
Class Constructors
Class Destructor
Function or Method Overloading
Operator Overloading
External extension of definitions
Copy Constructors
at the end we'll try to actually make the example class, which is an
extended array.
1) Data Types: DATA DATA DATA, everything is DATA
As we know, C and C++ have built in data type which each variable and
object the language needs to be defined as. Both C and C++ are typed
language.
We have char, int, double, float, pointer, and so on, a complete list
of which is available around the net. A pointer stores an address of
another C++ object. Different hardware and software platforms also
have different sizes that it allocates for these data types, creating an
inconsistency which, as an aside, the Free Software community has tried
to address with the creation of the glib library, part of the GTK project.
What is less apparent is that with regard to a computer, everything is
data. We have the data that we create and manipulated with instructions,
but the instructions themselves are a form data which we can package
up and save for later use, and feed to the CPU at will. When the CPU
runs out instructions the resulting actions are further instructions
that can be packaged and saved as data. In a word, everything is data,
and how we package that data is what separates one programming language
from the next.
This concept is covered extensively in NYLXS “Introduction Programming
with Perl” class and since this is an advanced topic, we won't get
much further into this. But we will review the basics of data types and
then look at the new feature that C++ gives which C didn't have in such
a generous way, the ability to easily create new data types easily and
in a reusable fashion.
An integer in the C family on the 32 bit Intel clone architecture is
defined as a marked space in memory of 32 bits in size to represent both
positive and negitive numbers. On the new 64 bit architecture that many
of you might have, I don't know if this still holds true since the word
size of a those machines is 64 bits or 8 bytes.
when you use the declaration
int a = 3456;
The computer your program sets aside 32 bits, 4 bytes, of space in ram and
puts the binary representation of that value in that space. The leftmost
bit is usually the signed bit determining whether the number represented
within is either positive or negative. A signed integer can therefor
have a maximum value of 2,147,483,647 positive or negative. beyond that
you must use a long int, which on 32 bit architecture actually won't
help you, or to use external libraries with other data types defined.
By default, C allows certain syntax with a data type. It will
automatically translate it, for example, into a char that will print
its representation for functions such as printf or in C++ the cout object:
printf (“%d\n”, myint);
cout << myint;
It can be combined with operators it can be used with the assignment
operator to fill or initialize its space with data.
int myans,myx = 6,myy =12;
It can be combined with arithmetic operators and have results assigned
accordingly.
myans = myy + myx;
Two of them can be compared.
while (myy < myx){
... ...
}
They can be auto incremented
myy++; ++myy;
and so on....
One data type is actually a serial arraignment of data, that is an array.
int myarray[100];
This defines an array of 100 elements indexed from zero to ninety-nine.
myy = myarray[4];
assigns the fifth element of our array to our integer variable myy.
One thing you can not do with an array data type is use an assignment
operator on the entire array object.
myarray[] = myarray2[]; //THIS IS AN ERROR
C++ allows up to define our own data types that have all the properties
of the built in ones. It uses the class mechanize, operator overloading,
and the “new” keyword to accomplish this.
2) Pointers and References – Where did I PUT THAT!
When we create data for our program, we ask the program to insert memory
into RAM and to retrieve or assign the data from that memory location
for use. Internally the program keeps track of the symbols and the
memory locations. In fact is you run the program “nm” on a C or C++
binary it will tell you all the symbols that it has in that binary.
ruben@www2:~/cplus> nm file3|less
0804a210 A __bss_start
08048a84 t call_gmon_start
0804a29c b completed.1
0804a0bc d __CTOR_END__
0804a0b4 d __CTOR_LIST__
U __cxa_atexit@@GLIBC_2.1.3
0804a204 D __data_start
0804a204 W data_start
08048ed0 t __do_global_ctors_aux
08048ab0 t __do_global_dtors_aux
0804a208 D __dso_handle
......
But we can also create memory locations that are assignable, and store
a representation of that memory location directly into a variable that
only stores the memory location as data, not the data itself. In c
and C++ this is called pointers and we can use the following syntax to
create them.
int *pt = &myint;
This declares the pointer to an int variable called pt which stores the
address for myint. The syntax int * in a declaration (and ONLY in a
declaration) says make a pointer to an int. The & syntax in front of a
variable myint says don't return the value of the variable, but return
the address of the data stored in the variable itself.
There are functions that return only pointer data. Those functions make
it possible to access memory without the declaration of variables at all.
There are also declarations that can be made in C and C++ which can
create variables without variable names either.
int (*pt)[10];
This declares a pointer (pt) to an array of 10 integers.
char (*p)[10][100]
This is a pointer which addresses an array of 10 pointers (implied)
to arrays of 100 chars each. Commonly this is know as a point to an
array of 10 strings. The symbolic variable name for an array often
gets automatically cast as a point type.
Most string functions in C return a char pointer for example
char * strtok(char *s1, const char *s2);
This would return an address of a char, which in theory would represent
an array of chars. In use it would look like this
char * spt; char wd1[100] = 'hello world', wd2[100] = ' ';
spt = strtok(wd1,wd2); printf(“%s\n”, spt);
Manual Memory Allocation and the 'new' and delete keywords
C++ makes it very convent to create dynamically allocated memory which
is accessed by pointers. We might call these anonymous pointers because
they do not point to any variables, just defined memory. We do this
with the key word “new”.
int *pt = new int(124);
This creates a new int pointer called pt and assigns to the memory
pointed to by pt with the integer value 124.
delete pt;
deletes the anonymous pointer pt.
int *pt = new int[100];
This declaration creates a new int pointer to an array of 100 integers,
with no data assigned yet to that block of memory.
delete [] pt;
deletes the entire array pointed to by pt and then undefines pt.
Ruben
--
http://www.mrbrklyn.com - Interesting Stuff
http://www.nylxs.com - Leadership Development in Free Software
In C++ there are three categories of datatypes. There is the built in
datatypes and their built in aggregates. There is standard C++ extended
Class based datatypes. And then there are user defined or created
datatypes.
A) Built in Datatypes and their aggregates
B) Standard Library C++ datatypes
C) User Created Data types.
Previously I discussed that when it comes to a computer, and any
programming language, everything is data. The code you write is data,
the information your program retrieves and manipulates as a target is
data, and the addressing of the storage locations of your data is data.
In order to think about this in a simplified way, we often view a
computer as a process manipulation system. This mathematically and
theoretically involves set theory and Turing Machine principles, which I
am not going to cover, but try to remember that when dealing with your
computer, and when programming your computer, that is system exists in
the real world. It is easy to forget this, but strangely enough,
computers aren't just good at virtual reality, but actually work in
reality...period. It is a machine. And as such it has inherent
properties, many of which we take for granted in the real world, although
scientists and engineers don't take it for granted, but when we interact
with our digital systems, we forget, and then we suffer from poor coding
habits and computers that perplex us.
As a real world machine, computers deal with processes. And these
processes we try to control and shape to our needs. All processes, and
this isn't negotiable, but a matter of fact, have three basic elements.
They have input. They have output, and they have side affects. In the
physical world, for example, all processes result in an increase in
heat. The input is usually some kind of fuel. And the side affect is
pretty much what we find useful. A simple machine like a gasoline
powered
automobile takes in gas, air and electricity, outputs burnt fuel, gases
like Carbon Dioxide, and HEAT, and the side affect is controlled
movement.
Computers and coding is a little more complex, but the principles remain
intact. Your data, in the form of code, is sent into the CPU, along
with other inputs and data, the CPU processes that information and
returns its "state" and all the pretty pictures, sounds, printed
materials, whatever your hardware allows, is the side affect.
Now that is very internal to your system, but another useful process to
examine and to be aware of when working with your computer is process of
how information flows through your computer, and involves the end user.
On a standard computer model, the real world of process control is built
into your digital computer. Information (data) enters your computer
from the STANDARD INPUT DEVICE (your keyboard), does some work and
displays results on your STANDARD OUTPUT DEVICE. And the side affects
can be varied. But this is an artificial construction, otherwise the
computer would be useless, because the interaction of your computer with
the real physical world would would be incomprehensible by the stupid
humans that are using it. So while for your CPU, creating recognizable
images on the screen is really a side affect, from an information theory
perspective, and from a practical programming perspective, it is the
output. And most of your job as a programmer is to bridge that gap
between your CPU, who's output is a return of "state" information, to
get all of your programs side affects working so that the information
processing generates correct and usable information responses for the
humans communicating with it.
Now if I haven't yet confused you (and go back and reread this if I
did), data must be understood by your computer and by you. The data
that goes into your program must be understood by your computer and the
data that is outputted must be understood by you. And the side affects
of this process have to be correct as well. In fact, more often than
not, the side affects is everything because the only information your
program might be returning to you is, "OK - I'm finished - awaiting more
instructions". We'll return to these principles over and over again.
In order to help bridge the human digital divide, so to speak, your
computer handles data, and in modern machines, that data is based on the
8 bit Byte. The simplest computer that individuals come in contact with
is the simple light switch. It does everything your computer does. You
have a simple switch with a bulb attached to it. Your turn the switch
on (standard input). Your switch allows electricity to flow to the build
and creates light and heat (side affect), and the switch records its
"state", which is ON...otherwise it wouldn't be very useful. And as
long as the system is working, it retains its state until you give it
more information (turn it off in this case). Now in this system, you
have one bit of information that can be processed. The light is either
ON or OFF. There is nothing else you can do. But if we have 2 switches
connected to a light bulb, we can engineer a system with up to 4 states.
Off, On Dim, On Brighter, On Brightest, each one representing a
different "state". With eight switches, you can represent 256
states...and THAT is exactly what your digital computer does. Your
hardware and program determines what each of those 256 states can mean.
If you need more that 256 states to implement a side affect, lets say we
have a byte in our hardware that makes out standard output device a
color. 256 colors is nice. The billions of colors that the human eye can
distinguish is nicer. But then we need to string together multiple bytes
to implement that screen. Perhaps, two or even 4 bytes. The same holds
true for numbers. If we are using one byte, that will only allow us to
describe 256 numbers, and in this case, to allow for positive and
negative numbers, one of the bits in our byte usually identifies the
number as positive or negative. -127 to 127. That is called a signed
one byte integer. And unsigned integer can't represent negative
numbers, (one bit in the byte is not used to describe negative or
positive state) and it can represent 0-255.
Now that isn't good enough for banking, science, and most other
applications, although it is fine if we are making a card game or a dice
game, so integers are most often represents by at least 2 bytes, and
often 4 or more. And the ability to do with has to be available to the
hardware and written into the language libraries.
So what data types does C++ give us by default? Well, not colors, we
would have to create that. It gives us the following default data
types, which I'll try to explain.
char - Characters. A representation of standard characters as a single
byte, which is more than adequate for Latin based languages like
English. A data input that is a character is stored in a single byte
which C++ will automatically translate into characters, like A-Za-z and
others as defined in ASCI.
As computers got larger, the ASCII set was extended to into 2 bytes
(ASCII extended) but in C++ a char is a SINGLE BYTE.
int - A one word Integer. Storage is hardware dependent, most normally
in something called a " word" which on the standard 32 bit hardware and
operating systems is 4 bytes (hence 32 bits). Machines are increasingly
64 bite word based hardware and the operating systems are catching up I
would assume that those systems have 8 bytes ints.
Before going forward, it is worth stating that there is a standard for
the C programming language called the ANSI C standard. You have to buy
the standard if you want it and in it has rules for how to define the
limits of datatypes like integers, and these are stored in your C
programming environment in a file called limit.h.which on my system is
located at :
/usr/include/limit.h
Integer sizes aren't nearly as well defined as I would like. The
standard is a typical compromise of different cooperate and academic
interests. In the KN King text, on page 111, the following limits are
outlined on 32 but systems
Integer data types can be defined as extended types with different sizes
as follows:
short int: -32,768 - 32768
unsigned short int: 0 - 65535
int -2,147,483.648 - 2,147,483,648
unsigned int 0 - 4,294,967,295
long int -2,147,483.648 - 2,147,483,648
unsigned long int 0 - 4,294,967,295
These numbers are byte sizes (65535 is the largest number representable
in 4 bytes).
If you have a 64 bit system, see your limits.h file for the definition
on your box.
You can drop the word "int" for longs and shorts.
long int x;
syntactically is the same as
long x;
Although we haven't yet discussed any syntax.
Computers has a special chip to work with factional numbers in decimal
notation (not an easy piece of engineering IMO). C++ has built in data
types to cope with them which have the following minimal and maximum
representations on 32 bit systems:
float 1.17 x 10^-39 - 3.40x10^38 and 6 digit precision
double 2.22 x10^-308 - 1.19x10^308 and 15 digit precision
long double very hardware specific
As I understand it, the numbers are actually stored in scientific
notation which explains a precision of 10^308 is only expressed in 15
accurate digits. These are engineering definition as defined by an
electronics standard called IEEE standard 754...more information than we
usually need. But if you need know what you limits are exactly, see the
float.h file that on my system is located at:
/usr/include/c++/4.4/tr1/float.h
and
/usr/lib/gcc/i586-suse-linux/4.4/include/float.h
There are also manual pages on your Linux system
man float.h
C++ adds one additional data type calls a Boolean type to store true or
false. In C (and in C++), processes that need to test for a true or
false view 0 as false and anything else as true. But there are issues
with that. int's are 4 bytes and signed, chars can actually be singed
and unsigned as well....so C++ adds
bool true or false
which is really like some kind of enum operator (which hasn't been
introduced).
Before exploring how we actually represent data in our C++ programs, I
want to introduce a formal discussion of basic C++ syntax, which, like
data types, doesn't get enough attention in most standardized texts.
All programming languages require syntax rules in order for the
compilers, to parse and create working machine code. These syntax rules
require basic understanding of core components. These components include
files, structure, statements, data and operators.
Starting from the top, first you have files, and files usually have naming
rules. C++ inherits from C nearly all the file structures, and actually
requires a greater knowledge in more detail and at an earlier level of
expertise. C++, because of its object oriented design depends heavely on
library creation. In fact, even for beginners, most of your work happens
on the library level.
All C programs inherit from the Unix enviroment, which co-developed with
C, the need for an initiation of the main function definition. All C
programs start with main, and main will then absorb and use all other
parts of the systems libraries and programming to produce your completed
program. Main is located in your upper most programming file.
Standard Programming Files: A standard programming file is when the top
most programming will take place. In the C language, most of your code,
espeically as a beginner takes place in this file. Most commonly these
files have a suffix of either .cc oro .C. file.C for example is a
standard C++ File name.
A standard programming file will have several components:
1) Include Prepocessor Directives - These import header files and define
the definitions of the symbols which your not spontaneously creating,
that your program will use.
And include directive might look like this:
#include <iostream>
Which tells the compiler to load up the defintions of all the functions
and objects defined in the iostream library.
Standard C++ libraries are included using the angle blacket notions as
above.They are search for by your compiler in a set of standard locations
which are defined by your compiler and programming enviroment (something
I wouldn't mind understanding better on modern Linux and GNU enviroments).
If you use the syntax
#include "myheader"
with double quotes, the compiler will look for these headers in the local
directory.
C libraries are accessable in C++ and can either have a standard C
language notion
#include <assert.h>
#include <stdio.h>
***Note the .h suffix being included***
or use the C++ version
#include <casset>
#include <cstdio>
2) Macro and other Prepocessor Compiler Directives - Help set up
conditions in which libraries and header files are brought into your
program to help prevent duplication and to create different versions of
a program as might be needed for differing architecture or conditions.
The list of Preprocessor Directives are as follows:
#define
#endif
#ifdef
#ifndef
#include (as discussed above)
A Macro directive might look like this:
#ifndef HEAD
#define HEAD
#include <iostream>
#include <string>
#endif
Development of skills using these directives, which is a language in a
language,is one of the skills that advanced C and C++ coders have that
separate them from amateurs.
This Macro is telling the compiler to include the libraries and symbols
for iostream and string from the core C++ library if and ONLY IF, the
symbol
HEAD, in the compiler instructions, haven't been already defined.
There are also constants that your program has which the compiler adds
to your code which include
__cplusplus
__DATE__
__FILE__
__LINE__
__STDC__
__TIME__
__DATE__ and __TIME__ are the date and time the program is compiled.
3) Original Code and runtime directives starting with main.
C++ has added a new programming directive called the "using" directive
which is used to create namespace. Namespace gives a finer grain control
of which symbols your code recognizes in a specified space. Its really
important and in many ways was a long time coming to the C family of
languages. Most importantly it prevents you from accidentally stepping on
library symbols or words that you might not have been aware of or that
programmers after you might not be aware of. It also allows to define
the same symbol in multiple locations of your code without stepping on
your own toes.
So todays modern C++ main program files might look something look
something
like this:
#ifndef TOP_H
#include <iostream>
#define TOP_H
#endif
#ifndef INTARRAY_H
#include "intarray.h"
#define INTARRAY_H
#endif
using namespace std;
int main( int argc, const char* argv[] )
{
//YOUR PROGRAMMIGN CODE
}
There is a catch to the namespace usage though. It might very well be
that your library files, especially if you are creating them yourself,
which you will in C++, have the using directive. If so, you will likely
depend on them.
Header Files:
Header files normally have a .h suffix. file1.h would be an exampe of a
header file for C or C++. These are the files that are being included in
you
#include preprocessor directive.
> There is a catch to the namespace usage though. It might very well be
> that your library files, especially if you are creating them yourself,
> which you will in C++, have the using directive. If so, you will likely
> depend on them.
>
> Header Files:
> Header files normally have a .h suffix. file1.h would be an exampe of a
> header file for C or C++. These are the files that are being included
> in you
> #include preprocessor directive.
>
>
3) Original Code and runtime directives starting with main.
C++ has added a new programming directive called the "using" directive
which is used to create namespace. Namespace gives a finer grain
control of which symbols your code recognizes in a specified space.
Its really important and in many ways was a long time coming to the C
family of languages. Most importantly it prevents you from accidently
stepping on library symbols or words that you might not have been aware
of or that programmers after you might not be aware of. It also allows
to define the same symbol in multiple locations of your code without
stepping on your own toes.
So todays modern C++ main program files might look something look
something like this: #ifndef TOP_H #include <iostream> #define TOP_H
#endif
#ifndef INTARRAY_H
#include "intarray.h"
#define INTARRAY_H
#endif
using namespace std;
int main( int argc, const char* argv[] )
{ //YOUR PROGRAMMING CODE
}
There is a catch to the namespace usage though. It might very well be
that your library files, especially if you are creating them yourself,
which you will in C++, have the using directive. If so, you will likely
depend on them.
Header Files: Header files normally have a .h suffix. file1.h would
be an exampe of a header file for C or C++. These are the files that
are being included in you #include preprocessor directive. These files
are often distributed with a program and you can examine them. They are
useful for discovering the definitions of programing objects in libraries
are used and often programmers will point them out to you as a form of
documentation, which itself is a practice I'm not happy about because
many programmers mistake them as a sunstitute for real documentation.
Library Files: After researching this, it has occured to me that there
is an abiguity about the structure of C and C++ Programming files.
Professional programs generally have header files that are described
above, but don't have a proper name for the coding files that associate
with the headers and which produce object binary files and static or
linked libraries. For a beginner this is all confusing and the lack of
proper nomenclature makes this all the more harder to learn. I little
bit of compiler theory is needed to understand the files structure
and binary construction of your program. For now, I just want to
point out that programming objects defined in your header file for use
in your programming has to have source code to produce the actually
machine code that is represented by the symbols in your header file.
Those library source files will not have the main function. But the
compiler can be asked to create what is called object files, which are
partially processed C binary code for later inclusion in your program.
When we look closer at the gcc compiler we will examine these object
files and learn why they are so important.
What is important to say, however, is that in C++, because of its
object orientation and its emphasis on creating Application Programming
Interfaces (API), most of the C++ coding you will do is taking place in
these library C++ source files (which I will refere to as Library Code
from here on out).
There are two kinds of Library code files that you will work with,
that which you create, and that which you borrow from your system for
inclusion in your programs.
User defined:
User defined library files define the code to create working programming
objects that are normally declared in your matching header files.
These programming source code files look just like your main programming
file except they don't have the main function. Your top most main
programming file is dependent on these library code files. The code
they produce has to be linked into your program by your compiler.
Standard C++ or Packaged third party: These are the standard libraries,
either in source or in object files, that define standard language needs
and are usually found somewhere in /lib or /usr/lib on your system.
Standard C++ File Creation:
All our C++ programs has to be created in with a standard text editor.
The code that the compiler works on, also known as translation units for
the compiler at straight ASCII text. You can NOT use a word processor.
My prefered text editor is VIM or GVIM, which is a derivitive of VI.
VI is the standard text editor on Unix like systems and there are many
tutorials for it around the internet. Other editors include EMACS, and
then there are C++ working environments like Anjuta, which I strongly
discourage. I discourage the Programming Integrated Programming
enviroments because with GNU and Unix like systems, your OS is your
integrated enviroment, and I believe one should learn to use the standard
tools that are on your GNU/Linux system.
A standard C++ file needs to have at least one function defined.
We will look at functions (also called methods, more closely later,
but a new programmer should get use to looking at them from the start,
since everything in C++ is encapsulated in a function called main.
Functions are defined by following structure
"return type" "function name (the symbol)" ( Arguement list) {
Statements that end in semi-colon;
}
Functions do not that semi-colons after the closing curly brace.
The main function looks like this
int main(int argc, char * argv[]){
return 0;
}
A realistic C++ main program file, including prepocessor directives
would look as follows
#include <iostream> using namespace std;
int main(int argc, char * argv[]){
return 0;
}
The curly braces forms a block in which the coder can add really as
many instructions as they choose to. These blocks of statements are
seen in many C++ syntax structures including functions, if statements,
for loops and other structures. While the above is a minimal C++ source
file structure, generally most of the heavey lifting of your code takes
place outside of main in user defined functions which you create, as
well as objects. A more realistic first program skeletan might well
look something like this:
#include <iostream> using namespace std;
void oxygen(){ cout << "oxygen()\n";}
void hydrogen(){ cout << "hydrogen()\n";}
void helium(){ cout << "helium()\n";}
void neon(){ cout << "neon()\n";}
int main(int argc, char * argv[]){
oxygen();
hydrogen();
helium();
neon();
return 0;
}
Here we can see the declaring and defining for 4 user defined functions
that are outside of our program, and get instantated only when called.
the for functions are called read, sort, compact and write.
And notice that we are using the standard namespace called std.
Given the number of errors you teach, it is not really a gift you are
making.
[snip]
> I'm expecting to put out lecture size workshops 2 times a week, maybe
> Mondays and Thursdays. They will be posted to the NYLUG mailing list,
> NYLXS mailing list (hangout), to comp.lang.C++. I'll try to cross post
> to all three mediums,
Please, don't. This is as bad as spam.
[snip]
--
Michael
Statement Structure:
All C and C++ statements (although not all syntax) ends with a semi-colon.
You can even put two semi-colons on a single line, seperated by a
semicolon, but in general this isn't recommend.
Statements are constructed with Data, Operators and Keywords. C++ has
an exented set of Keywords than C.
Keywords:
Keywords are any symbols that the Standard C++ recognizes as having
instructional meaning, that is the tell the compiler to do something.
The Key Words in C++ are as follows, and learning the exact meaning of
all the keywords is essential to learning C++.
These are inhereted from C:
auto const double float int short struct unsigned
break continue else for long signed switch void
case default enum goto register sizeof typedef volatile
char do extern if return static union while
These are the extended set added to C++
asm dynamic_cast namespace reinterpret_cast try
bool explicit new static_cast typeid
catch false operator template typename
class friend private this using
const_cast inline public throw virtual
delete mutable protected true wchar_t
and most C++ Compilers also recognize the follow Keywords
and bitand compl not_eq or_eq xor_eq
and_eq bitor not or xor
Keywords are completely reserved and can not be used as symbols by any
user defined variables in your program. They are exclusive to the
language and compilers.
There are other important predefined symbols that C++ uses as well.
These are not strictly exclusive to the Language, however, overloading
them or using them as symbols for variables is a very bad idea.
There is a lot of them, but some of them might include
cin
endl
INT_MIN
iomanip
main
npos
std
cout
include
INT_MAX
iostream
MAX_RAND
NULL
string
not to mention the Macros like __DATE__ and __TIME__
Operators:
Operators, are very much like functions or methods in that they define
processes, taking in arguments and returning outputs (and having side
affects). In the C Language, Operators are immutable. You can't change
their meaning. In C++ many of them can be overloaded, that is that you
can create, and change their meaning. A lot of C++ study involves
discussing the overloading of Operators.
All Operators, as they do in Mathematics, have precedence and
associativity. For example, in arithmetic:
4 x 3 - 10 = 22
and not -28 or 2. That is because multiplication has a higher
precedence that subtraction and the associativity is left to right. A
complete list of C++ operators is considerable and as follows:
┌────────────────────────┬─────────────────────────────────────────┬────────────────┐
│ Operator │ Type │ Associativity │
├────────────────────────┼─────────────────────────────────────────┼────────────────┤
│ :: │ binary scope resolution │ │
│ :: │ unary scope resolution │ │
├────────────────────────┼─────────────────────────────────────────┤ │
│ () │ parentheses │ │
│ [] │ array subscript │ │
│ . │ member selection via object │ │
│ -> │ member selection via pointer │ left to right │
│ ++ │ unary postincrement │ │
│ -- │ unary postdecrement │ │
│ typeid │ run-time type information │ │
│ dynamic_cast< type > │ run-time type-checked cast │ │
│ static_cast │ compile-time type-checked cast │ │
│ reinterpret_cast │ cast for non-standard conversions │ │
│ const_cast │ cast away const-ness │ │
├────────────────────────┼─────────────────────────────────────────┼────────────────┤
│ ++ │ unary preincrement │ │
│ -- │ unary predecrement │ │
│ + │ unary plus │ │
│ - │ unary minus │ │
│ ! │ unary logical negation │ │
│ ~ │ unary bitwise complement │ │
│ ( type ) │ C-style unary cast │ right to left │
│ sizeof │ determine size in bytes │ │
│ & │ address │ │
│ * │ dereference │ │
│ new │ dynamic memory allocation │ │
│ new[] │ dynamic array allocation │ │
│ delete │ dynamic memory deallocation │ │
│ delete[] │ dynamic array deallocation │ │
├────────────────────────┼─────────────────────────────────────────┼────────────────┤
│ .* │ pointer to member via object │ │
│ ->* │ pointer to member via pointer │ │
├────────────────────────┼─────────────────────────────────────────┤ │
│ * │ multiplication │ │
│ / │ division │ │
│ % │ modulus │ │
├────────────────────────┼─────────────────────────────────────────┤ │
│ + │ addition │ │
│ - │ subtraction │ │
├────────────────────────┼─────────────────────────────────────────┤ │
│ << │ bitwise left shift │ │
│ >> │ bitwise right shift │ │
├────────────────────────┼─────────────────────────────────────────┤ │
│ < │ relational less than │ │
│ <= │ relational less than or equal to │ left to right │
│ > │ relational greater than │ │
│ >= │ relational greater than or equal to │ │
├────────────────────────┼─────────────────────────────────────────┤ │
│ == │ relational is equal to │ │
│ != │ relational is not equal to │ │
├────────────────────────┼─────────────────────────────────────────┤ │
│ & │ bitwise AND │ │
├────────────────────────┼─────────────────────────────────────────┤ │
│ ^ │ bitwise exclusive OR │ │
├────────────────────────┼─────────────────────────────────────────┤ │
│ | │ bitwise inclusive OR │ │
├────────────────────────┼─────────────────────────────────────────┤ │
│ && │ logical AND │ │
├────────────────────────┼─────────────────────────────────────────┤ │
│ || │ logical OR │ │
├────────────────────────┼─────────────────────────────────────────┼────────────────┤
│ ?: │ ternary conditional │ │
├────────────────────────┼─────────────────────────────────────────┤ │
│ = │ assignment │ │
│ += │ addition assignment │ │
│ -= │ subtraction assignment │ │
│ *= │ multiplication assignment │ │
│ /= │ division assignment │ right to left │
│ %= │ modulus assignment │ │
│ &= │ bitwise AND assignment │ │
│ ^= │ bitwise exclusive OR assignment │ │
│ |= │ bitwise inclusive OR assignment │ │
│ >>= │ bitwise left shift assignment │ │
│ <<= │ bitwise right shift with assignment │ │
├────────────────────────┼─────────────────────────────────────────┼────────────────┤
│ , │ comma │ left to right │
└────────────────────────┴─────────────────────────────────────────┴────────────────┘
We will walk through this complete list of operators later.
Are you in the process of learning this stuff yourself, or is this a
rough draft you expect us to help you to correct? Knowing how many
bits are in things like a byte and how binary numbers are represents
in computers is pretty basic programming knowledge. Are those numbers
really the ones that are given in the book? From inspection and
without working out what 2^31 or 2^32 actually is I can see that the
signed ranges are all wrong for 16 and 32 bits.
65535 is the largest number representable in 2 bytes (16 bits, and
65535 is 2^16-1 so 16 bit number can store from 0 to 65535 which is
65536 numbers which 2^16) using an unsigned integral type. Which of
the unsigned integer types is 2 bytes is implementation defined, and
in fact there is no requirement that one even be present.
> Computers has a special chip to work with factional numbers in decimal
> notation (not an easy piece of engineering IMO)
Presumably you mean binary notation here? And not all computers have
an FPU. Floating numbers and fractional numbers aren't really the same
thing at all either.
> float 1.17 x 10^-39 - 3.40x10^38 and 6 digit precision
> double 2.22 x10^-308 - 1.19x10^308 and 15 digit precision
> long double very hardware specific
>
> As I understand it, the numbers are actually stored in scientific
> notation
You should probably actually know and understand this if you plan on
teaching it.
> C++ adds one additional data type calls a Boolean type to store true or
> false. In C (and in C++), processes that need to test for a true or
> false view 0 as false and anything else as true. But there are issues
> with that. int's are 4 bytes and signed, chars can actually be singed
> and unsigned as well....so C++ adds
>
> bool true or false
>
> which is really like some kind of enum operator (which hasn't been
> introduced).
Ummm.... no comment
I've not read the rest of it, but if this part is any guide it looks
like it still needs a lot of work to get it into any sort of shape
where it will be a good resource for other people to learn from.
As it stands I'm sure you will learn a lot in getting this stuff
correct and to a standard where other people can make use of it.
K
> Are you in the process of learning this stuff yourself, or is this a
> rough draft you expect us to help you to correct? Knowing how many bits
> are in things like a byte and how binary numbers are represents in
> computers is pretty basic programming knowledge.
Not for beginners. For beginners it is a complete mystery, especially in
adult education.
> Are those numbers
> really the ones that are given in the book? From inspection and without
> working out what 2^31 or 2^32 actually is I can see that the signed
> ranges are all wrong for 16 and 32 bits.
That is from King, C Programming: A Modern Approach ISBN 0393969452
page 111, one of about 8 resources I'm using.
>> Computers has a special chip to work with factional numbers in decimal
>> notation (not an easy piece of engineering IMO)
>
> Presumably you mean binary notation here? And not all computers have an
> FPU. Floating numbers and fractional numbers aren't really the same
> thing at all either.
BTW - I couldn't find much with regard to understandable information on
float point processors around. I know that in the old days hackers
literally ripped them out of calculators and installed them on main
boards. There is an IEEE Floating Point standard which I did some
searching on but I couldn't find at least one resource I was looking for
by David Goldberg. As I understand the representation, the exponent by
the standard for single precision is 8 bits long and the fraction
occupies 23 bits. But my general understanding of floating points
representation isn't as clear as I'd like and if you have a reference for
more background I'd be interested in reading it.
Ruben
> As it stands I'm sure you will learn a lot in getting this stuff correct
> and to a standard where other people can make use of it.
Its really an informal workshop, but I am trying to edit it to be a more
permanent reference and constructive criticism, and even nonconstructive
criticism is read and reviewed.
FWIW, I'm really unhappy with the level of education on Programming in
the University level and otherwise. What my kids learn is really useless
and the students I've had are not being given a broad enough
understanding of programming and Comp Sci. I can tell you some stories,
but I'm sure everyone has run into this. I'm hoping to just try to work
with people to hammer out a better overall learning construction and to
help a few people along the way.
Ruben
From which source did you get ?
<quote - your post 08 Feb 2010>
C++ has added a new programming directive called the "using" directive
which is used to create namespace.[..]
</quote>
--
Michael
> As I understand the representation, the
> exponent by the standard for single precision is 8 bits long and the
> fraction occupies 23 bits. But my general understanding of floating
> points representation isn't as clear as I'd like and if you have a
> reference for more background I'd be interested in reading it.
Lippman, Lajoie: C++ Primer 437-440 ISBN 0201824701
Would you like a complete bibiography? I was thinking of writing one up
anyway.
Ruben
Thanks. I was looking for something more credible at this point than
wikipedia. Just my own opinion, but I really really finished with
Wikipedia.
Ruben
You are of course entitled to your opinion. So take Wikipedia out of that
http://lmgtfy.com/?q=floating+point
... and look at the hits. We have on the first page:
Wikipedia (I checked, that's one of the better articles BTW)
Princeton
U. of Utah
Sun Microsystems (I read that one BTW and it's pretty good)
There must be something there you think is credible.
Andy
I see where the mistake comes from, an unfortunate wording I guess.
IIRC Lippman says that a using directive *open* the namespace (in the
same meaning as "open a can", I guess).
In your wording, it is even worse. The using directive doesn't create
anything; it makes names visible in the scope.
> Would you like a complete bibiography? I was thinking of writing one up
> anyway.
Not really. The important part is not in the book.
--
Michael
Perhaps, but if the term directive is what is bothering you specifically
with regard to "using", the text says specifically,
"Using directives also influence how we compose the set of candidate
functions. Let's assume that we decide to use a using directive instead
of a declaration to make the function max()"
and on pg 437 Section 8.6.3 - Using Directives
"Namespaces were introduced with Standard C++. Prior C++ implementations
did not support namespaces, and, as a consequence, pre-Standard libraries
did not wrap their global declarations in namespaces. An important
amount of C++ code and many applications were written before namespaces
and became available with various C++ implementations."
He later writes, "We can use using declarations to make visible the
library names that our programs use." So under the heading called "Using
Directives" he calls them "using declarations".
And then later writes:
"A using directive begins with the keyword using, followed by the keyword
namespace, followed by the namespace name".
and
"A using directive makes the namespace member names visible as if they
were declared outside the namespace at the location where the namespace
definition is located"
So "using", according to Lippman, is a directive. Further, it is listed
in the index as such.
What bothers me is "create namespace". 'using' doesn't *create*
namespace.
In all cases 'using' only add/resolve name for unqualified lookup in
the enclosing scope (file, namespace, class, block ...).
> [snip: quote from Lippman]
> So "using", according to Lippman, is a directive. Further, it is listed
> in the index as such.
Both exists (and are listed in the standard):
- the using declaration (§7.3.3): introduce a name (it is a
declaration)
- the using directive (§7.3.4): add a namespace content in current
scope (like an include but for namespace)
--
Michael
Oh that is good. Do you have a URL for the whole standard? I was
reviewing scope before writing it up and I got hung up on the
relationship with namespace and scope in C++, which prior to writing
this, is something I thought I understood quite well. A look at the
standard would be very helpful.
Lippman says there are three kinds of scope, global, local and
namespace. Lippman then discusses the creation of user defined
namespaces. Now, traditionally, I've thought of namespace and scope as
two distinct properties of a program, and the programming concepts are
similar in TCL, Perl and Python. But Lippman is meshing the two together.
And then there are the implications of Scope on Linkage and Compiling,
which I wanted to explain as clearly and concisely as I can, as this is
often hopped over when teaching to new users. There is also a nice text
by Tmothy Budd, "Data Structures in C++: Using the Standard Template
Library" which has a nice, concise and clear discussion of the run time
stack, which I also want to introduce.
Ruben
> Oh that is good. Do you have a URL for the whole standard? I was
> reviewing scope before writing it up and I got hung up on the
> relationship with namespace and scope in C++, which prior to writing
> this, is something I thought I understood quite well. A look at the
> standard would be very helpful.
>
The Standard is not freely available, it must be purchased. You can
find some of the pre-standardization drafts online.
See FAQ 6.13
http://www.parashift.com/c++-faq-lite/big-picture.html#faq-6.13
> Oh that is good. Do you have a URL for the whole standard? I was
> reviewing scope before writing it up and I got hung up on the
> relationship with namespace and scope in C++, which prior to writing
> this, is something I thought I understood quite well. A look at the
> standard would be very helpful.
>
The Standard is not freely available, it must be purchased. You can
chars: Characters are the most basic data type in C and C++ being
stored in memory in a single signed or unsigned byte.
chars can be represented in several ways, and internally are represented
as small ints, either signed or unsigned. We can create character
constants by putting keyboard characters into single quotes ==> 'B' and
assigned them to either char, unsigned char, or pointer indirectly
by addressing to char variable or some other memory construction that
stores a char or unsigned char.
Characters can be represented in programming code, not only with the
char var = 'X';
syntax, but also by Integer, Octal Code and Hexadecimal code which
represent ASCII mapped characters. The syntax for these codes includes
the following:
Integer constants: (note the single quotes)
char letter = 65; //stores an ASCII A.
char letterOct = '\102';
char letterHex = '\x43';
char letterOct = 0102;
char letterHex = 0x43;
Octals have the generic form of 3 digits preceded by a backslash if
quoted, but the integer syntax can be used. And integer that starts
with a 0 is interpreted as non-decimal. Remember that you feel more
comfortable with decimal numbers, but the machine couldn't care less.
If the 0 is followed by an x or X, then it is hexadecimal. If you put
it in single quotes, it needs a backslash first.
Their is also a short hand for special characters that can not be
readily typed from a US 105 key keyboard. These are also backslashed.
I'll show a complete list in the code example, but the most important
two, by far, are '\n', the line feed, and '\t' the horizontal tab. The
'\n' can also be represented in C++ with the endl symbol (which stands
for end of line). I'm not going to discuss the broken MS end of line.
A character literal can be also have an 'L' in front of it to use for
double wide characters, such as in Chinese etc. It has to be stored in
an appropriate variable called a wchar_t.
You can't use any of these literal representations on the left side of
an assignment operator. For some, this might seem obvious that you can
do this assignment:
'0' = 'G';
but trust me, some day you will do just this for some twisted reason.
Here is a sample program to show all the character variations:
Three Files: First the Header File
http://www.nylxs.com/docs/workshops/cpp/data.h.html
http://www.nylxs.com/docs/workshops/cpp/data.h
1 #ifndef DATA_H
2 #define DATA_H
3 #endif /* DATA_H */
4
5 void show_chars();
6 void show_ints();
7 void show_floats();
8 void show_arrays();
9 void show_cstrings();
10 void show_strings();
11
The Library Source File:
http://www.nylxs.com/docs/workshops/cpp/file_1.cc.html:
http://www.nylxs.com/docs/workshops/cpp/file_1.cc
1 #include <iostream>
2
3 using namespace std;
4
5
6 void show_chars()
7 {
8 //declare and define char types in a function
9 char letter;
10 unsigned char letterU;
11 //assigning chars means using a single quote mark
12 letter = 'R';
13 letterU = 'u';
14 //declare,define and assign
15 char letterNull = 0;
16 char letterZero = '0';
17 char letterINT = 65;
18 char letterAlert = '\a';
19 char letterBackspace = '\b';
20 char letterFormFeed = '\f';
21 char letterNewLine = '\n';
22 char letterCarriageReturn = '\r';
23 char letterVertTab = '\v';
24 char letterHorzTab = '\t';
25 char letterBackslash = '\\';
26 char letterQuestionMark = '\?';
27 char letterSingleQuote = '\'';
28 char letterDoubleQuote = '\"';
29 char letterOct = '\103';
30 char letterHex = '\x44';
31 char letterOct2 = 0104;
32 char letterHex2 = 0x45;
33 //depreciated and causes a segfault char * letterptr = "\x48";
34 cout << "signed char ==> " << letter << endl;
35 cout << "unsigned char==> " << letterU << endl;
36 cout << "NULL char ==> " << letterNull << endl;
37 cout << "ZERO char ==> " << letterZero << endl;
38 cout << "INT char ==> " << letterINT << endl;
39 cout << "Alert char ==> " << letterAlert << endl;
40 cout << "Backspace char ==> ::" << letterBackspace << "end" << endl;
41 cout << "FormFeed char ==> " << letterFormFeed << "end" << endl;
42 cout << "New Line char ==> " << letterNewLine << "end" << endl;
43 cout << "Carriage Return char ==> " << letterCarriageReturn << "end" << endl;
44 cout << "Verticle Tab char ==> " << letterVertTab << "end" << endl;
45 cout << "Horizonal Tab char ==> " << letterHorzTab << letterHorzTab << "end" << endl;
46 cout << "Backslash char ==> " << letterBackslash << endl;
47 cout << "Question Mark char ==> " << letterQuestionMark << endl;
48 cout << "Single Quotes char ==> " << letterSingleQuote << endl;
49 cout << "Double Quote char ==> " << letterDoubleQuote << endl;
50 cout << "Octal char ==> " << letterOct << endl;
51 cout << "Hexidecimal char ==> " << letterHex << endl;
52 cout << "Octal char 2 ==> " << letterOct2 << endl;
53 cout << "Hexidecimal char 2 ==> " << letterHex2<< endl;
54 // cout << "NOT REALLY a pointer to char but a string ==> " << *letterptr << endl;
55
56 //can't asign a value to a string literal or const char: *letterptr = 'd';
57 // cout << "pointer to char ==> " << *letterptr << endl;
58
59
60
61 }
62
63 void show_ints()
64 {
65 }
66
67 void show_floats()
68 {
69 }
70
71 void show_arrays()
72 {
73 }
74
75 void show_cstrings()
76 {
77 }
78
79 void show_strings()
80 {
81 }
82
The Main programing file:
http://www.nylxs.com/docs/workshops/cpp/file_1.cc.html
http://www.nylxs.com/docs/workshops/cpp/file_1.cc
#include <iostream>
#include "data.h"
int main(int argv, char * argc []){
show_chars();
show_ints();
show_floats();
show_arrays();
show_cstrings();
show_strings();
}
and the Makefile to compile
http://www.nylxs.com/docs/workshops/cpp/makefile.html
http://www.nylxs.com/docs/workshops/cpp/makefile
data : data.o data_main.o
g++ -o data data.o data_main.o
data.o : file_1.cc data.h
g++ -Wall -o data.o -c file_1.cc
data_main.o : file_1_main.cc
g++ -Wall -o data_main.o -c file_1_main.cc
Just to say it, there is some differences in the character handling in
modern C++ and C, specific to pointers. This syntax, which is almost
always wrong
char * ptr = "A"; //Double QUOTES THERE
needs to specify itself as a const
const char * ptr = "A";
and you should be aware that the double quotes is not a character, but a
string of the size of 2 chars, a null char is implied, something we will
:be exploring more closely in the near future.
Furthermore, there is no direct way to assign the address of a literal
char to a pointer.
char * ptr = 'A'; // Wrong
char * ptr = "A"; // Wrong and depreciated and a string
You can do this:
char letter = 'A';
chat * ptr = &letter; //we will look at this syntax when looking at
//pointers in full
Workshop Assignment: Print Out a complete set of ASCII chars and the
decimal and numbers associated with them. You can use a for loop
int i = 0;
for( i = 0; i < 127; i++){
//you code in here
}
and then using unsigned chars, for fun, extend it to a complete set of
256 chars.
C++ Integer types - Data Representation
Constant and Literal Integers are represented as numbers without any
quotes. They can be represented in decimal, octal, and Hexadecimal
forms. They can be assigned to int, short, long and signed and unsigned
variables and they are literal, and therefor can not be left values: ie
values that go on the left of an assignment operator.
Decimal Forms look like these examples:
int i = 255;
short i = 255;
long i = 4000;
unsigned short = 32;
signed int = -273;
Octal examples are similar to the integers that we saw with chars and
begin with zeros:
int o = 042;
short o = 0101;
long o = 0076;
unsigned o = 0101;
singed long o =011102;
Hexadecimal examples begin with x or X and again are without quotes:
int h = 0xFF;
short = 0x12;
long = 0xA2E44D;
unsigned = 0xA2;
signed long = 0xAF23E4;
Integers also have the ability to be expressed as Unsigned or Long
literal values, if the need arises to do so.
unsigned regist = 121U;
long lightspeed = 123456789L;
#define MAXVAL 1234567U
While discussing integer values, the programmer also needs to be aware
of the size_t typedef that C and C++ uses for the sizeof() operator.
The sizeof() operator returns the size of any data object in size of
bytes. Its return value is an integer of type size_t. Because in C
and C++ we manage memory directly, the sizeof() operator plays a significant
role in your programming. And example of sizeof() is:
size_t i = sizeof(oint);
One of the unusual properties of integers and chars is that both represent
real integer values. As a result, many of the mathematical operators can be
used with them and they can be assigned to each other. There are automated
rules for "recasting" the data types as they interact with each other. We'll
look at this rules in detail later. But for now one should be aware
that statements theses are common in C and C++.
char letter, letter2;
int number;
short num2;
long num3;
letter = 'c';
letter2 = 'G';
letter++; //now stores 'd'
cout << letter << " " << endl; //prints 'd' and a line feed
num2 = letter;
cout << num2 << " " << endl; //prints '100' and a line feed
cout << letter + leter2;
Floating Point
Floating Point data essentially can be represented in the two styles
already discussed, as decimal and scientific notation. Decimal notation
can be followed by an 'F' or 'f' for single precision or a double
precision with an 'L' or 'l' (not a 'D' for double). Here are some
examples:
double avog = 6.23xE23;
float pizza = 0.125;
float trip = 102.7F
float population = 8323456L;
float pop_brklyn = 8.32xE6;
Casting:
C and C++ are typed languages, but they have some flexibility built into
their design in this regard. This can be good and bad, because this
also means that the language will give you room to hang yourself if you
don't learn the explicit rules for the accommodations that C++ will make
for you. For example, one can assign a float into an integer - but then
you are left to understand what the resulting outcome is. And these
are very difficult bugs to catch because the code looks correct, seems
logical, and it is a raw syntax error.
For example, what does this legal code do?
#include <iostream>
#include <cmath>;
int a,b,c,z;
float d,e,f;
a = 1.25;
b = 2.50;
c = 5;
d = 0.6125;
e = 0.30625;
f = 0.153125;
z = 25U + 75;
z = z * a;
cout << b/2 << endl;
z=((pow(b,2)) + e)/((pow(f,e)) * z);
cout << "I have no clue what this results in and can't be paid enough to
debug it " << z << endl;
Rules for Casting:
Implicit Rules:
When the compiler is confronted with two different data types, it tries
to work operations by casting one data type to another which is
compatible to the expression. The programmer can also manually do such
casting. The Implicit casting rules are as follows:
There are 4 events that trigger C and C++ to do implicit type
conversion:
A) When the operants in a mathematic or logic expression are of two
different type: example
char a = 'A';
int b = 9;
long c;
c = a + b;
if(c == b){
//do something
}
B) When the assignment on the right side of an equation doesn't match
the type of the variable or lvaue on the left: example
char a = 'a';
double b;
b = c;
C) When the argument to a function or method doesn't match the parameter.
We will see this when we look at functions.
D) When the return statement doesn't match the function type. Again, we
will see this when we examine functions.
Rules:
Generally the implicit cast rules are designed to loose the least about of
precision possible. If an arithmetic operation includes floating
point data then the implicit casting of data follows the following rules
float --promotion-->double-->long double
Neither operand in an arithmetic operation has a float:
int-->unsigned int-->long-->unsigned long : Note that these promotions
can lose there sign (lose negativity). I also not that when attempting
to verify these promotion rules in C and in C++, that they don't hold.
I can not create an example program that will promote the signed data
type to an unsigned one, and to then lose the sign.
According to Lippman the C++ rules for implicit promotion in C++ is as follows:
In arithmetic operations involving binary operators and data of mixed
type will convert to the widest data type present. All arithmetic
expressions involving types less wide that an INT are promoted to an INT
before processing. When data is present in the expression as a long
double, everything is converted to long double's....
otherwise, if neither is a long double and one is of type
double, then everything is converted to double...
otherwise, if neither is a double, then if one type is a
float, everything is converted to a float...
Otherwise, when there are no floats involved,
then integer promotion is evaluated. At the beginning of evaluation all
integers small than a INT is promoted to an INT. Unsigned short ints are
promoted to ints as well unless they can't fit, then they are promoted to
unsigned ints. Now, after the floats are done being evaluation the
larger ints are evaluated for promotion. If there is an unsigned long,
all are converted to unsigned long (not the loss of negative values)...
Otherwise, if there is no unsigned
longs, and we have a long, the others are converted to long, and an
unsigned int is converted to long if it is large enough to hold the
bytes, otherwise it is converted to a unsigned long ...
Otherwise...if there is an
unsigned int, then everything is promoted to unsigned it.
Again, I will repeat that I have not been able to confirm the documented
implicit conversion in arithmetic operations in cases where sign is lost
(conversions from unsigned ints to long for example), unless the value
is being assigned to an unsigned variable.
Explicit Cast:
For a variety of reasons, one might need to cast data intentionally.
There are two styles to do this, the older C style and the newer C++
standard. First the new style.
The kinds of New Style Casting:
static_cast, dynamic_cast, const_cast, and reinterpret_cast. Syntax for
these casts follows the following conventions:
int int_variable = static_cast<int>(char_variable);
cast_name<type>(variable) in the general form. I'm not going to yet
explain the differences at this point, but will come back to it soon
enough. I will say that the result is to forcefully convert the data
from one type to another, in the case above, from a char to a int.
In the C style, parenthesis are used to make the cast:
char letter;
int var = (int) letter;
casts the value of letter to an int.
Aggregate Data Types:
Most of the action involving your program will involve more than a single
indepent integer, char or float. Groups of data types together creates
most of the useful. C and C++ gives multiple tools for handling these
agregate data types. The key element is the C style array. An array's
syntax is declared, defined and assigned like the elementry data types,
and looks like this, using the square bracket operator:
char mystraing[]; // Declares an array of chars without dimension
char mystring[100]; //Declares an array of chars with 100 chars within
//it
char * mystring[]; //Declares an array of pointers to chars similar to
//the paramenter of main char * argv[];
One can assign and declare your array with a single statement. When
doing so, C and C++ has several syntax tools to help you create many
necessecary subtle data contructions that you need for your programming.
The comments below outlines these examples and behaviors.
char mystring[] = "This is our first string"; //Declares a char array of
//27 chars which is terminated with a null value
char mystring[] = {'a','b','c','d','e'}; //Creates an array of 5 chars.
int matrix[100] = {1.2,3,4,5}; //This creates an array of 100
//integers filling the first 5 locations with 1,2,3,4,5
//and then adds 0's or NULLS to the remaining 95 indexed
//locations
int matrix[100] = {'1'.'2','3','4','5'}; //This creates an array of 100
//integers where the equivilent of the short
//intergers which represent the ascii values for
//the characters '1' and '2' etc, and then fills
//the rest of the array with zeros. It is
//similar to the next statement (but not
//exactly)
char matrix[100] = "12345"; // This example creates a string literal
//"12345" which ends in a null, and then
//pads the rest of the array with nulls. The
//result is the same as above, but via a
//different mechanism because all string
//literals end in null. The above examples
//has implicit promotion from char to integer
//types. This example must be a char type,
//otherwise the the compiler will not accept
//the assignment. Furthmore, only the care
//type will print a string when asked. The
//top example needs an explicit cast. See
//and thry this example for a demonstration.
#include <iostream>
using namespace std;
int main(int argc, char * argv[]){
unsigned short int matrix[100] = {'1','2','3','4','5'};
char matrix2[100] = "12345";
cout << "First Martix "<< matrix << endl;
cout << "Second Matrix " << matrix2 << endl;
for(int i=0;i<5;i++){
cout << matrix[i] << endl;
}
for(int i=0;i<5;i++){
cout << static_cast<char>(matrix[i]) << endl;
}
return 0;
}
ruben@www2:~/cplus> g++ -Wall test.cc -o test.bin
You have mail in /var/mail/ruben
ruben@www2:~/cplus>
ruben@www2:~/cplus> ./test.bin
First Martix 0xbfc98c3c
Second Matrix 12345
49
50
51
52
53
1
2
3
4
5
ruben@www2:~/cplus>
Notice that the second matrix prints a seemingly random number. That
number is actually the memory address that matrix points at. It acts
like a pointer in the context of cout. The for loop itself will be
looked at more closely when we discuss flow control operators.
We can not mix data types in an array. An array is defined by as a
single data type only.
Arrays are indexed starting with zero. You have to know the size of
your arrays, otherwise you can walk past the end of them into the
undefined sections of your memory. Usually this will cause a
segmentation fault, but not always. Arrrays have syntax that allow them
to be converted to pointers. Pointers is the next section, after we
look at arrays, and we wil look closely at pointers and arrays at soon.
Arrays can have two dimensions like this:
float matrix[4][7];
That declares an array of 4 columns of nine rows (c before r),
for example, we can initialize such an array like this:
float matrix[4][7] = {
{ 2.11, 2.22, 2.33, 2.44, 2.55, 2.66, 2.77 },
{ 3.11, 3.33, 3.33, 3.44, 3.55, 3.66, 3.77 },
{ 4.11, 4.44, 4.33, 4.44, 4.55, 4.66, 4.77 },
{ 5.11, 5.55, 5.33, 5.44, 5.55, 5.66, 5.77 }
};
or you can drop in inside curly braces and the compiler will do the
rest..
float matrix[4][7] = {
2.11, 2.22. 2.33, 2.44, 2.55, 2.66, 2.77 ,
3.11, 3.33. 3.33, 3.44, 3.55, 3.66, 3.77,
4.11, 4.44. 4.33, 4.44, 4.55, 4.66, 4.77 ,
5.11, 5.55. 5.33, 5.44, 5.55, 5.66, 5.77
};
Although we stupid humans conceptualize this as columns and rows, in RAM
this is stored as a single linear block of memory.
There are alot of minefields with two dimensional arrays, and this
program shows some of them:
#include <iostream>
using namespace std;
int main(int argc, char * argv[]){
unsigned short int matrix[100] = {'1','2','3','4','5'};
char matrix2[1000] = "12345";
float dmatrix[4][7] = {
{ 2.11, 2.22, 2.33, 2.44, 2.55, 2.66, 2.77 },
{ 3.11, 3.33, 3.33, 3.44, 3.55, 3.66, 3.77 },
{ 4.11, 4.44, 4.33, 4.44, 4.55, 4.66, 4.77 },
{ 5.11, 5.55, 5.33, 5.44, 5.55, 5.66, 5.77 }
};
float * track;
cout << "First Martix "<< matrix << endl;
cout << "Second Matrix " << matrix2 << endl;
for(int i=0;i<5;i++){
cout << matrix[i] << endl;
}
for(int i=0;i<5;i++){
cout << static_cast<char>(matrix[i]) << endl;
}
for(int i=0;i<100;i++){
cout << &matrix[i] << endl;
}
for(int i=0;i<5;i++){
cout << "STRING " << reinterpret_cast<int *>(&matrix2[i]) << endl;
}
track = *dmatrix;
float * last = &dmatrix[3][6];
for(int count = 0; track <= last; track++){
cout << "Position ==>" << count++ << "\tMemory Location==>"<<track << "\tValue==>" << *track <<endl;
}
return 0;
}
A particularly special array is the charater array, which in C forms the
basis for strings. We already know that a single char is a C and C++
built in data type and we can have an array of chars, and lastly that we
can have string literals, which are constant. For review, lets look at
code examples of each:
Example A:
char car = 'A'; //a single character assigned to a char variable. Note
//the single quote
Example B:
char cararray[] = {'A', 'B', 'C', 'D'}; // The definition and assignment An array of 4 chars,
//which got it's size with the
//initialization of the array
//and of which each of which
//element can be accessed
//through indexing ie:
//char b = cararray[3] or
//pointers such as
//char b = *(cararray + 3)
Example C:
const char *stringo = "ABCD"; //This is the assignment of a string
//constant literal to a pointer to a
//char constant. This is a real string
//that differs from the above example
//because it creates an array of chars,
//not 4 chars long but 5 chars long
//because it appends a NULL character
//to the end
Example D:
char[] = "My Dog Has Fleas\n";//similar to above with 19 char
//assigned to the array ending with
//a NULL char but not a constant
//literal
There are some important but subtle differences between "true" string
literals and strings formed by manually creating arrays of chars as
shown in the technique of Example B and Example D. We can see an
example of this difference in the following code.
Make a new directory and in GVIM or the editor of your choice create the
following files:
test.cc
------------------------------------------
#include <iostream>
#include "test.h"
using namespace std;
int main(int argc, char * argv[]){
stringexample();
return 0;
}
----------------------------------------------
test.h
----------------------------------------------
#ifndef TEST_H
#define TEST_H
#endif /* TEST_H */
void stringexample();
--------------------------------------------------
string_ex.cc
--------------------------------------------------
#include <iostream>
#include "test.h"
using namespace std;
void stingexample(){
char test[] = "My Dog has Fleas\n";
const char * test2 = "My Dog has Fleas\n";
cout << test;
test[3] = 'C';
test[4] = 'a';
test[5] = 't';
cout << test;
cout << test2;
const_cast<char>(test2[3]) = 'C';
const_cast<char>(test2[4]) = 'a';
const_cast<char>(test2[5]) = 't';
cout << test2;
}
--------------------------------------------------
test.cc
--------------------------------------------------
#include <iostream>
#include "test.h"
using namespace std;
int main(int argc, char * argv[]){
stringexample();
return 0;
}
----------------------------------------------------
and create the following makefile
----------------------------------------------------
test.bin : test.o string_ex.o
g++ -Wall -o test.bin test.o string_ex.o
test.o : test.cc
g++ -Wall -c test.cc
string_ex.o : string_ex.cc test.h
g++ -Wall -c string_ex.cc
----------------------------------------------------
Note that the makefile MUST have those TABS and not spaces
Then run 'make'
gcc gives you the following output
g++ -Wall -c string_ex.cc
string_ex.cc: In function ‘void stingexample()’:
string_ex.cc:15: error: assignment of read-only location ‘*(test2 + 3u)’
string_ex.cc:15: error: invalid use of const_cast with type ‘char’, which is not a pointer, reference, nor a pointer-to-data-member type
string_ex.cc:16: error: assignment of read-only location ‘*(test2 + 4u)’
string_ex.cc:16: error: invalid use of const_cast with type ‘char’, which is not a pointer, reference, nor a pointer-to-data-member type
string_ex.cc:17: error: assignment of read-only location ‘*(test2 + 5u)’
string_ex.cc:17: error: invalid use of const_cast with type ‘char’, which is not a pointer, reference, nor a pointer-to-data-member type
make: *** [string_ex.o] Error 1
This is a very useful error message and the GCC compiler is now taking
the programming to school. Lets look at the complaints of the compiler
about our code. The first problem gcc makes is about line 15 in
string_ex.cc which is this line:
const_cast<char>(test2[3] = 'C');
The compiler is telling us that array (or string) and test2 points to is
read only. That variable is defined on line 8:
const char * test2 = "My Dog has Fleas\n";
It is obvious from the code that the data is defined as a "const", less
obvious is that the compiler will complain and refuse to compile if you
do NOT make test2 a "const". Because of the assignment of the string
literal to the char pointer, it must be a const. Therefore, we tried to
cast the const away with const_cast<char>, and that fails as well
because, as the compiler says to us:
invalid use of const_cast with type ‘char’, which is not a pointer,
reference, nor a pointer-to-data-member type. We can not just cast away
to constantness of the string literal assigned to test2.
So again we see that arrays and pointers have differences, and arrays
and strings have even great differences. The standard iostream object
"cout" will recognize both as strings for printing to standard output.
Incidentally, we can compile this substitution for string_ex.cc
--------------------------------------------------
#include <iostream>
#include "test.h"
using namespace std;
void stringexample(){
char test[] = "My Dog has Fleas\n";
const char * test2 = "My Dog has Fleas\n";
char * test3;
cout << test;
test[3] = 'C';
test[4] = 'a';
test[5] = 't';
cout << test;
cout << test2;
test3 = const_cast<char *>(test2);
test3[3] = 'C';
test3[4] = 'a';
test3[5] = 't';
cout << test2;
}
---------------------------------------------
but it creates a segmentation fault on the line:
test3[3] = 'C';
if you add the compiler options -ggdb to your g++ commands in your
makefile, you can trace this error. This UNDERSCORES how dangerous that
explicit casting can be, even when allowed.
size_t i = sizeof(oint);
num2 = letter;
cout << letter + leter2;
Floating Point
#include <iostream>
#include <cmath>;
int a,b,c,z;
float d,e,f;
Rules for Casting:
Implicit Rules:
Rules:
Explicit Cast:
float matrix[4][7];
The difficulty in this example code is that the symbol for a
multidimensional array is often thought to be equal to a pointer.
It isn't. Array names are implicitely converted to pointers
as needed by the compiler. But with a multi-dimensional array, the
symbol converts to a ***pointer to an array of some type***, which is
not specifically the same as a pointer to a pointer, or even a pointer
to the beginning address of the two dimensional array. So examining the
statement:
track = *dmatrix;
We are attempting to aquire the first address of the entire
multi-dimensional array. The safest and most obvious way of doing this
is to just index the first position and request the address. So a
functionally equivilent statement is as follows:
track = &dmatrix[0][0];
There is a difference, however, between the two statements. In the
first statement *dmatrix is processing this statement in the following
order:
1) First dmatrix is evaluated, which is an a two dimensional array.
2) The compiler implicitly converts the array symbol dmatrix to a
pointer to an array and returns the address of the first element of the array
and its type. This first element is a pointer to an array of floats, not a pointer
to a pointer of floats. Though similar, they are not the same. ,
The returned address is for the 1st element of the first
array, the column is an array pointer to the second array, the row.
So it returns a pointer to an array of floats.
3) Then this pointer to an array of floats address is derefenced because of the
(*) operator to the value of the first position of the first array in
the second dimension, the row array.
3) The compiler then again implicitedly converts that
array value to the pointer of the address of the first float element of the
second dimension.
a third way of gaining this function is as follows:
track = dmartix[0];
If your not convinced that the array symbol and the pointer symbol are
not the same, use the sizeof operator to return the size of each, and
you will see the compiler clearly knows when it is dealing with an array
pointer rather than a pointer to a data type.
#include <iostream>
using namespace std;
int main(int argc, char * argv[]){
unsigned short int matrix[100] = {'1','2','3','4','5'};
char matrix2[1000] = "12345";
float dmatrix[4][7] = {
{ 2.11, 2.22, 2.33, 2.44, 2.55, 2.66, 2.77 },
{ 3.11, 3.33, 3.33, 3.44, 3.55, 3.66, 3.77 },
{ 4.11, 4.44, 4.33, 4.44, 4.55, 4.66, 4.77 },
{ 5.11, 5.55, 5.33, 5.44, 5.55, 5.66, 5.77 }
};
float * track;
cout << "First Martix "<< matrix << endl;
cout << "Second Matrix " << matrix2 << endl;
for(int i=0;i<5;i++){
cout << matrix[i] << endl;
}
for(int i=0;i<5;i++){
cout << static_cast<char>(matrix[i]) << endl;
}
for(int i=0;i<100;i++){
cout << &matrix[i] << endl;
}
for(int i=0;i<5;i++){
cout << "STRING " << reinterpret_cast<int *>(&matrix2[i]) << endl;
}
track = *dmatrix;
float * last = &dmatrix[3][6];
for(int count = 0; track <= last; track++){
cout << "Position with track ==>" << count++ << "\tMemory Location==>"<<track << "\tValue==>" << *track <<endl;
}
float * track2 = dmatrix[0];
float * last2 = &dmatrix[3][6];
for(int count = 0; track2 <= last2; track2++){
cout << "Position with track2 ==>" << count++ << "\tMemory Location==>"<<track2 << "\tValue==>" << *track2 <<endl;
}
int size_dmatrix = sizeof(dmatrix);
int size_dmatrix_row = sizeof(dmatrix[0]);
int size_track = sizeof(track);
int size_track2 = sizeof(track2);
cout << endl << endl << "Size of Data Types:\ndmatrix ==>" << size_dmatrix << endl;
cout << "Size of Track ==>" << size_track << endl;
cout << "Size of Track2==>" << size_track2 << endl;
cout << "Size of dmatix[0] ==>" << size_dmatrix_row << endl;
return 0;
}
test.cc
----------------------------------------------
test.h
----------------------------------------------
void stringexample();
--------------------------------------------------
string_ex.cc
--------------------------------------------------
void stringexample(){
}
--------------------------------------------------
test.cc
--------------------------------------------------
----------------------------------------------------
----------------------------------------------------
----------------------------------------------------
Then run 'make'
test3[3] = 'C';
The next agregate data type that C++ provides is structs and unions.
Structs and Unions have largely been superceded in C++ by Classes. They
are inherited from C and are designed as the key multi-type agregrate
data type that C uses to create related gruops of data, similar to what
a database can provide. Through coding algorithism, their importance
have grown far greater than just mear static records, but in this
section we will look only at their basic syntax and usage.
The real limitation to arrays is that data must be all of the same type.
structs create a new type which contain many of types with in. The
basic format of a struct is as follows:
Declaration:
stuct struct_type_name {
data type;
data tpye;
....
}[optional object name];
Notice the semicolon on the end of the struct declaration, which is
unusual and particular to struct's in that it is after the curley brace.
structs create a new user defined data type, and after their declaration
can be used as any other data type:
Example:
struct birds{
char species[30];
char gender[1];
char color[10];
int size_in_inches;
double weight;
char diet[30];
};
birds african_grey;
birds canary;
or you can make the instances with the declation:
struct birds{
char species[30];
char gender[1];
char color[10];
int size_in_inches;
double weight;
char diet[30];
} canary, african_grey;
Now it might be noted here an important diference between C and C++. In
C, you need the struct keyword to create instances of your struct's.
C EXAMPLE:
struct birds african_grey;
struct birds canary;
Otherwise, you need to use typedef in C
typedef birds parrot;
parrot african_grey, scarlet_macaw, conour, monk, bundie;
or even use typedef in the declaration:
typedef struct {
char species[30];
char gender[1];
char color[10];
int size_in_inches;
double weight;
char diet[30];
} birds;
birds african_grey;
birds canary;
This is a variation of typedef, which we haven't covered. In general
typedef creates an alias for a datatype of any kind:
typedef data_type alias;
typedef int BOOL;
typedef char[300] buffer;
thus the above example:
typedef struct {
char species[30];
char gender[1];
char color[10];
int size_in_inches;
double weight;
char diet[30];
} birds;
is not the same as
struct birds {
char species[30];
char gender[1];
char color[10];
int size_in_inches;
double weight;
char diet[30];
} canary, african_grey;
None creates a new data type, "birds" and the other creates new
instances of the struct, "canary", "african_grey".
****in C***
struct's can be initialized in C++ as follows, with the use of the assignment
operator and curly braces:
birds african_grey = { "African Grey Congo", 'M', "Light Grey", 13, 8.34, "Fruit and Seed" };
birds canary = { "Red Factor Canary", 'M', "Red to Orange", 2, 1.2, "Seed and Grass" };
in C remember to add the struct keyword.
The internal data types are reached by use of the dot operator.
char * color = canary.color;
cout << canary.color << endl;
strcpy(canary.color, "Deep Red"); //can not assign a char[]
You can make a pointer to a struct, and this is often useful
birds * canary;
but remember that you have no memory allocated for members yet.
birds *finches, parrot={"Conure", 'F', "Green", 6, 60, "Oranges and Peanuts"};
finches = &parrot;
When you use a pointer, access to members is gained using the infix
operaotr "->"
Example:
struct birds{
char species[30];
char gender[1];
char color[10];
int size_in_inches;
double weight;
char diet[30];
} *finches, parrots = {"Conure",'F',"Yellow",'6',60,"Peanuts and Oranges"};
finches = &parrots;
cout << "Your " << finches->species << "is " << finches->color << endl;
By creating arrays of struct's, large databases of records can be stored
in your program
birds finches[100];
strcpy(finches[0].species, "Zebra Finch");
birds *parrots[100];
strcpy(parrot[0]->species, "Amazon");
-------------------------------------------------------
Now - I've reached a crossroads about what direction I want to continue
in. I have a binary choice in front of me, left or right. So I decided
to split this down the middle. There are two other aggregate data
types, similar to structs, which create new data types when used, one is
the "union" and the other is the "class". The problem is, and this is
also true of struct in C++, that they are actually very complicated and
the class data construction is particularly important and lays out the
basis for most advanced C++ object oriented coding. But before I get
into a full throttle discussion of classes (and unions), I want to
discuss more elementry syntax uses, such as functions, a detailed look
at operators, typing syntax and flow control. But I want to be complete.
I've been trying to underscore the point that everything in programming
is data. The code you write is data, variables are data, functions are
data, and literal data types are data. Data can be stored in C++ in
typed variable, anonymous memory segments pointed at by pointers, and
referred to through references. structs allow us to keep a lot of data
together in a single package (or object) that we can carry around as
needed. And although I haven't demonstrated this for structs, one of
the types of data that can be packaged in the data type includes
functions. This is true for unions, an is essential for classes.
Classes, Structs and Unions are really all related. But I don't want to
detail the correct constructions of Classes and Unions yet. So I'm
presenting a very simplified view of these data constructions, and later
we will greatly expand on them.
Unions:
Unions are inherited from C and they are often described as being
similar to structs. I'd like to turn this upside down and say that
Unions are really nothing like structs. They do share similar syntax,
but under the hood they are completely different.
A struct has many members that are packaged together sequentially. They
sit in memory likes duck on the pond. With union's we have one member
in memory, that can morph to any type that is defined within the
construction.
Union's are created similarly as struct's Their basic format is
union <union name> {
member;
member;
member;
} union_object, union_object;
Again, notice the semicolon at the end of the definition.
Here is a simple example:
union one_at_a_time{
int dataone;
int matrix[10];
double dollars1;
long double parts_per_billion;
};
one_at_a_time myunion; //declare a union of type one_at_a_time
union access is similar to a struct:
myunion1.dataone = 500;
cout << myunion1.dataone <<endl;
There is only one, however, data member for myunion. But it can be
accessed by any of the members names, which will most likely give you a
wrong result. We gave it an integer and if we try to retrive the data
as the double (using memeber dollar), we're going to get the binary data
for the int 500 intepreted as a double. The size of myunion is the size
of the largest data member of the union, in our case the int array
matrix. Here is an example of a union and the pitfalls:
#include <iostream>
using namespace std;
int main(int argc, char * argv[]){
union one_at_a_time{
int dataone;
int matrix[10];
double dollars1;
long double parts_per_billion;
};
one_at_a_time myunion1;
myunion1.dataone = 500;
cout << myunion1.dataone <<endl;
//one_at_a_time myunion2;
//myunion2.matrix= { ,{6,5,4,3,2,1}}; doesn't work
//one_at_a_time myunion2= {6,5,4,3,2,1}; doest work ... you can only
//initialize with ONE data at a time
one_at_a_time myunion2;
myunion2.matrix[0] = 6;
myunion2.matrix[1] = 5;
myunion2.matrix[2] = 4;
myunion2.matrix[3] = 3;
myunion2.matrix[4] = 2;
myunion2.matrix[5] = 1;
myunion2.matrix[6] = 10;
for(unsigned int i = 0; i < sizeof(myunion2.matrix)/sizeof(int); i++){
cout << myunion2.matrix[i] << endl;
}
myunion2.dollars1 = 2.75;
for(unsigned int i = 0; i < sizeof(myunion2)/sizeof(int); i++){
cout << "Dollar1==>" << myunion2.dollars1 << endl;
cout << "martix ==>" << myunion2.matrix[i] << endl;
}
}