'Poweful' isn't exactly the word. Basically, in C, if you try to reuse code, you'll end up with files that look like this: (this is a real file, BTW)
Code:
//Matrix.h
#ifndef Matrix_h
#define Matrix_h
// IntMatrix...
typedef struct
{
int * array;
unsigned int width, depth;
} IntMatrix;
IntMatrix * makeIntMatrix (unsigned int width, unsigned int depth);
void killIntMatrix (IntMatrix * killMe);
int traverseIntMatrix (IntMatrix * traverseMe, int (*fn) (int*));
// MACRO int& pokeIntMatrix (intMatrix * pokeMe, int x, int y);
// MACRO int widthIntMatrix (IntMatrix * widthMe);
// MACRO int depthIntMatrix (IntMatrix * depthMe);
void copyIntMatrix (IntMatrix * intoMe, IntMatrix const * copyMe);
void initIntMatrix (IntMatrix * initMe, unsigned int width, unsigned int depth);
void attach2BottomIntMatrix (IntMatrix * add2Me, IntMatrix const * attachMe);
int * rowIntMatrix (IntMatrix * rowMe, int y);
#define widthIntMatrix(widthMe) \
((widthMe) -> width)
#define depthIntMatrix(depthMe) \
((depthMe) -> depth)
#define pokeIntMatrix(pokeMe, x, y) \
(((pokeMe) -> array)[(x) + (y) * widthIntMatrix(pokeMe)])
// Char Matrix...
typedef struct
{
char * array;
int width, depth;
} CharMatrix;
CharMatrix * makeCharMatrix (unsigned int width, unsigned int depth);
void killCharMatrix (CharMatrix * killMe);
int traverseCharMatrix (CharMatrix * traverseMe, int (*fn) (char*));
// MACRO char & pokeCharMatrix (intMatrix * pokeMe, int x, int y);
// MACRO int widthCharMatrix (IntMatrix * widthMe);
// MACRO int depthCharMatrix (IntMatrix * depthMe);
#define widthCharMatrix(widthMe) \
((widthMe) -> width)
#define depthCharMatrix(depthMe) \
((depthMe) -> depth)
#define pokeCharMatrix(pokeMe, x, y) \
(((pokeMe) -> array)[(x) + (y) * widthCharMatrix(pokeMe)])
// DoubleMatrix
typedef struct
{
double * array;
unsigned int width, depth;
} DoubleMatrix;
DoubleMatrix * makeDoubleMatrix (unsigned int width, unsigned int depth);
void killDoubleMatrix (DoubleMatrix * killMe);
int traverseDoubleMatrix (DoubleMatrix * traverseMe, int (*fn) (double*));
// MACRO double & pokeDoubleMatrix (DoubleMatrix * pokeMe, int x, int y);
// MACRO int widthDoubleMatrix (DoubleMatrix * widthMe);
// MACRO int depthDoubleMatrix (DoubleMatrix * depthMe);
void initDoubleMatrix (DoubleMatrix * initMe, unsigned int width, unsigned int depth);
void multDoubleMatrix (DoubleMatrix * A, DoubleMatrix * B, DoubleMatrix * C);
void copyDoubleMatrix (DoubleMatrix * intoMe, DoubleMatrix const * copyMe);
void attach2BottomDoubleMatrix (DoubleMatrix * add2Me, DoubleMatrix const * attachMe);
// MACRO double * rowDoubleMatrix (DoubleMatrix * rowMe, int y);
#define widthDoubleMatrix(widthMe) \
((widthMe) -> width)
#define depthDoubleMatrix(depthMe) \
((depthMe) -> depth)
#define pokeDoubleMatrix(pokeMe, x, y) \
(((pokeMe) -> array)[(x) + (y) * widthDoubleMatrix(pokeMe)])
#define rowDoubleMatrix(rowMe, y) \
(((rowMe) -> array) + y * ((rowMe) -> width))
typedef struct
{
double x, y, z;
} DoubleVector;
DoubleVector crossProductDoubleVector (DoubleVector const * A, DoubleVector const * B);
double dotProductDoubleVector (DoubleVector const * A, DoubleVector const * B);
#endif // Matrix_h
You can imagine what the corresponding Matrix.c file looks like. Every time I need to have a matrix of a new kind of object, I get to spend five minutes of copy n' pasting, followed by ten minutes of search n' replacing. If I want to have consistency, then every time I modify one type of Matrix, I have to do the same for each other kind (you might notice that the DoubleMatrix type has quite a bit more functionality because I didn't want to bother with this).
This is an awful lot of trouble to go through considering that the only real difference between
Code:
void attach2BottomIntMatrix (IntMatrix * add2Me, IntMatrix const * attachMe);
and..
Code:
void attach2BottomDoubleMatrix (DoubleMatrix * add2Me, DoubleMatrix const * attachMe);
Is whether they use sizeof(double) or sizeof(int).
Now, it would be possible to make a single generic Matrix class in C, using some pointers and a bit of know-how... but it is inefficient, and still akward to code.
This is the kind of problem that C++ remedies. Instead of having to make tedious additions to my old libraries, I can just write a single Matrix class, and be done with it.
There are a few more tricks that C++ can conjure, but the set of important ones basically boil down to the same kind of problem: being able to use a single data structure/algorithm for multiple types. (efficiently)
There are more differences of course (most notably, the nature of their standard libraries), but this is the main difference, the reason it was created really.