Many of the features of the language are illustrated below, but it is mostly description by example - there isn't much text.
Note that most statements are terminated with a semicolon, and that // is the comment symbol - everything to the right of the double slash is ignored by the compiler Example:
// include the io library #include <iostream> // specify the namespace we are using using namespace std; // declare a constant size to be used for arrays const int SIZE = 20; // declare floatarray as a type describing arrays // of "SIZE" floating point elements typedef float floatarray[SIZE]; // declare a subroutine, printarray, which takes a // floatarray as a parameter void printarray(floatarray arr); // provide the main body of the program int main() { floatarray myarray; for (int index = 0; index < SIZE; index++) { myarray[index] = index * 1.5; } printarray(myarray); } // provide the body of the printarray subroutine // declared earlier void printarray(floatarray arr) { for (int index = 0; index < SIZE; index++) { cout << arr[index] << endl; } }
float myfloat = 3.7; int myint = 29; char mychar = 'c';
mychar = char(myint); myint = int(myfloat);float to int conversion is done by rounding down (truncating)
char to int conversion (and int to char) is done using the ascii tables for matching character values to integers
const int DaysInYear = 365; const float Pi = 3.14; const char Blank = ' ';
static
retain their values
across
function calls
extern
in all files except X
int myint, anotherint, andanotherint; float fees = 0.0; static char initial = 'D'; extern Boolean quit;
NOTE: some versions of C++ treat each block of code (demarcated with { and } ) as a possible scope: a variable declared within a block is regarded as local to that block. This is not recognized by all C++ compilers, however, so we recommend that variables either be declared with global scope - immediately after the global type declarations - or as local to a function, at the very start of the function body.
return-type functionname(list of parameters) { local variable declarations body of the function return statement }Where the value returned by the return statement must match the return type. (If the function never returns a value you may leave out the return statement, and use a return type of void.)
Example:
int sumandtruncatefloats(float f, float g) { // takes two floating point values, // adds them together, // truncates the decimal portion, // stores the value in an integer variable named result, // and returns this result int result; result = int(f+g); return( result ); }
The corresponding function declarations and calls in the rest of the program might look like
#include <iostream> using namespace std; int sumandtruncatefloats(float f, float g); int main() { float y = 2.914; int x; x = truncatefloat(3.7 + y); cout << x << endl; }
Pass-by-reference, or reference parameters, allows the function to alter the value of a variable passed to it. Pass-by-reference is achieved by placing an ampersand before the parameter identifier in the function header, e.g.:
void validswap(int &x, int &y) { int temp; temp = x; x = y; y = temp; }The call
validswap(var1, var2);
would then swap the values
of variables var1 and var2.
Arrays and structs are automatically passed by reference. If you wish to prevent this (to prevent the function from altering the array or struct contents) prefix them with const in the parameter list, e.g.:
void foo(const char str[], int length); ... foo(mystring, SIZE);
int X = 0; float y = 3.7; char c = 'a'; char myarray[4] = "xyz\0"; cout << "X has value " << X << endl << "y has value " << y << endl; cout << "c has value " << c << endl << "myarray has value " << myarray << endl;This would print out
X has value 0 y has value 3.7 c has value a myarray has value xyzNote that
endl
is the keyword for the end-of-line symbol,
and '\0' is the end-of-string character.
cout << setw(3) << x;This would print the value of x as a 3-character field
cout << setiosflags(ios::fixed) << setprecision(N) << x;
int X; float y; char c; char myarray[4]; cin >> X >> y >> c >> myarray;
cin.get(c); // where c is a char variable
cin.getline(mystring, Size-1); // where mystring is an array of Size chars
This is achieved by creating a variable which acts as a pointer into the file, and opening the file as part of an input stream or as part of an output stream.
The example below shows typical file input and output
// include the necessary i/o libraries #include <iostream> #include <fstream> using namespace std; // declare outfile as a pointer into a file to be used // as the destination for an output stream ofstream outfile; // declare infile as a pointer into a file to be used // as the source for an input stream ifstream infile; int main() { // declare character arrays to hold the names of our input // and output files char infilename[SIZE]; char outfilename[SIZE]; // declare a character array to hold a single word of text // (a word is a block of text containing no whitespace) char nextword[SIZE]; // declare a character array to hold lines of text for input or output char wholeline[SIZE]; // declare a character to hold single characters of text for input or output char c; cout << "Please enter the name of the input file" << endl; cin >> infilename; cout << "Please enter the name of the output file" << endl; cin >> outfilename; // open the input file, and check to make sure it opened successfully // exit if it failed to open (e.g. the file doesn't exist, // or is read-protected) infile.open(infilename); if (infile.fail()) exit(1); // open the output file, and check to make sure it opened successfully // exit if it failed to open (e.g. is write-protected) outfile.open(outfilename); if (outfile.fail()) exit(2); // read the next line of text, up to SIZE characters, from // the input file into the wholeline array infile.getline(wholeline, SIZE); // go through the input file until you hit the end of file // on each pass through the while loop, the following takes place: // read the next word of text (i.e. everything up to the next whitespace) // into the nextword array // read the next character into the variable c, // after double-checking to make sure the last read // didn't take us to the end-of-file // write the first word in the nextword array into the output file // write the character in variable c into the output file while (!infile.eof()) { infile >> nextword; if (!infile.eof()) infile.get(c); outfile << nextword; outfile << c; } // close the input file and close the output file infile.close(); outfile.close(); }
if (<Boolean expression) { // statements if expression is true } else { // statements if expression is false }The "else" portion of the statement is optional.
if (x == 3) { // statements for x == 3 } else if (x == 7) { // statements for x == 7 } else if (x < 0) { // statements for x is negative } else { // statements for all other values of x }
switch (x) { case 3: // statements for x == 3 break; case 7: // statements for x == 7 break; default: if (x < 0) { // statements for x is negative } else { // statements for all other values } }Leaving out the break statements can allow us to group values for which we want similar behaviour, e.g.:
switch (c) { case 'q': case 'Q': // statements for letter q break; case 'c': case 'C': // statements for letter c break; default: // statements for all other letters }
for (<initialisation>; <continuance test>; <update> ) { <executable statements> }Example: consider all even integers from 0 through 98
for (int x = 0; x < 100; x += 2) { // statements to be executed }
while (<Boolean expression) { // statements to be executed }Meaning: as long as the Boolean expression evaluates to true, carry out the body of the loop.
Example: repeat until the user enters Q
char c = 'C'; while (c != 'Q') { // executable statements cout << "Enter Q to quit, C to continue" << endl; cin >> c; }
do { // statements to be executed } while (<Boolean expression>);
Example: repeat until the user enters a positive value:
do { cout << "Enter a postive integer" << endl; cin >> x; } while (x <= 0);
// declare a type of array to hold 50 floats const int Size = 50; typedef float floatarray[Size]; // declare an actual array variable floatarray myfloats;
arrayname[i]
, e.g.:
myfloats[3] = 17.4; myfloats[2] = myfloats[3] + 4.1; x = sqrt(myfloats[2]);
myarray[i]
is functionally identical
to *(myarray + i)
void fillarray(floatarray arr, int arrsize); int main() { floatarray myfloats; fillarray(myfloats, Size); } void fillarray(floatarray arr, int arrsize) { cout << "Enter " << arrsize << " floats" << endl; for (int i = 0; i < arrsize; i++) { cin >> arr[i]; } }To pass arrays as value parameters prefix them with
const
in the parameter list, e.g. void viewarray(const floatarr arr, int
arrsize);
They are stored in row-major order
const int Rows = 10; const int Columns = 16; typdef char grid[Rows][Columns]; int main() { grid Mygrid; for (int r = 0; r < Rows; r++) { for (int c = 0; c < Columns; c++) { Mygrid[r][c] = ' '; } } }To pass multidimensional arrays as parameters you must declare (in the parameter list) the size of all dimensions except the first, e.g.:
int matrix[20][30]; void initmatrix(int m[][30], int rows);
struct <typename> { <type> field1; <type> field2; ... <type> fieldN; };The syntax for accessing a field value is
<varname>.<fieldname>
Example: employee record
struct EmployeeRec{ string surname; string givenname; float hourlywage; }; int main() { EmployeeRec employee1; strcpy(employee1.surname, "Wessels"); strcpy(employee1.givenname, "Dave"); employee1.hourlywage = 0.01; }
struct StaffList { string bossname; int numemployees; EmployeeRec staff[Size]; }; int main() { StaffList Meagresoft; strcpy(Meagresoft.bossname, "Gill,Bates"); int numemployees = Zillion; for (int replicate = 0; replicate < Size; replicate++) { strcpy(Meagresoft.staff[replicate].surname, " "); strcpy(Meagresoft.staff[replicate].givenname, " "); Meagresoft.staff[replicate].hourlywage = 35.0; } }
void initcomp(StaffList company); int main() { StaffList Meagresoft; initcomp(Meagresoft); ... }
enum booleflag { false, true }; booleflag = true;
// declare the class, listing public and private operations and data variables class Myclass { public: // constructor Myclass(); // destructor ~Myclass(); // inline functions int returndata() { return(mydata); } void setdata(int x) { mydata = x; } // general functions void dosomework(); private: // private functions void adjustdata() { mydata++; } // private data int mydata; Myclass *classptr; } // declare the bodies for the constructor and destructors Myclass::Myclass() { mydata = 0; } Myclass::~Myclass() { delete classptr; } // declare the bodies for the other general and private functions void Myclass::dosomework() { classptr = new Myclass; classptr->setdata(mydata + 1); } // example of using a variable of type "Myclass" from within the main routine int main() { // declare a Myclass variable Myclass classvar; // call the "dosomework" operation to work on that particular variable classvar->dosomework(); }
#include <iostream> #include <cstdlib> #include <ctime> using namespace std; // C++ random number generator, // user enters values X and Y and the program // generates a random value in that range int main() { int X, Y, result; // initialize the random number generator srand((unsigned int)(time(NULL))); // get the bounds cout << "Enter the lower bound for the generated numbers" << endl; cin >> X; cout << "Enter the upper bound for the generated numbers" << endl; cin >> Y; // generate the random value in the correct range result = (rand() % (top + 1 - bottom)) + bottom; // print out the random number cout << result << endl; }
#include <iostream> #include <cmath> using namespace std; // compile using g++ progname.cpp -o progname -fhandle-exceptions -lm int main() { float x, y; cout << "Enter a numerator and denominator" << endl; cin >> y >> x; // a try block contains exception handling // exceptions are invoked with throw() try { if (x == 0) throw("Divide by zero\0"); if ((y/x) < 0) throw("Sqrt of negative\0"); cout << "sqrt(" << y << "/" << x; cout << ") is " << sqrt(y / x) << endl; } // exceptions are handled with catch() catch(char str[]) { cout << str << endl; throw; // re-throws any unhandled errors } }
The asterisk (*) is used to denote a pointer, while the ampersand (&) is used to determine the address of a variable.
Some of the basic uses of pointers are illustrated in the examples below
int x; // x is an integer int *x_ptr; // x_ptr is a pointer to an integer x_ptr = &x; // make x_ptr point to x x = 3; cout << x << " " << *x_ptr; // print out 3 3Pointers to functions:
int foo(float x); int (*f_ptr) (); // f_ptr can point to any function // which returns an int f_ptr = foo; // f_ptr points to function foo y = (*f_ptr)(3.7); // equivalent to y = foo(3.7);
// we want an array of 10 student records, s_ptr will point // to the start of the array StudentRec *s_ptr; // use sizeof to calculate the size of one student record, // pass the number of records we want (10) and the size // to the calloc routine, which allocates memory and returns // a pointer to the allocated block // then convert the address to a pointer to a student record // and store it in s_ptr s_ptr = (StudentRec *)calloc(10, sizeof(StudentRec)); // or alternatively use malloc, where we calculate the whole size // (for all 10 records) and pass that as the parameter // s_ptr = (StudentRec *)malloc(10 * sizeof(StudentRec));
The extra arguments are passed as parameters to the main routine, where they can be accessed using argc (which tells you how many arguments were passed) and argv (which is effectively an array of pointers to the arguments).
Each argument is passed as a seperate character array, so argv[1] points to the first argument text, argv[2] points to the second argument text, etc. (argv[0] gives you the name of the executable file for the program itself)
#include <cstdlib> using namespace std; int main(int argc, char *argv[]) { cout << "You called program " << argv[0] << " with arguments " << endl; for (int i = 1; i < argc; i++) { cout << argv[i] << " "; } cout << endl; }If the user typed in "myprog foo blah x" then the output from this program would be
You called program myprog with arguments foo blah xMaking Unix system calls
You can make Unix system calls (e.g. to run other Unix commands or
programs)
from within a C++ program by creating a string containing the command
you wish to run then invoking the system
routine:
char commandline[256]; cout << "Please enter a filename" << endl; cin >> filename; strcpy(commandline, "cat "); strcat(commandline, filename); strcat(commandline, " | wc >> "); strcat(commandline, filename); strcat(commandline, ".out"); system(commandline);If the user entered filename "mydata" this program would execute the Unix command "cat mydata | wc >> mydata.out", which would cout the words in file "mydata" and write the tally into the file "mydata.out".
myglobal
is declared in first.cpp
and used in main.cpp
make mainprog
, and the system will automatically
compile exactly those files which have been altered since the
last time make was run
Dependencies: mainprog / | \ / | \ first.h | second.h \ | / \ | / boolean.h //-------------------------------------------------------------------- // File boolean.h: gets included from several .h files // and the main program //-------------------------------------------------------------------- #ifndef BOOLE #undef TRUE #undef FALSE typedef int Boolean; const int TRUE = 1; const int FALSE = 0; using namespace std; #define BOOLE #endif //-------------------------------------------------------------------- // File first.h: gets included from main, // is header for first.cpp routines //-------------------------------------------------------------------- #ifndef FIRST #include "boolean.h" int foo(char c); #define FIRST #endif //-------------------------------------------------------------------- // File first.cpp: body of routines from first.h //-------------------------------------------------------------------- #include#include "first.h" int myglobal; int foo(char c) { cout << "C is " << c << endl; return( int(c) ); } //-------------------------------------------------------------------- // File second.h: gets included from main, // is header for second.cpp routines //-------------------------------------------------------------------- #ifndef SECOND #include "boolean.h" char blah(int x); #define SECOND #endif //-------------------------------------------------------------------- // File second.cpp: body of routines from second.h //-------------------------------------------------------------------- #include #include "second.h" char blah(int x) { cout << "X is " << x << endl; return( char(x) ); } //-------------------------------------------------------------------- // File mainprog.cpp: main program, uses seperately compiled files // first.o and second.o //-------------------------------------------------------------------- #include #include "first.h" #include "second.h" #include "boolean.h" extern int myglobal; int main() { int mynum; char mychar; mynum = foo('Z'); mychar = blah(mynum); } //-------------------------------------------------------------------- // File makefile: make mainprog - brings mainprog up to date, // updates other files only if needed // make clean - gets rid of .o files //-------------------------------------------------------------------- mainprog: first.o second.o mainprog.cpp g++ first.o second.o mainprog.cpp -o mainprog first.o: first.cpp g++ -c first.cpp second.o: second.cpp g++ -c second.cpp clean: rm -f *.o
For C/C++ CGI programs we'll need to remember to compile the programs and put the executables in the appropriate location (e.g. a cgi-bin directory) or give them appropriate file extensions (e.g. .cgi) to comply with the server configuration.
The only library truly necessary to allow C to work with forms and CGI programming is <stdlib.h> (or, for C++, <cstdlib>).
Script output in C/C++ can be easily generated with printf statements, e.g.:
#include <stdlib.h> int main() { /* note the 13 and 10 are ascii values for return and \n */ printf( "Content-type: text/html; charset=iso-8859-1%c%c\n", 13, 10 ); printf( "<html><body>\n"); printf( "Hi!\n"); printf( "</body></html>\n"); return 0; }
The only catch is that we'll need to do some manual processing and analysis of the query string or post body to extract the necessary name/value pairs coming from a GET or POST request.
For forms submitted using the GET method, we can use the C get_env function to obtain the query string variable. If we know the order and identifiers for the pairs in the query string we can extract the data fairly easily, as shown in the example below:
/* the query string is stored in an environment variable * named QUERY_STRING, which we can retrieve as follows: */ char *qstring = getenv("QUERY_STRING"); /* in this example we're expecting the user to enter two * integer data values (a numerator and denominator) in the * form fields, and the identifiers are "num" and "denom" * if we (foolishly) trust that the query string will be in * our expected format, and that only appropriate data values * will be entered by the user, * then we can extract the data from the query string with sscanf * as shown below: */ long n, d, numfields; numfields = sscanf(qstring, "num=%ld&denom=%ld", &n, &d); if (numfields != 2) { /* do some error processing, * as sscanf didn't find 2 long ints */ } else { /* do whatever processing you want on the * numerator (n) and denominator (d) values */ }
Note that there are no guarantees our script won't get hit by people creating URLs that have different identifiers than we're expecting, so it's worth spending the time to come up with a more robust parsing method. |
If the form submits data using the POST method then we can look up the length of the submitted data by looking up the CONTENT_LENGTH environment variable, and then read the data from standard input.
char qstring[MAXLENGTH]; long length, numfields, n, d; /* get the content length environment variable * (note that it is stored as text) * and check that it isn't empty */ char *lengthstr = getenv("CONTENT_LENGTH"); if (lengthstr == NULL) { /* do some sort of error processing, since * the body of the request is apparently empty */ } /* extract the actual integer length from the stored string * and check that the string did actually hold a single int */ numfields = sscanf(lengthstr, "%ld", &length); if (numfields != 1) { /* perform error processing, * as the content length environment variable * hasn't been set correctly */ } /* make sure the length of the post body is acceptable */ if ((length < 1) || (length >= MAXLENGTH)) { /* the length is too short or too long * for our script to work with */ } /* now read the query string from standard input */ fgets(qstring, length+1, stdin); /* Now we can process the query string as per the GETS example */ numfields = sscanf(qstring, "num=%ld&denom=%ld", &n, &d); if (numfields != 2) { /* do some error processing, * as sscanf didn't find 2 long ints */ } else { /* do whatever processing you want on the * numerator (n) and denominator (d) values */ }
I'm assuming folks are reasonably competent with either C or C++ already (if not, see the CSCI 160/161 course web pages for an intro).
Essentially, the mysql.h library provides a collection of routines that allow you to establish a connection to a MySQL database, issue queries/updates, and store and process the results.
The key data types and routines are as follows:
Data types:
// assuming mysql_init and mysql_real_connect have already been run... // open the database connection and store the status/error number int errnum = mysql_query(dbptr, "SHOW DATABASES;") MYSQL_RES *all_results = mysql_store_result(dbptr); // read the first/next row of the results into an array // (returns NULL when you run out of rows to read) MYSQL_ROW row = mysql_fetch_result(all_results);
Functions: (unless otherwise specified, most of these return an integer value, using 0 for error-free and a different value to indicate a specific error status).
Compilation notes:
Aside from including the mysql.h C library, you must also use a number of flags when compiling your C/C++ program, specifying the location of a number of files and libraries. The compliation command below assumes the name of our source code file is db.C and the desired executable will be named dbconn.
g++ -o dbconn db.C -I/usr/local/mysql/include/mysql -L/usr/local/mysql/lib/mysql -lmysqlclient -lz
Code Example:
Here's a quick code snippet illustrating how you can connect to a MySQL database, test for errors, and run and output a simple query using the C mysql.h library.
I haven't included much in the way of comments, but will (hopefully) expand that "soon".
#include <cstdio> #include <mysql.h> using namespace std; int main() { MYSQL_RES *query_result; MYSQL_ROW row; MYSQL *db_handle, mysql; int query_error; // mysql_init retruns an initialized mysql handle, // ready for a realconnect mysql_init(&mysql); // set up the connection variables int port = 3306; // 0; char *socket = "/tmp/mysql.sock"; // NULL; uint client_flag = 0; char *host = "localhost"; char *user = "username"; char *db = "some_db_name"; char *pwd = "password"; // establish a connection to the given server and database, with the specified user info db_handle = mysql_real_connect(&mysql, host, user, pwd, db, port, socket, client_flag); // mysql_error returns the error message for the most recent api function, // or an empty string if there was no error if (db_handle == NULL) { printf(mysql_error(&mysql)); return 1; } // display the server status (like mysqladmin status) printf("Server status: %s\n", mysql_stat(&mysql)); // display the type of connection printf("Connection type: %s\n", mysql_get_host_info(&mysql)); // execute a query query_error = mysql_query(db_handle, "SHOW DATABASES"); if (query_error != 0) { printf(mysql_error(db_handle)); return 1; } // store the results from the query so they can be accessed query_result = mysql_store_result(db_handle); // look up how many rows and fields there were in the result int numrows = mysql_num_rows(query_result); int numfields = mysql_num_fields(query_result); printf("Found %d rows of %d fields\n", numrows, numfields); // access the query results, one row at a time while ((row = mysql_fetch_row(query_result)) != NULL) { printf("Table: %s\n", (row[0] ? row[0] : "NULL")); } mysql_free_result(query_result); // change which database you're using query_error = mysql_select_db(db_handle, "test"); // free the resources for the connection mysql_close(db_handle); } |
Curses is a library that provides routines to set up and manipulate a text based window. This is handy when the programs need to run in command windows (e.g. assignments on csciun1 ...).
This allows the programmer to write to different spots on the screen (specified by text row and column), change highlighting, etc.
The sample program below works on csciun1 when compiled using
cxx filename.C -o executablename -lcurses
#include <string.h> #include <curses.h> // =================================================================================== // Handy functions from the curses library // // initscr(); // initializes the window, stores key information // // this also initializes two variables, named LINES and COLS // // to record the number of rows and columns in the window // endwin(); // restores the window to its original state // // move(r, c); // moves the cursor to the specified position // getyx(w, r, c); // looks up the current position of the cursor in this window, // // and stores the row and column in r and c // // assuming wnd is of type WINDOW* as in the main routine below // // c = getch(); // reads the next character the user types // delch(); // deletes the character currently under the cursor // addch(c); // writes the character to the current cursor position // nodelay(w, true); // turns on no-delay mode for window w, // // meaning getch will always return immediately, // // and will return -1 if no key has been pressed // nodelay(w, false); // turns no-delay mode off again, // // i.e. getch() will wait for input // // refresh(); // updates the screen display after an alteration // clear(); // clears the window // // attron(attribute); // these two routines turn on/off the use of special // attroff(attribute); // features when displaying characters // // some of the special features available include: // // A_UNDERLINE // // A_BOLD // // A_REVERSE // // A_DIM // // A_NORMAL // =================================================================================== // Some example functions and a simple main routine that uses them // sets up the window for curses, and establishes some typical settings // (turns off they keyboard echo, turns off the need for users to hit // return before the program can read their input, etc) WINDOW *InitWindow(); // restores the original window state void FinishWindow(); // writes a null-terminated string starting at the specified row/column // (note that row 0 is the top of the screen, // and column 0 is the leftmost column) void WriteText(char text[], int row, int col); // writes a character to the specified position but in bold type void WriteBoldText(char text[], int row, int col); // writes a character to the specified position but dim and in inverse colour void WriteDimReversedText(char text[], int row, int col); int main() { int r, c; WINDOW *w = InitWindow(); WriteText("Hello!", 0, 0); // write "Hello!" starting at row 0, column 0 WriteBoldText("BOLD!", 5, 10); // write text (in bold) starting at row 5, column 10 WriteDimReversedText("Dim and reverse video", 2, 20); FinishWindow(); } WINDOW *InitWindow() { WINDOW *w = initscr(); cbreak(); noecho(); crmode(); nonl(); intrflush(stdscr, false); keypad(stdscr, true); clear(); refresh(); return w; } void FinishWindow() { endwin(); } void WriteText(char text[], int row, int col) { int i = 0; while (text[i] != '\0') { if (col >= COLS) { col = 0; row++; } if (row >= LINES) row = 0; move(row, col++); addch(text[i++]); } refresh(); } void WriteBoldText(char text[], int row, int col) { attron(A_BOLD); // start using bold when writing WriteText(text, row, col); // write the characters attroff(A_BOLD); // stop using bold on characters } void WriteDimReversedText(char text[], int row, int col) { attron(A_DIM); attron(A_REVERSE); WriteText(text, row, col); attroff(A_REVERSE);attroff(A_DIM); }