Upload
vuongtuyen
View
218
Download
1
Embed Size (px)
Citation preview
The C/C++ preprocessor substitutes “mechanically”, from top to bottom inside the file. On the left, the occurrences of i inside mainare both replaced by 20, whereas on the right they are both replaced by 30.
Note that the compiler “sees” and empty function fun in both cases!
Solution
Solution
The compiler expects ptr to point to a specific type of function – one with a char as the first argument. Instead, when ptr is used to access func, the first argument is an int!
Note below that the C++ compiler is more strict than the C compiler (as expected):
The only way to get massive increases in productivity is to leverage off other people’s code. That is, to use libraries.
One of the primary design goals of C++ is to make library use easier.
text
A tiny C-like libraryMost C libraries have a set of structs and a set of functions that act on those structs. lAs an example, consider a programming tool that acts like an array, but whose size can be established at runtime, when it is created.lI’ll call it a CStash. lAlthough it’s written in C++, it has the style of what you’d write in C:
text
Note that every function takes a pointer to the struct as argument!
currently allocated
The index of the next empty element. It also means the
number of elements currently stored.
Note that coutwasn’t executed –assert caused the program to exit!
The actual message is implementation-dependent
Why are structure arguments generally passed as pointers in C?
If passed by value:• A copy of the struct is made, wasting
memory (think big structs!)• Changes made by the function are not
visible outside.
• element is a pointer to void, i.e. to a yet-unknown piece of data. The function will find how many bytes the data has from the size field of s
• The data pointed to by element is constant, i.e. it cannot be changed (for safety)
Remember from ch.2: separate compilation
For each translation unit (.cpp file), the compiler creates an object file, with an extension of .o or .obj or something similar. These object files, along with the necessary start-up code, must be collected by the linker into the executable program (.exe). During linking, all the external references must be resolved.
Separate compilationFor example, in CLibTest.cpp, functions such as initialize( )and fetch( ) are declared (that is, the compiler is told what they look like) and used, but not defined. They are defined elsewhere, in CLib.cpp. Thus, the calls in CLib.cpp are external references. The linker must, when it puts all the object files together, take the unresolved external references and find the addresses they actually refer to. Those addresses are put into the executable program to replace the external references.
Problem: Name clash
In C, the external references are simply function names, generally with an underscore in front of them. So all the linker has to do is match up the function name where it is called and the function body in the object file, and it’s done. If you accidentally made a call that the compiler interpreted as func(int) and there’s a function body for func(float) in some other object file, the linker will see _func in one place and _funcin another, and it will think everything’s OK.
Labels in assembly language
How a name clash can lead to a bug
The func( ) at the calling location will push an int onto the stack, and the func( ) function body will expect a float to be on the stack. If the function only reads the value and doesn’t write to it, it won’t blow up the stack. (In fact, the float value it reads off the stack might even make some kind of sense. That’s worse because it’s harder to find the bug. )
To solve this problem, C library vendors will often prepend a sequence of unique characters to the beginning of all their function names. So initialize( ) and cleanup( ) might become CStash_initialize( ) and CStash_cleanup( ). This is a logical thing to do because it “decorates” the name of the struct the function works on with the name of the function, but it creates redundancy:
void CStash_cleanup(CStash *s);
Manual name decoration
[…] the first step toward creating classes in C++. Variable names inside a struct do not clash with global variable names. So why not take advantage of this for function names, when those functions operate on a particular struct? That is, why not make functions members of structs?
A better solution!
In a C program (extension .c), we cannot have functions as members of a struct …
… however, in a C++ program (extension .cpp), functions members are OK!
Decoration is done with the
scope resolution operator
NotS -> size
No moreStash *s
as first argument!
Most C libraries have a set of structs and a set of functions that act on those structs.
The first argument of such a function is usually a struct pointer.
What are two differences between structs in C and C++?
• C++ structs may contain function members (a.k.a. methods)
• The tag of a C++ struct is a name by itself –we don’t precede it with struct when declaring objects (and no need for typedef!)
QUIZDeclare a struct named Aaa with the following members:• Float member f• Integer member p• Function member power that returns the number f p
(declaration and definition)Create, initialize and use and object a of type Aaa in the main program.
Stronger type checking in C++
While C++ allows the assignment of any type of pointer to a void* (this was the original intent of void*, which is required to be large enough to hold a pointer to any type), it will notallow you to assign a void pointer to any other type of pointer. A cast is always required to tell the reader and the compiler that you really do want to treat it as the destination type.
Abstract data typingThe definition of Stash creates a new data type. You create one by saying Stash s, just as you create a float by saying float f. A Stash also has characteristics and behavior. Even though it acts like a real, built-in data type, we refer to it as an abstract data type (ADT), perhaps because it allows us to abstract a concept from the problem space into the solution space. ADTs are sometimes called user-defined types.
Abstract data typingThe C++ compiler treats an abstract type like any data type.E.g. if you say a function expects a Stash, the compiler makes sure you pass a Stash to that function. The same level of type checking happens with abstract data types as with built-in types.
You can immediately see a difference, however, in the way you perform operations on objects. You say object.memberFunction(arglist). This is “calling a member function for an object.” But in object-oriented parlance, this is also referred to as “sending a message to an object.” So for a Stash s, the statement s.add(&i) “sends a message to s” saying, “add( ) this to yourself.”
OOP can be summed up in a single phrase:
sending messages to objects.
The trick, of course, is figuring out what your objects and messages are, but once you accomplish this the implementation in C++ is surprisingly straightforward.
Object detailsA question that often comes up in seminars is, “How big is an object, and what does it look like?” The answer is “about what you expect from a C struct.” In fact, the code the C compiler produces for a C struct (with no C++ adornments) will usually look exactly the same as the code produced by a C++ compiler.
Object detailsThe size of a struct is the combined size of all of its members.Sometimes when the compiler lays out a struct, it adds extra bytes to make the boundaries come out neatly – this may increase execution efficiency.
Why do we need header files?
Header file etiquette
When you create a struct containing member functions, you are creating a new data type. In general, you want this type to be easily accessible to yourself and others. In addition, you want to separate the interface (the declaration) from the implementation (the definition of the member functions) so the implementation can be changed without forcing a re-compile of the entire system. You achieve this end by putting the declaration for your new type in a header file.
text
1. What can we put into header files?The basic rule is “only declarations,” i.e. nothing that allocates storage by generating code or creating variables.This is because the header file will typically be included in several translation units in a project, and if storage for one identifier is allocated in more than one place, the linker will come up with a multiple definition error (this is C++’s one definition rule: You can declare things as many times as you want, but there can be only one actual definition for each thing).
Three rules for header files
2. How are multiple declarations handled?It is possible for the .h file to be included more than once in a complicated program.Both C and C++ allow you to redeclare a function, as long as the two declarations match, but neither will allow the redeclaration of a structure.
Three rules for header files
2. How are multiple declarations handled?The compiler considers the redeclaration of a structure (this includes both structs and classes) to be an error, since it would otherwise allow you to use the same name for different types.To prevent this error when multiple header files are included, you need to build some intelligence into your header files using the preprocessor (Standard C++ header files like <iostream>already have this “intelligence”).
The preprocessor directives #define, #ifdef, and #endif
The name HEADER_FLAG can be any unique name, but a reliable standard to follow is to capitalize the name of the header file and replace periods with underscores (leading underscores, however, are reserved for system names). Here’s an example:
These preprocessor statements that prevent multiple inclusion are often referred to as include guards.
3. How are using directives handled?
The using directive eliminates the protection of that particular namespace, and the effect lasts until the end of the current compilation unit. If you put a using directive (outside of a scope) in a header file, it means that this loss of “namespace protection” will occur with any file that includes this header, which often means other header files.It’s very easy to end up “turning off” namespaces practically everywhere, and thereby neutralizing the beneficial effects of namespaces.
Nested structures
Both C and C++ allow declaration of a structinside another.Here is a simple example (not in text):
http://www.c4learn.com/c-programming/c-nested-structure/
Nested structure example
http://www.c4learn.com/c-programming/c-nested-structure/
?
Draw memory diagram!
Note that there are two functions initialize!
Global scope resolutionThe scope resolution operator gets you out of situations in which the name the compiler chooses by default (the “nearest” name) isn’t what you want. For example, suppose you have a structure with a local identifier a, and you want to select a global identifier a from inside a member function. The compiler would default to choosing the local one, so you must tell it to do otherwise.