Program crashes at: (1) matrix multiplication; and (2) failed matrix addition/subtraction - c++

In short, I was assigned the task of creating a class that dynamically allocates memory to form a matrix of int values.
Part of the class are member functions that perform the basic matrix calculations -- addition, subtraction, and multiplication. Everything compiles (on my end at least), but when I used a driver to test the multiplication portion, it keeps crashing.
I'm using Codeblocks as my IDE and haven't had much luck with the debugger there in trying to figure it out. It appears that the calculation completes (with correct values), but then things go horribly wrong somewhere.
For clarity, each object of the Matrix class has the following member data:
private:
int rows;
int cols;
int **element;
Below is a snippet of the implementation file in which the overloaded operator* is fleshed out. The portion where temp.element[i][x] is set to '0' before the loop performing the multiplication is commented out because the default constructor already sets all values to '0' -- which I had forgotten when I put it in originally. It didn't work when I didn't have it commented out either.
In testing, I used one 2x3 array and one 3x2 array.
Matrix Matrix::operator*(const Matrix &aMatrix) const
{
if(cols == aMatrix.rows)
{
Matrix temp(rows, aMatrix.cols);
for(int i = 0; i < rows; i++)
{
for(int x = 0; x < aMatrix.cols; x++)
{
//temp.element[i][x] = 0;
for(int n = 0; n < cols; n++)
{
temp.element[i][x] += (element[i][n]
* aMatrix.element[n][x]);
}
}
}
return temp;
}
else
{
cerr << "Matrix multiplication failed -- incompatible matrix sizes."
<< endl;
return *this;
}
}
Upon trying to go through the code and find errors, I started re-checking the other functions I had. It appeared that both addition and subtraction worked, but the program would close if the matrices were incompatible (ie. trying to add a 2x3 and a 4x4).
Below is the snippet for the addition (subtraction is almost identical just '-' instead of '+' in the final loops.
Matrix Matrix::operator+(const Matrix &aMatrix) const
{
if(rows == aMatrix.rows && cols == aMatrix.cols)
{
Matrix temp(rows, cols);
for(int i = 0; i < rows; i++)
{
for(int x = 0; x < cols; x++)
{
temp.element[i][x] = element[i][x] + aMatrix.element[i][x];
}
}
return temp;
}
else
{
cerr << "Undefined matrix addition -- matrices are different sizes."
<< endl;
return *this;
}
}
Any help or insight is appreciated. Thanks.
EDITED: Added overloaded assignment operator, copy constructor, and destructor code.
Below is the overloaded assignment operator:
Matrix Matrix::operator=(Matrix aMatrix)
{
if(this != &aMatrix)
{
for(int i = 0; i < rows; i++)
{
delete [] element[i];
element[i] = NULL;
}
delete [] element;
rows = aMatrix.rows;
cols = aMatrix.cols;
element = new int* [rows];
for(int i = 0; i < rows; i++)
{
element[i] = new int [cols];
for (int x = 0; x < cols; x++)
{
element[i][x] = aMatrix.element[i][x];
}
}
}
return *this;
}
Below is the copy constructor:
Matrix::Matrix(const Matrix &aMatrix)
{
rows = aMatrix.rows;
cols = aMatrix.cols;
element = new int* [rows];
for(int i = 0; i < rows; i++)
{
element[i] = new int [cols];
for (int x = 0; x < cols; x++)
{
element[i][x] = aMatrix.element[i][x];
}
}
}
Destructor:
Matrix::~Matrix()
{
for(int i = 0; i < rows; i++)
{
delete [] element[i];
element[i] = NULL:
}
delete [] element;
element = NULL;
}

You posted a copy constructor and assignment operator. Your assignment operator has 4 major issues:
You should pass the parameter by const reference, not by value.
You should return a reference to the current object, not a brand new object.
If new throws an exception during assignment, you've messed up your object by deleting the memory beforehand.
It is redundant. The same code appears in your copy constructor.
You can rewrite the assignment operator in terms of the copy constructor and alleviate these issues:
#include <algorithm>
//...
Matrix& Matrix::operator=(const Matrix& aMatrix)
{
Matrix temp(aMatrix);
swap(*this, temp);
return *this;
}
void Matrix::swap(Matrix& left, Matrix& right)
{
std::swap(left.rows, right.rows);
std::swap(left.cols, right.cols);
std::swap(left.element, right.element);
}
You just need to add the swap function to your Matrix class (probably as a private) function.
The code above uses the copy/swap idiom, and alleviates all of the issues I mentioned. This will work provided that your copy constructor and destructor are written properly. There are many threads on SO that talk about this idiom, but basically what is happening is this:
Create a temporary object from the passed-in object. If there is a problem with new throwing, your this doesn't get messed up.
Swap out this's members with the temporaries members. This refreshes the this object with the temporary's data, and gives the temporary the old data we don't need any more.
Let the temporary die with the old stuff we gave it from this.
Return this.
As to the other aspects, if the two Matrices cannot be multiplied, then just throw an exception. Do not return a bogus or confusing Matrix object that doesn't really reflect what happened.
In addition, you should first write operator += and *=. Why? Because implementing operator + and * can be done in terms of += and *=, respectively, plus you get the added bonus of having += and *= available.
For example:
Matrix& Matrix::operator+=(const Matrix &aMatrix)
{
if(rows != aMatrix.rows || cols != aMatrix.cols)
throw SomeException;
for(int i = 0; i < rows; i++)
{
for(int x = 0; x < cols; x++)
element[i][x] += aMatrix.element[i][x];
}
return *this;
}
Matrix Matrix::operator+(const Matrix &aMatrix)
{
Matrix temp(*this);
temp += aMatrix;
return temp;
}
So if the Matrix sizes are an issue, operator + and += will throw an exception. Note how operator + is implemented in terms of +=. Take the same approach to *, *=, and also - and -=.

You need to figure out what the syntax and semantics are of these operations. It looks from your example as though you want something like:
Matrix lhs = <something>;
Matrix rhs = <something>;
Matrix result = lhs * rhs;
to "just work". While this might be nice syntactically, you need to account for what happens if lhs and rhs can't be multiplied (or added, etc.) You also need to worry about setting up the result properly. You can't just return a "struct consisting of number of rows, columns and an array of pointers" and expect not to run into horrific memory management problems.
What will be responsible for making the product disappear? When it goes out of scope? C++ is not a very good language for linear algebra.
The first decision that you need to make is whether you intend to throw an exception for invalid operations, or return some indication of what went wrong, perhaps bool and a const char * to an error message.
Then you probably should try writing a function taking a result reference and 2 operand references, and just get the basic algebra right, if the matrices support the operation. (I have not mentioned yet that your Matrix is an integer matrix only.)
In "real life", you would probably try to find an open source standard solution that manages all of this, because numerical operations need to be managed very carefully.
One might even go so far as to suggest that this is a problem that has already been capably solved decades ago in Fortran, and to this day, if performance is an issue, there is a good chance that the underlying calculations might be in Fortran, or a library that was a port from C.

Related

Why override operators aren't working with pointer?

I need to write a class Matrix with override operators + - * = and I've got some code that is works, but there is error.
//Matrix.h
template <class T>
class Matrix
{
public:
Matrix(int rows, int columns);
Matrix(const Matrix<T> &m);
Matrix<T>& operator=(Matrix<T>& m);
Matrix<T> operator+(Matrix<T>& m) const;
Matrix<T>* operator*(Matrix<T>* m);
};
template <class T> Matrix<T>& Matrix<T>::operator*(Matrix<T>& m) {
Matrix<T>* newMatrix = new Matrix<T>(rowCount, m.colCount);
for (int i = 0; i < rowCount; ++i)
{
for (int j = 0; j < m.colCount; ++j)
{
newMatrix->data[i][j] = 0;
for (int k = 0; k < colCount; ++k)
newMatrix->data[i][j] += data[i][k] * m.data[k][j];
}
}
return *newMatrix;
}
And override operators work fine in this code
Matrix<int> matrix(2, 2);
matrix = matrix + matrix;
//and other operators work fine here
But here it gives an error during compilation
Matrix<int>* matrix = new Matrix<int>(2, 2);
matrix = matrix + matrix;
matrix = matrix * matrix;
//etc
error
error C2804: binary "operator +" has too many parameters
Type information is crutal in C++.
This:
Matrix<int> matrix(2, 2);
matrix = matrix + matrix;
Here the type of matrix is Matrix. You have defined what the operator + for the type Matrix so this works fine.
This second one is different:
Matrix<int>* matrix = new Matrix<int>(2, 2);
matrix = matrix + matrix;
Here the type of matrix is Matrix*. Notice the star on the end of Matrix (this makes it a Matrix Pointer). This is a different type than above. You have not defined what operator + does for Matrix* so the compiler looks at its default operations and finds something close but not exact enough and generates an appropriate error message that tries to help.
To use the operator + you defined above you need to make sure the types of the values are Matrix and NOT Matrix*. You can covert a Matrix* into a Matrix by dereferencing the pointer via operator *.
Matrix<int>* matrix = new Matrix<int>(2, 2);
(*matrix) = (*matrix) + (*matrix);
Here: (*matrix) dereferences the Matrix* object and you get a Matrix. This can now correctly be applied to the operator + you defined above.
But saying all that.
That explains whay your problem is, but I don't think you actually want (or need) to use new in this context.
Dynamically allocating memory like this is probably not the correct way to implement this. It is usually better to use automatic variables (as this makes memory management easier).
template <class T>
Matrix<T> Matrix<T>::operator*(Matrix<T>& m)
// I also removed the & from the return value
// So that the value is copied out of the function.
// because with dynamic allocation the result will disappear
// after the function exits.
// Note: Though the matrix may be officially copied out
// the compiler is likely to optimize away this copy.
// and when you upgrade your class with move semantics
// then it will definitely be moved.
{
Matrix<T> newMatrix(rowCount, m.colCount);
// Remove the Pointer from the above line.
// And fix the -> into . in the code below.
for (int i = 0; i < rowCount; ++i)
{
for (int j = 0; j < m.colCount; ++j)
{
newMatrix.data[i][j] = 0;
for (int k = 0; k < colCount; ++k)
newMatrix.data[i][j] += data[i][k] * m.data[k][j];
}
}
return newMatrix;
// Now you can remove the * from the return value.
// The code works exactly the same.
// But you have not leaked the memory you allocated with `new`
}

munmap_chunk(): invalid pointer in C++ program

I get an error "munmap_chunk(): invalid pointer", I don't know why. Problem appears when I use MultipliedByMatrix method. It can't properly delete the matrix that was created in this method.
#include "matrix.h"
Matrix::Matrix(int matr_size) {
size = matr_size;
Matr = new int *[size];
for(int i = 0; i < size; i++)
Matr[i] = new int[size];
for(int i = 0 ; i < size; i++)
for(int j = 0; j < size; j++)
Matr[i][j] = rand() % 100;
std::cout << "New matrix is created" << std::endl;
}
Matrix::~Matrix() {
for(int i = 0; i < size; i++)
delete[] Matr[i];
delete[] Matr;
Matr = NULL;
std::cout << "Matrix is deleted" << std::endl;
}
Matrix Matrix::MultipliedByMatrix(Matrix OtherMatr) {
Matrix new_matr = Matrix(this->GetSize());
int new_value;
for(int i = 0 ; i < size; i++)
for(int j = 0; j < size; j++) {
new_value = 0;
new_value += Matr[j][i] * OtherMatr.GetValue(i, j);
new_matr.SetValue(i, j, new_value);
}
return new_matr;
}
int Matrix::GetSize() {
return size;
}
int Matrix::GetValue(int i, int j) {
return Matr[i][j];
}
void Matrix::SetValue(int i, int j, int value) {
Matr[i][j] = value;
}
Did you write the Matrix class yourself? If so, I bet the problem is that you don't have a copy or move constructor. If so, the compiler will have generated one for you. This will copy the values of size and Matr but it won't create copies of the pointed-to arrays. When you write:
return new_matr;
this creates a new matrix (using the copy constructor - which just copies the pointer), and then calls the destructor of new_matr (which deletes the memory which is pointed to). The calling function is then dealing with junk memory, and when it tries to eventually delete the result, all hell will break loose
You also will need to write a move assignment operator.
Alternatively make Matr a std::vector<int> (of length 'size' squared), and write:
int Matrix::GetValue(int i, int j) {
return Matr[i*size+j];
}
(and similarly for other functions). std::vector has a proper copy and move constructor, and proper assignment behaviour - so it will all just work. (It will also be a lot faster - you save a whole pointer indirection.)
This is not an analytical answer to the question but a piece of advice with respect to solving (or better circumventing) the problem.
Avoid memory handling on your own if you can. (And it is very likely that you actually can avoid it.)
You can read my answer on the question "1D or 2D array, what's faster?" to get a lengthy explenation why it is probably undesirable to use the kind of memory layout you're using.
Furthermore, you'll find an (yet untested) example of how to implement a simple matrix container on top of std::vector.
You can look at the scheme and try to implement your own if you want. The design has several advantages compared to your implementation:
It is templated and thus usable with int as well as many other types.
Conformance to the standard container concept is achieved easily.
No destructor / copy constructor / move constructor or assignment operators required: std::vector is handling the resources and does the "dirty work" for you.
If you still want to use your RAW-Pointer approach (for academic purposes or something):
Read What is meant by Resource Acquisition is Initialization (RAII)? and try to understand the answers.
Read What is The Rule of Three? properly and make sure you have implemented (preferably obeying the RAII concept) those functions:
copy constructor,
destructor,
assignment operator and if desired
move constructor and
move assignment operator.
Still read my answer to the "1D or 2D array, what's faster?" question to see how you would want to organize your allocations in order to be exception safe in case of std::bad_alloc.
Example: Your constructor the 'a little better' way:
Matrix::Matrix(std::size_t const matr_size) // you have a size here, no sign required
{
Matr = new int*[matr_size];
std::size_t allocs(0U);
try
{ // try block doing further allocations
for (std::size_t i = 0; i < matr_size; ++i)
{
Matr[i] = new int[matr_size]; // allocate
++allocs; // advance counter if no exception occured
for(std::size_t j = 0; j < matr_size; j++)
{
Matr[i][j] = rand() % 100;
}
}
}
catch (std::bad_alloc & be)
{ // if an exception occurs we need to free out memory
for (size_t i = 0; i < allocs; ++i) delete[] Matr[i]; // free all alloced rows
delete[] Matr; // free Matr
throw; // rethrow bad_alloc
}
}

overload the + operator to add 2 polynomials

I'm trying to add two polynomials stored in a 2D array with the first rows store the coefficients and the second row store the degree.
I created a overloading + function but here I use 2D allocated memory array and it crashes when the function is called.
**Update: it runs now but the results are some big negative numbers
FYI, I have a copy constructor and an overloading = operator
// overload +
Polynomial Polynomial::operator+(const Polynomial &right)
{
maxExp = right.maxExp;
poly = new int *[maxExp];
for (int i=0; i < maxExp; i++)
*(poly + i) = new int[2];
Polynomial temp = *this;
for (int i=0; i < maxExp; i++)
{
for (int j=0; j < 2; j++)
{
temp.poly[i][j] = poly[i][j] + right.poly[i][j];
}
}
return temp;
}
just start with += and copy operators and make the + operator form that.
There are multiple problems, but the most glaring one is that the very first line:
delete [] poly;
destroys one of the summands (the x in x + y). It also leaks memory, but at this point this is secondary.
edit: I notice that you've edited the delete[] out of your question. This doesn't fix the problem, but makes it worse: you're still overwriting the summand, but now are leaking even more memory.

Operator overloading c++ advice

I am struggling to get my head around operator overloading. In this case the + operator I have and example of what I have tried, any help would be greatly appreciated.
I am getting an error which says "invaild use of 'class Matrix' I am unsure on how to fix this how can I add these two Matrix objects together?
Matrix Matrix::operator+(const Matrix& rhs){
return Matrix(Matrix + rhs.Matrix());
}
Matrix::Matrix(int MM, int NN){
M = MM;
N = NN;
data = new double[M * N];
for ( int i =0; i < M; i++)
{
for (int j = 0; j < N; j++)
{
data[i* N+j] = (double) 1000 + i*N+j;
// cout << data[i*N+j] <<"\t";
}
//cout <<"\n";
}
cout << "Matrix Constructor... (code to be implemented here!!!)\n";}
Thanks
rhs is a Matrix
calling a constructor like a method is very illegal
in Matrix + rhs, Matrix is not an identifier
Once you get your identifiers straightened out, *this + rhs is equivalent to this->operator+(rhs). It should be obvious from this that all you've done here is create an infinite recursion.
jpm's answer is very important to look at. Once you've fixed these things, you can look at mine.
Essentially, an operator overload is no different than any other function.
So, in the case of:
Matrix Matrix::operator+(const Matrix& rhs)
What you're really doing there is saying: add rhs to the current matrix, and return a new matrix. Your overload should not alter the current matrix. Help yourself and use a constant:
Matrix Matrix::operator+(const Matrix& rhs) const
A matrix addition like that should first check if the matrices have the same dimensions so that you can add them together, then loop through all "cells" and add them, and create a matrix out of that. For that, I'm guessing you'll need a second constructor, something like:
Matrix::Matrix(int MM, int NN, double values[])
{
M = MM;//TODO: change to width
N = NN;//TODO: change to height
data = new double[M*N];
for(int i < 0; i < M; i++)
for(int j < 0; j < N; j++)
data[i * N+j] = values[i * N+j];
}
return Matrix(Matrix + rhs.Matrix());
^^^^^^ ^^^^^^
You're using a type name (Matrix) where there should be an expression - that's what the compiler is complaining about. You're also trying to call the constructor on an existing object, which is invalid (and nonsensical). You also seem to be trying to implement operator+ in such a way that it calls itself; if you do somehow make it compile, then it will cause a stack overflow due to infinite recursion.
Perhaps the easiest way to implement addition is to implement operator+= to add one matrix to an existing one, and then implement operator+ in terms of that:
Matrix & Matrix::operator+=(Matrix const & rhs) {
// Perform addition here
for (int i = 0; i < N*M; ++i) {
data[i] += rhs.data[i];
}
return *this;
}
// This can be a non-member function
// Pass "lhs" by value to get a copy, then modify and return that.
Matrix operator+(Matrix lhs, Matrix const & lhs) {
return lhs += rhs;
}
// Or if you really want a member function for some reason
Matrix Matrix::operator+(Matix const & rhs) const {
return Matrix(*this) += rhs;
}
This does require you to have a correct copy constructor - since you're allocating memory yourself in the constructor, and presumably deallocating it in the destructor, you must implement a copy-constructor to correctly allocate new memory (per the Rule of Three), otherwise you'll get a double-deletion after copying a matrix.

Bad Access Error in 2D-Array (Matricies)

I have a little bit of a problem... I understand what a EXC_BAD_ACCESS error is and I generally know how to fix it but this one has got me completely stuffed. I have this all within a class, here is one method:
double Matrix::get_element(int r, int c) const {
//Retrieve the element at row r and column c
//Should not modify the value stored in Matrix but return a double copy of the value
double currentValue = matrix[r][c];
return currentValue;
}
Now, I have another piece of my code that calls this method:
std::string Matrix::to_string() const {
std::string result;
double current;
Matrix working = *this;
std::ostringstream oss;
oss << "[";
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
current = 0.0;
current = working.get_element(i, j);
oss << " " << current << " ";
}
oss << "; ";
}
oss << "]";
result = oss.str();
return result;
}
I know that the working object has 3 rows and 3 cols at the point where working.get_element(i, j); is called. The variable list shows me just before the get_element() method, that both rows and cols are set to 3. In the method, I'm able to get the value at get_element(0, 0) but not get_element(0, 1).
I can't see why this is the case... Anyone know why or require more of my code to understand why these methods are being called?
EDIT:
Here is the header file:
class Matrix {
private:
//Any variables required
int rows;
int cols;
double **matrix;
public:
Matrix(); //Working M
~Matrix(); //Working M
Matrix(int r, int c); //Working M
int getRows();
int getCols();
void set_element(int r, int c, double val); //Working M
double get_element(int r, int c) const; //Working M
void clear(); //Working M
bool is_empty(); //Working M
bool is_identity(); //Working M
const Matrix transpose(); //Working M
int minorMat(double **dest, const int row, const int col, int order); //Working M
double get_determinent(); //Working M
double higherDeterminents(int order); //Working M
const Matrix operator+(const Matrix &rhs); //Working M
const Matrix operator-(const Matrix &rhs); //Working M
const Matrix operator*(const Matrix &rhs);
bool operator==(const Matrix &rhs); //NOT assessed
const Matrix operator*(const double &rhs);
const Matrix operator/(const double &rhs);
Matrix & operator=(const Matrix &rhs);
std::string to_string() const;
};
Do ignore the comments sorry. And this is the constructors/destructors:
Matrix::Matrix() {
//Basic Constructor
rows = 1;
cols = 1;
matrix = new double*[rows];
for (int i = 0; i < rows; ++i) {
matrix[i] = new double[cols];
}
}
Matrix::~Matrix() {
//Basic Deconstructor
for (int i = 0; i < rows; ++i) {
delete[] matrix[i];
}
delete[] matrix;
rows = NULL;
cols = NULL;
matrix = NULL;
}
Matrix::Matrix(int r, int c) {
//Empty matrix (all 0's) with r rows and c columns, if they are -ve, set to 1
rows = r;
cols = c;
if (cols < 0)
cols = 1;
if (rows < 0)
rows = 1;
matrix = NULL;
matrix = new double*[rows];
for (int i = 0; i < rows; i++) {
matrix[i] = new double[cols];
}
}
EDIT2:
Matrix & Matrix::operator=(const Matrix &rhs) {
//rhs is matrix to be copied
//rhs compied into Matrix called on
double toCopy;
for (int i = 0; i < rhs.rows; i++) {
for (int j = 0; j < rhs.cols; j++) {
toCopy = rhs.get_element(i, j);
this->set_element(i, j, toCopy);
}
}
return *this;
}
It is impossible for us to say when you do not state how you declare and initialize the matrix element. Using something like that in your CTOR should be fine:
class Matrix {
float matrix[3][3];
...
}
Don't forget to initialize it in your CTOR to something that makes sense.
Btw: why do you do this: Matrix working = *this; ?? You could simply this->get_element(i, j); instead, which would not invoke copying of your whole object. [1]
EDIT: Update since you updated your answer. You should be careful with your copy CTORs and operator=() statements. It is easily possible to make double deletes or something ugly like that.
EDIT2: I think the problem is this line:
Matrix working = *this;
You are creating a new copy working of your this object. But working gets initialized with only 1 column and 1 row (as defined in your standard CTOR). I'm not sure if you are checking the bounds when calling set_element or get_element so I guess you are writing over the bounds of your arrays.
I think the best idea is to just remove the line Matrix working = *this; and to adhere to my tip in above:
this->get_element(i, j); in std::string Matrix::to_string() const.
Your Matrix(int r, int c) allocates memory for matrix, but leaves the values it points to uninitialized. Add something like this to the constructor:
for (int i = 0; i < r; i++) {
for (int j = 0; j < c; j++) {
this->set_element(i, j, 0);
}
}
Doing like this:
int main()
{
Matrix m(3,3);
std::cout << m.to_string();
}
Output:
[ 0 0 0; 0 0 0; 0 0 0; ].
Same thing in the default constructor:
Matrix::Matrix() {
//Basic Constructor
rows = 1;
cols = 1;
matrix = new double*[rows];
for (int i = 0; i < rows; ++i) {
matrix[i] = new double[cols];
}
}
You allocated memory, but wherever matrix[0][0] points to, it's uninitialized garbage value. Do something like matrix[0][0] = 0; or whatever default value you want it to have.
Hope that helps.
Your class is violating the "big three" rule. If a class has one of a destructor, assignment operator or copy constructor then it most probably you need to have all three.
In your case you have a destructor, but no assignment operator or copy constructor, and this is going to create an UB condition when you do Matrix working = *this.
By the way there are two other problems with your code
From the comment seems that you think that new double[size] will initialize the elements to 0 and this is not true.
Most of your code is technically very bad. Your matrix class would be much easier (less code) to implement correctly using std::vector instead of pointers and dynamic memory. Of course if this is just an exercise then it make sense to avoid using std::vector.
By the way if you never heard about the "big three" rule chances are that you're trying to learn C++ with experimentation instead that by reading.
With C++ this is not a smart move... logic can be used as substitute for study if 1) the topic is highly logical, 2) if you can be told when you get it wrong.
Instead C++ is very very complex and in a few place is also quite illogical (for historical reasons) so there are parts in which logic will simply misguide you.
Moreover when you make a mistake in C++ you don't get in general an error message, but "Undefined Behavior". This basically makes very very hard to learn C++ with experimentation because even wrong code may apparently work. It is also very easy to write code that looks good and that is instead quite wrong for subtle reasons.
Instead of just experimenting you should grab a good book and read it cover to cover...