My friend Ozgur Macit asked me if there is a way to implement auto pointer design pattern for ANSI C functions like malloc and free.
Here is the traditional way to write a function:
int traditional()
{
char*ptr = (char*)malloc( sizeof(char) );
//check the return value if ptr is NULL or not
//use this ptr in several operations
//if statements, switch statements, pass as argument to other functions etc.
free(ptr);
return 0; //EXIT POINT
}
if function returns from the EXIT POINT, we all know that "free" function gives the memory back. However what if the function throws in the middle of the execution, or returns for some reason. Then we should handle all of these cases and write the free(ptr); statement before return points. This makes code less maintainable as well as more error prone.
Lets implement our scope guard class which will provide a safe and easy way to prevent this memory to be leaked.
What will we do is basically creating a local object which will be destroyed when it goes out of scope. That is way i call it scope guard.
It is clear that, when it goes out of scope, it should call "free" function with the necessary parameter.
class scope_guard{
void* _MyPtr;
public:
scope_guard( void* ptr ): _MyPtr( ptr ){}
~scope_guard(){ free(_MyPtr); }
};
It is pretty straightforward, isn`t it? It takes the pointer and frees it when local object goes out of scope.
Here is a simple usage:
int contemporary(){
char*ptr = (char*)malloc( sizeof(char) );
if(!ptr){
//log error message and return error code
}
//here we go..
scope_guard guard( ptr );
//use ptr in several operations...
return 0; //no explicit call to free. guard object will do this for us..
}
You should create the guard object after checking the malloc result and BEFORE using this pointer in any operations. Because semantic of creating a guard object is providing an error handling mechanism. if you use ptr in any operation before creating guard object and if this operation fails, you are in trouble now..
Note that the class i wrote here is a simple example for demonstration purpose. You can add template and function pointer concepts to this class to design a generic scope guard class which can work for lots of functions. Clearly, this one is craeted for only "malloc" and "free" pair.
You can also put copy constructor, assignment operator as private methods since they are meaningless in this case.
Thanks for reading this post, any comments will be appreciated.
Thursday, July 31, 2008
Wednesday, July 30, 2008
Do you want to stop the time or do time travel to past?
This post dedicated to a non-computer science topic which is related to time. Time has a broad definition. Moreover if you think computer clock it is also a time. However todays topic is not too much related with computer clocks...
If you look at the subject, i mentioned about freezing the time and time traveling ?
Are these possible ? Can future`s technology let us go ? I will be trying to find some answers to these questions in this post.
Before reading the rest of this post please note that all these statements and ideas are mine and may be wrong or not. Don`t assume that what is written here is totally correct.
Before trying to finds an answer to time related questions, first lets define the time.
I have been thinking on this topic for a while. I think I am satisfied with my definitions. According to my opinion (i wont repeat this again since all ideas are my own truths) , Time is a "action" , "movement" , "motion" of an object or object groups. As you can see all these words are related with an energy of an object which makes "changes" on this object. In other words it changes the state of the object.
Indeed i haven`t looked at the literature definition of time (maybe it has the same definition). You may wonder, Why am i thinking like that? I will try to explain.
Before giving an example on a live object, i want to explain my thoughts on a simple object. Lets assume that our simple object is a "glass". Is time passing for the glass ? what is the meaning of time for this glass ? Let me explain..
If you apply a force to this glass and create a scratch on this, this glass will "change" its state. So "before" the scratch and "after" the scratch defines a time for this glass. ( Clearly, "before" and "after" are the words which are related with time).
Now, put this glass to a space. A space that no movement takes place in this space. No air ( no friction ), no waves inside it, nothing moves inside this space. All things remain static. Put the glass inside this and come back X years later. ( Note that you can not see the glass during this X year since there is nothing inside the space except glass. this means there is no photon! ). Glass is still the same glass if all things remain same inside the space during this X years. There is no concept such as time for this glass during this X years. How can it be? It is like freezing, no state changed, no events occurred, everything remains same like stopping the time for this glass! When you take it out of this space, time starts to pass again!
What about live objects? Can time also be stopped for them. If you go to such a space that we discussed for the glass, everything can be remain static except you! What does it mean? It means that, if you want to stop the time for you, you should stop breathing, your heart should stop beating, your eyes must not see anything, your ears must not hear anything. In other words, stop your neural system as well as other human systems. No impulse, no flow in your veins. This sounds pretty crazy, doesn`t it? I wont discuss if this is practicable or not. But these are the conditions that must be satisfied if you want to stop the time for yourself. I am not sure freezing your self like in the movies works for achieve this but if you are persistent give it a shot. let us know, if it works :)
If you read carefully, i am not talking about stopping the time by applying some mystic powers. You can not stop the time for other objects by your thoughts or your body energy. Some certain conditions must have satisfied.
So the answer to "is stopping the time possible" is yes. However some restrictions are applied. It is a experiment rather than experience.
In addition, my ideas are in the same page with relativity theory. I mentioned about stopping the time for certain objects. Time may be stopped for an object, but it is passing for other objects. One proof to my claim ( time is an "action" ... ) is that;
when you have no works to do, you are generally bored. You generally look your watch, time doesn`t pass quickly. 10 minutes may be like 1 hour. Do you know why? When you have no works to do, there is less action in your body. Less functionality, slow motions. So time becomes slower for you.
However when you are bored, your friend should be hurry, otherwise he miss the flight! He should prepare his luggage ASAP. Time passes quickly for him. Because he increase his "action". This is the relativity. Relativity is a difference between the velocity of two object actions.
Now, we answered one question. Lets look at the other one. Is time travel to past possible?
Since we have pretty much idea about the time now wen can answer this question quicker.
For returning back in time, you have to "undo" the actions. Can you revert an action which is already happened ? Answer is no. You can not revert it. What was happened was happened. There is no way to go back.
For explaining further, lets go back to our glass example. You scratch the glass and you are regretful now. You wish to go back in time and stop this event. You put the glass to our famous "space" to stop the time. everything is ok till now. You stopped the time for the glass, but how can you go back in time ? Once you stop all of the actions on this glass time is stopped for the glass. For reverting back, you should do some !magic!. Even if you have abilities to do some magic; if you do so, you will apply some action to that glass which causes to start the time for the glass.
So we have a pretty quick answer for time traveling. I am sorry, if i cause disappointments.
At the end of the day, you now have some ideas about time concepts.
Time is another dimension which consists of movements. If you want to stop it, you should stop the actions. If you want to revert it then good luck!
If you look at the subject, i mentioned about freezing the time and time traveling ?
Are these possible ? Can future`s technology let us go ? I will be trying to find some answers to these questions in this post.
Before reading the rest of this post please note that all these statements and ideas are mine and may be wrong or not. Don`t assume that what is written here is totally correct.
Before trying to finds an answer to time related questions, first lets define the time.
I have been thinking on this topic for a while. I think I am satisfied with my definitions. According to my opinion (i wont repeat this again since all ideas are my own truths) , Time is a "action" , "movement" , "motion" of an object or object groups. As you can see all these words are related with an energy of an object which makes "changes" on this object. In other words it changes the state of the object.
Indeed i haven`t looked at the literature definition of time (maybe it has the same definition). You may wonder, Why am i thinking like that? I will try to explain.
Before giving an example on a live object, i want to explain my thoughts on a simple object. Lets assume that our simple object is a "glass". Is time passing for the glass ? what is the meaning of time for this glass ? Let me explain..
If you apply a force to this glass and create a scratch on this, this glass will "change" its state. So "before" the scratch and "after" the scratch defines a time for this glass. ( Clearly, "before" and "after" are the words which are related with time).
Now, put this glass to a space. A space that no movement takes place in this space. No air ( no friction ), no waves inside it, nothing moves inside this space. All things remain static. Put the glass inside this and come back X years later. ( Note that you can not see the glass during this X year since there is nothing inside the space except glass. this means there is no photon! ). Glass is still the same glass if all things remain same inside the space during this X years. There is no concept such as time for this glass during this X years. How can it be? It is like freezing, no state changed, no events occurred, everything remains same like stopping the time for this glass! When you take it out of this space, time starts to pass again!
What about live objects? Can time also be stopped for them. If you go to such a space that we discussed for the glass, everything can be remain static except you! What does it mean? It means that, if you want to stop the time for you, you should stop breathing, your heart should stop beating, your eyes must not see anything, your ears must not hear anything. In other words, stop your neural system as well as other human systems. No impulse, no flow in your veins. This sounds pretty crazy, doesn`t it? I wont discuss if this is practicable or not. But these are the conditions that must be satisfied if you want to stop the time for yourself. I am not sure freezing your self like in the movies works for achieve this but if you are persistent give it a shot. let us know, if it works :)
If you read carefully, i am not talking about stopping the time by applying some mystic powers. You can not stop the time for other objects by your thoughts or your body energy. Some certain conditions must have satisfied.
So the answer to "is stopping the time possible" is yes. However some restrictions are applied. It is a experiment rather than experience.
In addition, my ideas are in the same page with relativity theory. I mentioned about stopping the time for certain objects. Time may be stopped for an object, but it is passing for other objects. One proof to my claim ( time is an "action" ... ) is that;
when you have no works to do, you are generally bored. You generally look your watch, time doesn`t pass quickly. 10 minutes may be like 1 hour. Do you know why? When you have no works to do, there is less action in your body. Less functionality, slow motions. So time becomes slower for you.
However when you are bored, your friend should be hurry, otherwise he miss the flight! He should prepare his luggage ASAP. Time passes quickly for him. Because he increase his "action". This is the relativity. Relativity is a difference between the velocity of two object actions.
Now, we answered one question. Lets look at the other one. Is time travel to past possible?
Since we have pretty much idea about the time now wen can answer this question quicker.
For returning back in time, you have to "undo" the actions. Can you revert an action which is already happened ? Answer is no. You can not revert it. What was happened was happened. There is no way to go back.
For explaining further, lets go back to our glass example. You scratch the glass and you are regretful now. You wish to go back in time and stop this event. You put the glass to our famous "space" to stop the time. everything is ok till now. You stopped the time for the glass, but how can you go back in time ? Once you stop all of the actions on this glass time is stopped for the glass. For reverting back, you should do some !magic!. Even if you have abilities to do some magic; if you do so, you will apply some action to that glass which causes to start the time for the glass.
So we have a pretty quick answer for time traveling. I am sorry, if i cause disappointments.
At the end of the day, you now have some ideas about time concepts.
Time is another dimension which consists of movements. If you want to stop it, you should stop the actions. If you want to revert it then good luck!
Wednesday, July 23, 2008
Avoid Memory Leaks with Auto Pointers
Hey Guys, i havent wroten a post for a long time.
Today i want to talk about Auto Pointers. It is a basic but powerful design pattern to prevent memory leaks.
When we are writing c++ programs we generally allocate some memory from heap and use it during the lifetime of a block/function/thread/process.. In most cases a careless programming causes leaking the memory. In case of an Exception or return statement we should give the memory back to OS, if we no longer need that heap area.
Let me introduce an example:
bool leak(){
char* pBuffer = new char[ MAX_BUFFER_LEN ];
// control , initialize or do smth with buffer
..................................
int rc = function1( pBuffer );
if( OK != rc )
return false; //Leak Memory
try{
rc = function2(); //function2 may throw
}catch( ... ){
return false; //Leak Memory
}
//Use buffer more
................................
delete[] pBuffer;
return true;
}
Obviously above function leaks memory if it returns false because of unsatisfied conditions or exceptions. If you are lucky your program just leaks the memory, but if system runs out of memory then your process will crash.
One way to avoid this leak is putting the delete[] pBuffer; statement to every return path. But this is not the best way, programmers may forget to do this or later another programmer may want to change this function and he may not have enough knowledge about all functions in big projects. Someone else can easily put a new return statement without releasing the memory. Besides that, code will become longer, if every return path contains resource cleanup statements.
Look at this:
bool leak(){
char* pBuffer = new char[ MAX_BUFFER_LEN ];
// control , initialize or do smth with buffer
..................................
int rc = function1( pBuffer );
if( OK != rc ){
delete[] pBuffer; //give memory back
return false;
}
try{
rc = function2(); //function2 may throw
}catch( ... ){
delete[] pBuffer; //give memory back
return false;
}
//Use buffer more (Another programmer may add new statements which may introduce new leaks
................................
delete[] pBuffer;
return true;
}
Auto Pointers make all this process easy. They are smart pointers which provides automatic memory deallocation.
now i am writing the same example with auto pointer:
bool leak(){
AutoVectorPtr pBuffer ( new char[ MAX_BUFFER_LEN ] );
// control , initialize or do smth with buffer
..................................
int rc = function1( pBuffer );
if( OK != rc )
return false;
try{
rc = function2(); //function2 may throw
}catch( ... ){
return false;
}
//Use buffer more
................................
return true;
}
So there are no delete[] statements, code is cleaner and more understandable. Even some other programmers add new return statements memory will never leak.
What is AutoVectorPtr ? It is a template class which takes a pointer to its constructor and deletes it in its destructor. Since it is a local object created in the function scope, it is guaranteed that it will be destroyed ( delete[] will be executed ) when function goes out of scope whether it returns or throws ( thanks to stack unwinding! )
Simple AutoVectorPtr is :
template
class AutoVectorPtr {
T* _MyPtr;
public:
AutoVectorPtr( T* ptr ) : _MyPtr( ptr ){}
~AutoVectorPtr(){ delete[] _MyPtr; }
T* operator()(){ return _MyPtr; }
};
Above is a simple AutoVectorPtr class for giving an idea what it looks like. It should also contain copy constructor, operator= and some more.
I named it as AutoVectorPtr because it makes operations on array. If it holds single item like int, char or any class object, we can name it something like AutoPtr.
There are some libraries which provide this kind of smart pointers.
Here is the wiki link: http://en.wikipedia.org/wiki/Auto_ptr
Today i want to talk about Auto Pointers. It is a basic but powerful design pattern to prevent memory leaks.
When we are writing c++ programs we generally allocate some memory from heap and use it during the lifetime of a block/function/thread/process.. In most cases a careless programming causes leaking the memory. In case of an Exception or return statement we should give the memory back to OS, if we no longer need that heap area.
Let me introduce an example:
bool leak(){
char* pBuffer = new char[ MAX_BUFFER_LEN ];
// control , initialize or do smth with buffer
..................................
int rc = function1( pBuffer );
if( OK != rc )
return false; //Leak Memory
try{
rc = function2(); //function2 may throw
}catch( ... ){
return false; //Leak Memory
}
//Use buffer more
................................
delete[] pBuffer;
return true;
}
Obviously above function leaks memory if it returns false because of unsatisfied conditions or exceptions. If you are lucky your program just leaks the memory, but if system runs out of memory then your process will crash.
One way to avoid this leak is putting the delete[] pBuffer; statement to every return path. But this is not the best way, programmers may forget to do this or later another programmer may want to change this function and he may not have enough knowledge about all functions in big projects. Someone else can easily put a new return statement without releasing the memory. Besides that, code will become longer, if every return path contains resource cleanup statements.
Look at this:
bool leak(){
char* pBuffer = new char[ MAX_BUFFER_LEN ];
// control , initialize or do smth with buffer
..................................
int rc = function1( pBuffer );
if( OK != rc ){
delete[] pBuffer; //give memory back
return false;
}
try{
rc = function2(); //function2 may throw
}catch( ... ){
delete[] pBuffer; //give memory back
return false;
}
//Use buffer more (Another programmer may add new statements which may introduce new leaks
................................
delete[] pBuffer;
return true;
}
Auto Pointers make all this process easy. They are smart pointers which provides automatic memory deallocation.
now i am writing the same example with auto pointer:
bool leak(){
AutoVectorPtr
// control , initialize or do smth with buffer
..................................
int rc = function1( pBuffer );
if( OK != rc )
return false;
try{
rc = function2(); //function2 may throw
}catch( ... ){
return false;
}
//Use buffer more
................................
return true;
}
So there are no delete[] statements, code is cleaner and more understandable. Even some other programmers add new return statements memory will never leak.
What is AutoVectorPtr ? It is a template class which takes a pointer to its constructor and deletes it in its destructor. Since it is a local object created in the function scope, it is guaranteed that it will be destroyed ( delete[] will be executed ) when function goes out of scope whether it returns or throws ( thanks to stack unwinding! )
Simple AutoVectorPtr is :
template
class AutoVectorPtr {
T* _MyPtr;
public:
AutoVectorPtr( T* ptr ) : _MyPtr( ptr ){}
~AutoVectorPtr(){ delete[] _MyPtr; }
T* operator()(){ return _MyPtr; }
};
Above is a simple AutoVectorPtr class for giving an idea what it looks like. It should also contain copy constructor, operator= and some more.
I named it as AutoVectorPtr because it makes operations on array. If it holds single item like int, char or any class object, we can name it something like AutoPtr.
There are some libraries which provide this kind of smart pointers.
Here is the wiki link: http://en.wikipedia.org/wiki/Auto_ptr
Wednesday, September 12, 2007
Awake from the darkness..
Hi , It has been a long time haa? Since i wrote my last post to this blog, i lost my mind:) There are many things changed, i was graduated in July from both of my major.. Now, it is happening again, i decide to write something in order to be alive.. You can not live in fair my man :p So take a look at that sezar crypto algorithm: ( it is not just complete but very near :)
yea, i want to begin with C, asm and pointers :) It is just a begining it is not the end.. C U Soon guys..
#include<stdio.h>
void encode( char* str ){
asm (
"movl %0,%%ebx\n\t" //store the string address in ebx register
"eback:\n\t" //loop scans the string character
"movb (%%ebx), %%al\n\t" //put next character to eax register
"cmp $0, %%al\n\t" //look at string termination, NULL character
"je efinish\n\t" //jump to finish point if null is found
"addb $3, %%al\n\t" //forward 3 characters front..
"movb %%al, (%%ebx)\n\t" //put encrypted character to its place
"addl $1, %%ebx\n\t" //go to next character
"jmp eback\n\t"
"efinish:\n\t"
:
:"g"(str)
:"%ebx" //ebx is marked as changed register
);
}
void decode( char* str ){
asm (
"movl %0,%%ebx\n\t" //store the string address in ebx register
"dback:\n\t" //loop scans the string character
"movb (%%ebx), %%al\n\t" //put next character to eax register
"cmp $0, %%al\n\t" //look at string termination, NULL character
"je dfinish\n\t" //jump to finish point if null is found
"subb $3, %%al\n\t" //forward 3 characters back..
"movb %%al, (%%ebx)\n\t" //put encrypted character to its place
"addl $1, %%ebx\n\t" //go to next character
"jmp dback\n\t"
"dfinish:\n\t"
:
:"g"(str)
:"%ebx" //ebx is marked as changed register
);
}
int main(){
char message[] = "Hi this is emreknlk";
/*original message*/
printf("original \tmessage: %s\n",message);
/* encode the message:*/
encode( message );
printf("encoded \tmessage: %s\n",message);
/*decode the message*/
decode( message );
printf("decoded \tmessage: %s\n",message);
return 0;
}
output:
D:\My Documents\itu\cc>sezar_asm.exe
original message: Hi this is emreknlk
encoded message: Kl#wklv#lv#hpuhnqon
decoded message: Hi this is emreknlk
yea, i want to begin with C, asm and pointers :) It is just a begining it is not the end.. C U Soon guys..
Sunday, April 01, 2007
Explore yourself before expoloring snakes
This post's title is written for my friend ozgur Macit. He llikes playing with snakes and i write this post in memory of him:)
Tonight i write some piece of code about binary trees. it is ordered. i mean, smaller values are in the left side, bigger values are in the right side.
The code probably isnt bug free because i didnt test it at all. I want to show you some basic concepts in C and C++. This code isnt written in C because i use templates, but i dont use the OOP also. It is C++ in C :)
Please take care of pointers, pointer to pointers, recursion, binary tree, template and other things which lies in the code...
I write most of the functions for solving problems in http://cslibrary.stanford.edu/110/BinaryTrees.html
I especially like hasPathSum function. It is very short and useful.
Firstly here is a test module:
#include"binaryTree.h"
#include<iostream>
using namespace std;
int main(){
BinaryTree<int> tree;
addNode( &tree.root , 100 );
addNode( &tree.root , 500 );
addNode( &tree.root , 200 );
addNode( &tree.root , 70 );
addNode( &tree.root , 10 );
addNode( &tree.root , 340 );
addNode( &tree.root , 324 );
printTreeInorder( tree.root );
cout << "size is: "<< size(tree.root )<<endl;
cout << "max depth is: "<< maxDepth(tree.root )<<endl;
cout << "min value is: "<< minValue(tree.root )<<endl;
cout << "max value is: "<< maxValue(tree.root )<<endl;
cout << "haspathsum ( 180 ) value is: "<< boolalpha << hasPathSum(tree.root , 180 )<<endl;
mirror(tree.root );
printTreeInorder( tree.root );
return 0;
}
And the binaryTree.h
#ifndef binaryTree
#define binaryTree
template <typename T>
struct Node{
T data;
Node* left;
Node* right;
Node( T dt )
{
data = dt;
left = right=reinterpret_cast<Node<T>*>(0);
}
};
template<typename T>
struct BinaryTree{
Node<T>* root;
BinaryTree()
{
root = reinterpret_cast<Node<T>*>(0);
}
};
template<typename T>
Node<T>* getNode( T data ) {
return new Node<T>( data );
}
template<typename T>
bool addNode( Node<T>** root , T data ){
try{
if(!*root){
*root = getNode( data);
}else{
if( data < (*root)->data )
addNode(&(*root)->left , data );
else
addNode(&(*root)->right,data);
}
}catch(...){
return false;
}
return true;
}
template<typename T>
int size( Node<T>* root ){
if( !root )
return 0;
if( !root->left && !root->right )
return 1;
else{
return size( root->left ) + size( root->right ) + 1;
}
}
template<typename T>
int maxDepth( Node<T>* root ){
if(!root)
return -1;
else{
int leftSize , rightSize;
leftSize = 1 + maxDepth( root->left );
rightSize = 1 + maxDepth( root->right );
return rightSize > leftSize ? rightSize : leftSize;
}
}
template<typename T>
T minValue( Node<T>* root ){
T dummy;
if( !root ){
return dummy;
}
if( root->left )
return minValue( root->left );
else
return root->data;
}
template<typename T>
T maxValue( Node<T>* root ){
T dummy;
if( !root ){
return dummy;
}
if( root->right )
return maxValue( root->right );
else
return root->data;
}
bool hasPathSum(struct Node<int>* root, int sum) {
if( !root )
return ( sum == 0 );
else{
return ( hasPathSum(root->left , sum - root->data ) || hasPathSum(root->right , sum - root->data ) );
}
}
template<typename T>
void mirror( Node<T>* root ){
if( !root->left && !root->right)
return;
if( root->left )
mirror(root->left);
if( root->right)
mirror(root->right);
Node<T>* t;
t = root->left;
root->left = root->right;
root->right = t;
}
template<typename T>
void printTreePreorder( Node<T>* root ){
if( root ){
cout << root->data << endl;
printTreePreorder( root->left );
printTreePreorder( root->right );
}
}
template<typename T>
void printTreeInorder( Node<T>* root ){
if( root ){
printTreeInorder( root->left );
cout << root->data << endl;
printTreeInorder( root->right );
}
}
template<typename T>
void printTreePostorder( Node<T>* root ){
if( root ){
printTreePostorder( root->left );
printTreePostorder( root->right );
cout << root->data << endl;
}
}
#endif
Tonight i write some piece of code about binary trees. it is ordered. i mean, smaller values are in the left side, bigger values are in the right side.
The code probably isnt bug free because i didnt test it at all. I want to show you some basic concepts in C and C++. This code isnt written in C because i use templates, but i dont use the OOP also. It is C++ in C :)
Please take care of pointers, pointer to pointers, recursion, binary tree, template and other things which lies in the code...
I write most of the functions for solving problems in http://cslibrary.stanford.edu/110/BinaryTrees.html
I especially like hasPathSum function. It is very short and useful.
Firstly here is a test module:
#include"binaryTree.h"
#include<iostream>
using namespace std;
int main(){
BinaryTree<int> tree;
addNode( &tree.root , 100 );
addNode( &tree.root , 500 );
addNode( &tree.root , 200 );
addNode( &tree.root , 70 );
addNode( &tree.root , 10 );
addNode( &tree.root , 340 );
addNode( &tree.root , 324 );
printTreeInorder( tree.root );
cout << "size is: "<< size(tree.root )<<endl;
cout << "max depth is: "<< maxDepth(tree.root )<<endl;
cout << "min value is: "<< minValue(tree.root )<<endl;
cout << "max value is: "<< maxValue(tree.root )<<endl;
cout << "haspathsum ( 180 ) value is: "<< boolalpha << hasPathSum(tree.root , 180 )<<endl;
mirror(tree.root );
printTreeInorder( tree.root );
return 0;
}
And the binaryTree.h
#ifndef binaryTree
#define binaryTree
template <typename T>
struct Node{
T data;
Node* left;
Node* right;
Node( T dt )
{
data = dt;
left = right=reinterpret_cast<Node<T>*>(0);
}
};
template<typename T>
struct BinaryTree{
Node<T>* root;
BinaryTree()
{
root = reinterpret_cast<Node<T>*>(0);
}
};
template<typename T>
Node<T>* getNode( T data ) {
return new Node<T>( data );
}
template<typename T>
bool addNode( Node<T>** root , T data ){
try{
if(!*root){
*root = getNode( data);
}else{
if( data < (*root)->data )
addNode(&(*root)->left , data );
else
addNode(&(*root)->right,data);
}
}catch(...){
return false;
}
return true;
}
template<typename T>
int size( Node<T>* root ){
if( !root )
return 0;
if( !root->left && !root->right )
return 1;
else{
return size( root->left ) + size( root->right ) + 1;
}
}
template<typename T>
int maxDepth( Node<T>* root ){
if(!root)
return -1;
else{
int leftSize , rightSize;
leftSize = 1 + maxDepth( root->left );
rightSize = 1 + maxDepth( root->right );
return rightSize > leftSize ? rightSize : leftSize;
}
}
template<typename T>
T minValue( Node<T>* root ){
T dummy;
if( !root ){
return dummy;
}
if( root->left )
return minValue( root->left );
else
return root->data;
}
template<typename T>
T maxValue( Node<T>* root ){
T dummy;
if( !root ){
return dummy;
}
if( root->right )
return maxValue( root->right );
else
return root->data;
}
bool hasPathSum(struct Node<int>* root, int sum) {
if( !root )
return ( sum == 0 );
else{
return ( hasPathSum(root->left , sum - root->data ) || hasPathSum(root->right , sum - root->data ) );
}
}
template<typename T>
void mirror( Node<T>* root ){
if( !root->left && !root->right)
return;
if( root->left )
mirror(root->left);
if( root->right)
mirror(root->right);
Node<T>* t;
t = root->left;
root->left = root->right;
root->right = t;
}
template<typename T>
void printTreePreorder( Node<T>* root ){
if( root ){
cout << root->data << endl;
printTreePreorder( root->left );
printTreePreorder( root->right );
}
}
template<typename T>
void printTreeInorder( Node<T>* root ){
if( root ){
printTreeInorder( root->left );
cout << root->data << endl;
printTreeInorder( root->right );
}
}
template<typename T>
void printTreePostorder( Node<T>* root ){
if( root ){
printTreePostorder( root->left );
printTreePostorder( root->right );
cout << root->data << endl;
}
}
#endif
Wednesday, March 21, 2007
Incredible Merge Sort :)
Today, i have written lots of sorting algorithms like bubble sort, selection sort, insertion sort, shell sort etc.
However, i have never implemented a merge sort which uses no temporary list to store the sorted sequences. And i have decided a merge sort algorithm like that.
It has some computational complexity but the main idea is simple. Every time in the merge operation we merge two lists which are indeed part of a one list! So i can use them as one list. It means if I overflow in the first list then i reach the second list! Also you should note that, it has some extra costs because i have to shift array elements for placing them to correct locations. This cost doesnt exist if you use extra lists to store the sorted sequences.
The merge code isnt so clear, may be you dont understand some parts because i dont give importance to naming conventions. Maybe i can revise and post it later. But now, i want to show you the code. If you find some bug please contact with me :)
template<typename T>
void mergeSort( T* arr , int len ){
int left, right, middle;
if( !arr || len < 2)
return;
left = 0;
right = len;
middle = left + ((right-left)/2 );
mergeSort( arr , middle - left );
mergeSort( arr+middle , right - middle);
merge(arr , middle-left , right-middle);
}
template<typename T>
void merge( T* left , int len1 , int len2 ){
int i,j ,k, ind , total = len1 + len2;
T val;
for(i = 0, j = 0; i < j+len1 && j < len2 ;){
if( left[i] > left[j+len1]){
val = left[ j + len1 ];
ind = j + len1 - i ;
k = 0;
while( ind >= 0 ){
left[j + len1 + k] = left[j + len1 +k -1];
k--;
ind--;
}
left[i] = val;
j++;
}else
i++;
}
}
For calling mergesort algorithm, just create an array of integers, chars or anything which overloads the > operator and call the method by giving the array and its size.
mergeSort( arr , size );
See you soon...
However, i have never implemented a merge sort which uses no temporary list to store the sorted sequences. And i have decided a merge sort algorithm like that.
It has some computational complexity but the main idea is simple. Every time in the merge operation we merge two lists which are indeed part of a one list! So i can use them as one list. It means if I overflow in the first list then i reach the second list! Also you should note that, it has some extra costs because i have to shift array elements for placing them to correct locations. This cost doesnt exist if you use extra lists to store the sorted sequences.
The merge code isnt so clear, may be you dont understand some parts because i dont give importance to naming conventions. Maybe i can revise and post it later. But now, i want to show you the code. If you find some bug please contact with me :)
template<typename T>
void mergeSort( T* arr , int len ){
int left, right, middle;
if( !arr || len < 2)
return;
left = 0;
right = len;
middle = left + ((right-left)/2 );
mergeSort( arr , middle - left );
mergeSort( arr+middle , right - middle);
merge(arr , middle-left , right-middle);
}
template<typename T>
void merge( T* left , int len1 , int len2 ){
int i,j ,k, ind , total = len1 + len2;
T val;
for(i = 0, j = 0; i < j+len1 && j < len2 ;){
if( left[i] > left[j+len1]){
val = left[ j + len1 ];
ind = j + len1 - i ;
k = 0;
while( ind >= 0 ){
left[j + len1 + k] = left[j + len1 +k -1];
k--;
ind--;
}
left[i] = val;
j++;
}else
i++;
}
}
For calling mergesort algorithm, just create an array of integers, chars or anything which overloads the > operator and call the method by giving the array and its size.
mergeSort( arr , size );
See you soon...
Wednesday, February 21, 2007
What are HEADER FILES ?
Tonight i began to write a linklist and a stack data structer implementation. It will be the most strong when it is completed. I try to complete it untill this sunday.
For now i want to tell about the header files. what are they for? Why do we need a header file like iostream.h or linklist.h?
When we are in the development, we generally want to write our class declarition to a header file and the methods definitions to a source file ( cpp ). This makes development more modular and easy.
However i want to talk about another issue about header files! It is a low level detail. Why do compilers, linkers (!), loaders (!) need these files?
If you dont know what are stored in the header files you can look inside of them. They contain ( at least they should contain ) only the declaritons of functions! For instance when we write an int ADD( int , int ) function, the definition of the function (i.e body ) doesnt exist in the header file. In fact definition of functions are stored in libraries. So, why the compiler, linker and loader want them?
The answers are really simple. However if you dont know the differences between compiler and linker, you may have some diffucties to understand the facts. So lets introduce these concepts with a few lines:
Compiler: compiles the C code :) its job is creating the object code from the source files. object files contain object code, debug information, symbols, segment informations etc.
Linker: Assume that your project consists of 2 source files. One source file contains a function and another source file uses this function inside it. When we compile them we have two object files because i tell you that compiler generates object files. So these two object files have to be linked in order to create an executable file. Linkers job is linking the object files which compiler generates.
Loader: Everything goes fine and finally we have an executable file. However there is a problem, it is stored in the secondary disk. When i want to execute it, the loader works and load this program to the memory, makes neccessary operations such as memory management process management and then our program becomes a process on the memory :)
It is worth to mention that compilers and linkers are part of the development tools while loaders are the parts of the operating system.
Anyway, you now have a basic knowledge about compilers, linkers and loaders. So is it important to have a header file?
Now i intorduce the purpose of the header file; Header files are required because they are used for type ( prototype ) checking!
Ok, so lets examine when we need this checking mechanism.
When we compile a source file, compiler looks for syntax errors. It is important the provide the correct syntaxes to compiler. So header files tell the compiler that this function has a prototype like this. Then compiler decides whether the programmer codes the application by correct function calls or not!
We see that compiler needs them because of checking the syntax.
For the linkers and loaders, header files arent important. Because they know that compiler generates the object code and control the syntax of the code before generating this object code. So linker basically links the neccessary libraries, object files and create the executable. Loader takes this executable and loads the memory. The definitions of the functions are stored in shared libraries and the in the executable itself. NOT in the header files!!!
However, what if we write the function definition to the inside of the header file!!
This means that we are bad programmers:) because our program may depend this header file in order to work correctly.
I ve tried to explain the roles of the compiler, linker and loader. In addition, i ve tried to give a brief summary about the header files. See you later!
For now i want to tell about the header files. what are they for? Why do we need a header file like iostream.h or linklist.h?
When we are in the development, we generally want to write our class declarition to a header file and the methods definitions to a source file ( cpp ). This makes development more modular and easy.
However i want to talk about another issue about header files! It is a low level detail. Why do compilers, linkers (!), loaders (!) need these files?
If you dont know what are stored in the header files you can look inside of them. They contain ( at least they should contain ) only the declaritons of functions! For instance when we write an int ADD( int , int ) function, the definition of the function (i.e body ) doesnt exist in the header file. In fact definition of functions are stored in libraries. So, why the compiler, linker and loader want them?
The answers are really simple. However if you dont know the differences between compiler and linker, you may have some diffucties to understand the facts. So lets introduce these concepts with a few lines:
Compiler: compiles the C code :) its job is creating the object code from the source files. object files contain object code, debug information, symbols, segment informations etc.
Linker: Assume that your project consists of 2 source files. One source file contains a function and another source file uses this function inside it. When we compile them we have two object files because i tell you that compiler generates object files. So these two object files have to be linked in order to create an executable file. Linkers job is linking the object files which compiler generates.
Loader: Everything goes fine and finally we have an executable file. However there is a problem, it is stored in the secondary disk. When i want to execute it, the loader works and load this program to the memory, makes neccessary operations such as memory management process management and then our program becomes a process on the memory :)
It is worth to mention that compilers and linkers are part of the development tools while loaders are the parts of the operating system.
Anyway, you now have a basic knowledge about compilers, linkers and loaders. So is it important to have a header file?
Now i intorduce the purpose of the header file; Header files are required because they are used for type ( prototype ) checking!
Ok, so lets examine when we need this checking mechanism.
When we compile a source file, compiler looks for syntax errors. It is important the provide the correct syntaxes to compiler. So header files tell the compiler that this function has a prototype like this. Then compiler decides whether the programmer codes the application by correct function calls or not!
We see that compiler needs them because of checking the syntax.
For the linkers and loaders, header files arent important. Because they know that compiler generates the object code and control the syntax of the code before generating this object code. So linker basically links the neccessary libraries, object files and create the executable. Loader takes this executable and loads the memory. The definitions of the functions are stored in shared libraries and the in the executable itself. NOT in the header files!!!
However, what if we write the function definition to the inside of the header file!!
This means that we are bad programmers:) because our program may depend this header file in order to work correctly.
I ve tried to explain the roles of the compiler, linker and loader. In addition, i ve tried to give a brief summary about the header files. See you later!
Wednesday, February 14, 2007
A Puzzle in C++
Today i want to introduce you a puzzle :) Indeed, it is a design problem! We will define the problem and explain the solution step by step. If you are ready lets begin!
One day someone came to me and said that "Hey emreknlk, i need a special class that at one time only one instance has to be allowed to live in the memory. I use this class as a special source consumer so there shouldnt be more than one instance of this class. At most one instance, do you understand me ha?"
and i said, "Hey john, be relax man, it is easy to implement. just watch it".
So, you understand the problem. We design a class that at most one object is allowed to be created. I mean, if there is no object around, you could create one. However, if there is an object, so there shouldnt be one more!
Before reading the solution, you can try to solve it by yourself.
Ok, let's implement this class otherwise john will be angry :p
There may be multiple solutions to this design problem. If you design in another way you can share it with us by putting a comment;)
Firstly i want to put the constructor of the class to the private area! Noone can create an object directly! Lets say the class name will be "CUnique".
CUnique inst; <- by putting the constructor to private, this line produce compiler error.
We prevent creating an instance of this class by putting the constructor to private area. However we have a problem, according to the problem, it should be allowed to create one instance. If we mark the ctor ( constructor ) as private then we cant create any instance!
For solving this problem, we simply define a static boolean member "isAllowedToCreate" and a static member function "createInstance". Purpose is very easy to understand. createInstance function can access to private constructor because it is a class method. If isAllowedToCreate is true then the call to createInstance returns an instance of this class by calling the constructor inside the static function. In addition, isAllowedToCreate variable has to be assigned to false otherwise multiple calls to createInstance function returns multiple instances which is prohibited. It is certain that "isAllowedToCreate" member has to be put to the private area, otherwise programmers can reach it and change it to the true value.
So, lets look at the below code:
class CUnique{
int Id; //a private member of class
//We define contructor in private section so that nobody can access it
//from the outside of the class.
CUnique( int ID = 0 ): Id( ID ) {}
//we define a boolean variable for knowing if an instance is allowed to be created.
static bool isAllowedToCreate;
public:
//this static function is used to create an instance if there hasnt been created yet
static CUnique* createInstance( int ID = 0 ){
if( isAllowedToCreate ){
isAllowedToCreate = false;
return (new CUnique( ID ));
}
//if there is an existing instance then returns NULL
return static_cast< CUnique* >(0) ;
}
int getId() const { return this->Id; }
};
looks fine, isnt it? If isAllowedToCreate is true we can create an instance. But it has a serios problem! What if we delete the instance that we have created! There will be no object around but worse thing is that isAllowedToCreate has the value false! We can never create an instance again although it is legal! So make an action and write a destructor to this class.
//in destructor set the boolean variable true for creating new instance.
~CUnique(){
isAllowedToCreate = true;
}
Yes now it looks really fine. isnt it? We can create only one instance and if we delete this instance we can create another. However, you sholdnt believe to me because i am lying! We can create one instance it is true, but we can go further and create one another! Indeed we can create hundreds of objects which live together at the same time! Lets look at this:
CUnique *ptr1 , *ptr2;
ptr2 = CUnique::createInstance();
ptr1 = new CUnique( *ptr2 );
We simply call the copy constructor!! if we dont write a copy contructor, compiler will provide one for us which copy every single byte of one instance to anohter. However if we write a copy constructor compiler wont provide it for us. We should simply put the copy constructor to the private area like constructor! If anyone tries to use it, compiler will generate an error saying that you can not access a private member.
Our final class contains a copy constructor and destructor is:
class CUnique{
int Id; //a private member of class
//We define contructor in private section so that nobody can access it
//from the outside of the class.
CUnique( int ID = 0 ): Id( ID ) {}
//put copy constructor to private area!
CUnique( CUnique& ){}
//we define a boolean variable for knowing if an instance is allowed to be created.
static bool isAllowedToCreate;
public:
//this static function is used to create an instance if there hasnt created yet
static CUnique* createInstance( int ID = 0 ){
if( isAllowedToCreate ){
isAllowedToCreate = false;
return (new CUnique( ID ));
}
//if there is an existing instance then returns NULL
return static_cast<CUnique* >(0) ;
}
int getId() const { return this->Id; }
//in destructor set the boolean variable true for creating new instance.
~CUnique(){
isAllowedToCreate = true;
}
};
bool CUnique::isAllowedToCreate = true;
I write a test case, please read the comments and inspect the output:
int main(){
CUnique *ptr1 , *ptr2;
//create the first instance
ptr1 = ptr1->createInstance();
//this call returns NULL, because another instance is living
ptr2 = CUnique::createInstance();
cout << "ptr1's address:" << int ( ptr1 ) << endl ;
cout << "ptr2's address:" << int( ptr2 ) << endl ;
//delete the unique instance !!!!
delete ptr1;
//now this call returns an instance to ptr2 because there is no instance around.
ptr2 = CUnique::createInstance();
cout << "ptr2's address:" << int( ptr2 ) << endl ;
ptr1 = ptr1->createInstance();
cout << "ptr1's address:" << int( ptr1 ) << endl ;
return 0;
}
OUTPUT is:
ptr1's address:3362632
ptr2's address:0
ptr2's address:3362632
ptr1's address:0
Press any key to continue . . .
Dont forget that ptr2 = ptr1; is always a valid assignment but this doesnt mean there are multiple instances. Both pointers point the same object by this assignment.
Is this the final class? Can you crate an instance more than once with this class? I dont give the answers of these questions:) see you soon!
One day someone came to me and said that "Hey emreknlk, i need a special class that at one time only one instance has to be allowed to live in the memory. I use this class as a special source consumer so there shouldnt be more than one instance of this class. At most one instance, do you understand me ha?"
and i said, "Hey john, be relax man, it is easy to implement. just watch it".
So, you understand the problem. We design a class that at most one object is allowed to be created. I mean, if there is no object around, you could create one. However, if there is an object, so there shouldnt be one more!
Before reading the solution, you can try to solve it by yourself.
Ok, let's implement this class otherwise john will be angry :p
There may be multiple solutions to this design problem. If you design in another way you can share it with us by putting a comment;)
Firstly i want to put the constructor of the class to the private area! Noone can create an object directly! Lets say the class name will be "CUnique".
CUnique inst; <- by putting the constructor to private, this line produce compiler error.
We prevent creating an instance of this class by putting the constructor to private area. However we have a problem, according to the problem, it should be allowed to create one instance. If we mark the ctor ( constructor ) as private then we cant create any instance!
For solving this problem, we simply define a static boolean member "isAllowedToCreate" and a static member function "createInstance". Purpose is very easy to understand. createInstance function can access to private constructor because it is a class method. If isAllowedToCreate is true then the call to createInstance returns an instance of this class by calling the constructor inside the static function. In addition, isAllowedToCreate variable has to be assigned to false otherwise multiple calls to createInstance function returns multiple instances which is prohibited. It is certain that "isAllowedToCreate" member has to be put to the private area, otherwise programmers can reach it and change it to the true value.
So, lets look at the below code:
class CUnique{
int Id; //a private member of class
//We define contructor in private section so that nobody can access it
//from the outside of the class.
CUnique( int ID = 0 ): Id( ID ) {}
//we define a boolean variable for knowing if an instance is allowed to be created.
static bool isAllowedToCreate;
public:
//this static function is used to create an instance if there hasnt been created yet
static CUnique* createInstance( int ID = 0 ){
if( isAllowedToCreate ){
isAllowedToCreate = false;
return (new CUnique( ID ));
}
//if there is an existing instance then returns NULL
return static_cast< CUnique* >(0) ;
}
int getId() const { return this->Id; }
};
looks fine, isnt it? If isAllowedToCreate is true we can create an instance. But it has a serios problem! What if we delete the instance that we have created! There will be no object around but worse thing is that isAllowedToCreate has the value false! We can never create an instance again although it is legal! So make an action and write a destructor to this class.
//in destructor set the boolean variable true for creating new instance.
~CUnique(){
isAllowedToCreate = true;
}
Yes now it looks really fine. isnt it? We can create only one instance and if we delete this instance we can create another. However, you sholdnt believe to me because i am lying! We can create one instance it is true, but we can go further and create one another! Indeed we can create hundreds of objects which live together at the same time! Lets look at this:
CUnique *ptr1 , *ptr2;
ptr2 = CUnique::createInstance();
ptr1 = new CUnique( *ptr2 );
We simply call the copy constructor!! if we dont write a copy contructor, compiler will provide one for us which copy every single byte of one instance to anohter. However if we write a copy constructor compiler wont provide it for us. We should simply put the copy constructor to the private area like constructor! If anyone tries to use it, compiler will generate an error saying that you can not access a private member.
Our final class contains a copy constructor and destructor is:
class CUnique{
int Id; //a private member of class
//We define contructor in private section so that nobody can access it
//from the outside of the class.
CUnique( int ID = 0 ): Id( ID ) {}
//put copy constructor to private area!
CUnique( CUnique& ){}
//we define a boolean variable for knowing if an instance is allowed to be created.
static bool isAllowedToCreate;
public:
//this static function is used to create an instance if there hasnt created yet
static CUnique* createInstance( int ID = 0 ){
if( isAllowedToCreate ){
isAllowedToCreate = false;
return (new CUnique( ID ));
}
//if there is an existing instance then returns NULL
return static_cast<CUnique* >(0) ;
}
int getId() const { return this->Id; }
//in destructor set the boolean variable true for creating new instance.
~CUnique(){
isAllowedToCreate = true;
}
};
bool CUnique::isAllowedToCreate = true;
I write a test case, please read the comments and inspect the output:
int main(){
CUnique *ptr1 , *ptr2;
//create the first instance
ptr1 = ptr1->createInstance();
//this call returns NULL, because another instance is living
ptr2 = CUnique::createInstance();
cout << "ptr1's address:" << int ( ptr1 ) << endl ;
cout << "ptr2's address:" << int( ptr2 ) << endl ;
//delete the unique instance !!!!
delete ptr1;
//now this call returns an instance to ptr2 because there is no instance around.
ptr2 = CUnique::createInstance();
cout << "ptr2's address:" << int( ptr2 ) << endl ;
ptr1 = ptr1->createInstance();
cout << "ptr1's address:" << int( ptr1 ) << endl ;
return 0;
}
OUTPUT is:
ptr1's address:3362632
ptr2's address:0
ptr2's address:3362632
ptr1's address:0
Press any key to continue . . .
Dont forget that ptr2 = ptr1; is always a valid assignment but this doesnt mean there are multiple instances. Both pointers point the same object by this assignment.
Is this the final class? Can you crate an instance more than once with this class? I dont give the answers of these questions:) see you soon!
Tuesday, February 13, 2007
Advanced Pointer Operations in C/C++
I like pointers i love pointers i use pointers so i want to explain 3 things about pointers. These are
1) Pointer to pointers
2) Pointer to functions
3) A strange syntax:)
There are much things to say. Lets begin with Pointer to pointer.
1) Pointer to pointer
Why do we need a pointer which points to a pointer? It is a common question which may be asked by a person who doesnt know much about the memory and the pointers.
Basically a pointer is a 4byte variable and nothing more. It just stores an address as a value inside it. Before explaining what a pointer to pointer is, lets examine the below code:
#include
using namespace std;
int main(){
char character = 'E';
char *ptr = &character; //pointer stores an ADDRESS ( address of character )
cout << "Address of ptr:" << &ptr << endl;
cout << "Content of ptr :"<< hex <<(int) ptr < cout << "Address of character:" << (int )&character << endl;
cout <<"Content of character:"<<*ptr <
return 0;
}
output:
Address of ptr:0x22ff68
Content of ptr :22ff6f
Address of character:22ff6f
Content of character:E
Press any key to continue . . .
we see that, ptr is a variable, it has own address and it stores the address of a variable in it. Puttin a * at the begining of a pointer provide us the value which is stored in the variable whose address is stored in pointer variable.
Most of us know about it. So why a pointer to pointer is neccessary? There may be some cases requiring some changes on pointers! For example, if we need to change a pointer's content which is passed to a function, we must use a pointer to point that pointer. Lets see the wrong example:
we write a function which takes a pointer and wants to assign its value to NULL:
void wrongChange( char* ptr ){
ptr = NULL;
}
and see the test case:
int main(){
char character = 'E';
char *ptr = &character; //pointer stores an ADDRESS ( address of character )
cout << "Content of ptr :"<< hex <<(int) ptr < wrongChange( ptr );
cout << "Content of ptr :"<< hex <<(int) ptr <
return 0;
}
Output is:
Content of ptr :22ff6f
Content of ptr :22ff6f
Press any key to continue . . .
You see that pointer's content doesnt change in main function. The ptr pointer in main function still points to the same address. This is because we tell it to do like this. Lets examine the wrongChange function:
1) Purpose is assigning the pointers content to NULL
2) When we call wrongChange, it allocates a variable in its stack for its local ptr.
3) After allocating a space in its stack, it copies the value of input argument which is provided by main function to its local ptr. Note that its local ptr is completly different than the ptr which is defined in main.
4) Local variable ptr is a pointer too, because we defined it as a pointer in argument list. So it is very normal to assign it a NULL value.
5) We assign a NULL value to the local ptr. However the ptr which is defined in main is still same because its address is very very different from the local ptr which is defined in stack of wrongChange function.
We see the reason why our ptr in main function is not change. Lets make some correction in our wrongChange function and rename it as correctChange :)
void correctChange( char** ptr ){
*ptr = NULL;
}
What we do in correctChange is changing the input parameter from char* to char**
Now our local ptr which is again created in stack of correctChange can point to a pointer! You can easily quess that the pointer which local ptr points will be the pointer which is given as an input paramter from main function.
A key point is that, when we call the correctChange function we shold provide a parameter which is an address of a pointer. So after we derefer the local ptr by putting an asterix, we reach the pointer which we want to change!
Our test case looks like:
int main(){
char character = 'E';
char *ptr = &character; //pointer stores an ADDRESS ( address of character )
cout << "Content of ptr :"<< hex <<(int) ptr < correctChange( &ptr );
cout << "Content of ptr :"<< hex <<(int) ptr <
return 0;
}
Output is:
Content of ptr :22ff6f
Content of ptr :0
Press any key to continue . . .
look at the call in main function. We give the pointer's address to correctChange by using ampersand ( i.e. &ptr ). So correctChange function uses that address to reach the pointer. Moreover if we use double * in correctChange function, we obtain the value of character variable. I mean, **ptr gives the value of 'E' in correctChange because the first asterix reaches the pointer ptr which is defined in main and the second one reaches the variable which ptr ( in main ) points to.
A major usage area of pointer to pointers is in data structers head/root pointers. For example if we want to add a node to the begining of the list, we must give the address of the head pointer to the addNode function. Because if we dont do like this, the node which we want to add, will be added to a begining of the list probably. Yes it is true, indeed it is added to the begining but the head pointer isnt updated because of using a local head pointer! We must change the head pointer for adding a node to the head of the link list. You can try and you will see ;)
2) Pointer to functions
It is also possible to point a pointer to a function. A function name is an address where the codes for that function begins. If you dont believe to me lets look at this code:
#include
using namespace std;
void function(){
cout <<"emreknlk." << endl;
}
int main(){
cout << hex << (int) function << endl;
return 0;
}
Output is:
40128a
Press any key to continue .
Now, we prove it in other ways. We can point a pointer to a function. It requires a little knowledge about how to create a pointer for pointing a function. However it is very easy. The below codes are self explanatory. Please follow the comments carefully. Begin with a simple example:
#include
using namespace std;
void function(){
cout <<"emreknlk." << endl;
}
int main(){
//first void is return type of function
//(*ptr) says that this is a pointer :)
//(void) means this function does not takes an input parameter
void (*ptr)( void );
//now we can point this ptr to function.
//the ptr's definition and function's prototype is same, it is safe to point
ptr = function;
//now ptr means function, they are same, they point to same address
//call the function by using ptr !!!
ptr();
return 0;
}
output is
emreknlk.
You see that, after we create a proper pointer to our function, we simply assign function to ptr and call the ptr instead of function. They both points the same address so our function is executed properly. At this example, the function has no return type and has no input parameter. However, it is also very easy for functions which takes paramters and returns a variable. Lets see an example:
#include
using namespace std;
double multiply( int num1, float num2 ){
return num1 * num2;
}
int main(){
//double is return type of function
//(*ptr) says that this is a pointer :)
//(int,float) means this function takes two input which are int and float variables
double (*ptr)( int , float );
//now we can point this ptr to function.
//the ptr's definition and function's prototype is same, it is safe to point
ptr = multiply;
//now ptr means multiply function, they are same, they point to same address
//call the function by using ptr !!!
cout <<"3*5.5 = " << ptr( 3, 5.5 ) << endl;
return 0;
}
output is:
3*5.5 = 16.5
Press any key to continue . . .
Usage are of pointer to functions is very large. There can be a switch mechanism for determining the function that want to be called at run time. I also compare this mechanism with the polymorphism. In polymorphism the function being called is determined in run time too by using virtual methods. You can also use pointer to functions
for fun:)
3) A strange syntax
Yesterday, i spoke with my friends and we realized that some of our friends hadnt know a syntax in C. I want to introduce it in here briefly.
Look at that code:
int main(){
char name[] = "emreknlk";
cout << name[3] << endl; //first line
cout << 3[name] << endl; //second line
cout << *(name+3) << endl; //third line
cout << *(3+name) << endl; //last line:)
return 0;
}
output:
e
e
e
e
All four line produce the same result! Look at the second line, is it a little strange? This code is perfectly correct because of the arithmetics provide the same result for four line. What compiler does is adding two constants to each other, evaluates the sum as the address of a variable and takes the variable in that address.
You know that the array name is a constant pointer which points to begining of the array. So name[3] means take the variable from name+3. In addition to this arithmetic 3[name] means take the variable from 3+name which is the same address with first.
Lets see a slightly different code:
char name[] = "emreknlk";
cout << &2[name-2] << endl;
Output is : emreknlk
This is C and this is why i like it, why i love it, why i use it:)
1) Pointer to pointers
2) Pointer to functions
3) A strange syntax:)
There are much things to say. Lets begin with Pointer to pointer.
1) Pointer to pointer
Why do we need a pointer which points to a pointer? It is a common question which may be asked by a person who doesnt know much about the memory and the pointers.
Basically a pointer is a 4byte variable and nothing more. It just stores an address as a value inside it. Before explaining what a pointer to pointer is, lets examine the below code:
#include
using namespace std;
int main(){
char character = 'E';
char *ptr = &character; //pointer stores an ADDRESS ( address of character )
cout << "Address of ptr:" << &ptr << endl;
cout << "Content of ptr :"<< hex <<(int) ptr <
cout <<"Content of character:"<<*ptr <
return 0;
}
output:
Address of ptr:0x22ff68
Content of ptr :22ff6f
Address of character:22ff6f
Content of character:E
Press any key to continue . . .
we see that, ptr is a variable, it has own address and it stores the address of a variable in it. Puttin a * at the begining of a pointer provide us the value which is stored in the variable whose address is stored in pointer variable.
Most of us know about it. So why a pointer to pointer is neccessary? There may be some cases requiring some changes on pointers! For example, if we need to change a pointer's content which is passed to a function, we must use a pointer to point that pointer. Lets see the wrong example:
we write a function which takes a pointer and wants to assign its value to NULL:
void wrongChange( char* ptr ){
ptr = NULL;
}
and see the test case:
int main(){
char character = 'E';
char *ptr = &character; //pointer stores an ADDRESS ( address of character )
cout << "Content of ptr :"<< hex <<(int) ptr <
cout << "Content of ptr :"<< hex <<(int) ptr <
return 0;
}
Output is:
Content of ptr :22ff6f
Content of ptr :22ff6f
Press any key to continue . . .
You see that pointer's content doesnt change in main function. The ptr pointer in main function still points to the same address. This is because we tell it to do like this. Lets examine the wrongChange function:
1) Purpose is assigning the pointers content to NULL
2) When we call wrongChange, it allocates a variable in its stack for its local ptr.
3) After allocating a space in its stack, it copies the value of input argument which is provided by main function to its local ptr. Note that its local ptr is completly different than the ptr which is defined in main.
4) Local variable ptr is a pointer too, because we defined it as a pointer in argument list. So it is very normal to assign it a NULL value.
5) We assign a NULL value to the local ptr. However the ptr which is defined in main is still same because its address is very very different from the local ptr which is defined in stack of wrongChange function.
We see the reason why our ptr in main function is not change. Lets make some correction in our wrongChange function and rename it as correctChange :)
void correctChange( char** ptr ){
*ptr = NULL;
}
What we do in correctChange is changing the input parameter from char* to char**
Now our local ptr which is again created in stack of correctChange can point to a pointer! You can easily quess that the pointer which local ptr points will be the pointer which is given as an input paramter from main function.
A key point is that, when we call the correctChange function we shold provide a parameter which is an address of a pointer. So after we derefer the local ptr by putting an asterix, we reach the pointer which we want to change!
Our test case looks like:
int main(){
char character = 'E';
char *ptr = &character; //pointer stores an ADDRESS ( address of character )
cout << "Content of ptr :"<< hex <<(int) ptr <
cout << "Content of ptr :"<< hex <<(int) ptr <
return 0;
}
Output is:
Content of ptr :22ff6f
Content of ptr :0
Press any key to continue . . .
look at the call in main function. We give the pointer's address to correctChange by using ampersand ( i.e. &ptr ). So correctChange function uses that address to reach the pointer. Moreover if we use double * in correctChange function, we obtain the value of character variable. I mean, **ptr gives the value of 'E' in correctChange because the first asterix reaches the pointer ptr which is defined in main and the second one reaches the variable which ptr ( in main ) points to.
A major usage area of pointer to pointers is in data structers head/root pointers. For example if we want to add a node to the begining of the list, we must give the address of the head pointer to the addNode function. Because if we dont do like this, the node which we want to add, will be added to a begining of the list probably. Yes it is true, indeed it is added to the begining but the head pointer isnt updated because of using a local head pointer! We must change the head pointer for adding a node to the head of the link list. You can try and you will see ;)
2) Pointer to functions
It is also possible to point a pointer to a function. A function name is an address where the codes for that function begins. If you dont believe to me lets look at this code:
#include
using namespace std;
void function(){
cout <<"emreknlk." << endl;
}
int main(){
cout << hex << (int) function << endl;
return 0;
}
Output is:
40128a
Press any key to continue .
Now, we prove it in other ways. We can point a pointer to a function. It requires a little knowledge about how to create a pointer for pointing a function. However it is very easy. The below codes are self explanatory. Please follow the comments carefully. Begin with a simple example:
#include
using namespace std;
void function(){
cout <<"emreknlk." << endl;
}
int main(){
//first void is return type of function
//(*ptr) says that this is a pointer :)
//(void) means this function does not takes an input parameter
void (*ptr)( void );
//now we can point this ptr to function.
//the ptr's definition and function's prototype is same, it is safe to point
ptr = function;
//now ptr means function, they are same, they point to same address
//call the function by using ptr !!!
ptr();
return 0;
}
output is
emreknlk.
You see that, after we create a proper pointer to our function, we simply assign function to ptr and call the ptr instead of function. They both points the same address so our function is executed properly. At this example, the function has no return type and has no input parameter. However, it is also very easy for functions which takes paramters and returns a variable. Lets see an example:
#include
using namespace std;
double multiply( int num1, float num2 ){
return num1 * num2;
}
int main(){
//double is return type of function
//(*ptr) says that this is a pointer :)
//(int,float) means this function takes two input which are int and float variables
double (*ptr)( int , float );
//now we can point this ptr to function.
//the ptr's definition and function's prototype is same, it is safe to point
ptr = multiply;
//now ptr means multiply function, they are same, they point to same address
//call the function by using ptr !!!
cout <<"3*5.5 = " << ptr( 3, 5.5 ) << endl;
return 0;
}
output is:
3*5.5 = 16.5
Press any key to continue . . .
Usage are of pointer to functions is very large. There can be a switch mechanism for determining the function that want to be called at run time. I also compare this mechanism with the polymorphism. In polymorphism the function being called is determined in run time too by using virtual methods. You can also use pointer to functions
for fun:)
3) A strange syntax
Yesterday, i spoke with my friends and we realized that some of our friends hadnt know a syntax in C. I want to introduce it in here briefly.
Look at that code:
int main(){
char name[] = "emreknlk";
cout << name[3] << endl; //first line
cout << 3[name] << endl; //second line
cout << *(name+3) << endl; //third line
cout << *(3+name) << endl; //last line:)
return 0;
}
output:
e
e
e
e
All four line produce the same result! Look at the second line, is it a little strange? This code is perfectly correct because of the arithmetics provide the same result for four line. What compiler does is adding two constants to each other, evaluates the sum as the address of a variable and takes the variable in that address.
You know that the array name is a constant pointer which points to begining of the array. So name[3] means take the variable from name+3. In addition to this arithmetic 3[name] means take the variable from 3+name which is the same address with first.
Lets see a slightly different code:
char name[] = "emreknlk";
cout << &2[name-2] << endl;
Output is : emreknlk
This is C and this is why i like it, why i love it, why i use it:)
Saturday, February 10, 2007
Buffer Overrun
Today i will talk about another security issue. Buffer overrun is a dangerous and destructive concept which shouldnt be exist in our codes. It has many forms like stack buffer overruns , heap buffer overruns, index out of bounds, ascii- unicode conflictions. However, i want to explain you only stack buffer overruns and leave the rest to you :)
When we look at the history of computer software, there were millions of dollars costs of these attacks. By using the buffer overflow attacks a hacker can reach lots of valuable informations or can destroy the whole system at once.
Before starting, i want to give some information about the C calling convention.
When we call a C function for a function, the caller function push the parameters to the stack in reverse order so the first parameter will be on the top of the stack and it is popped first. In addition when the called function wants to return to the caller it pops the return address of the caller from the stack and jump to there by taking this address. At this point, you can imagine that if the called function takes the wrong address from the stack, it jumps somewhere different then it shold jump to!
I want to show you with a simple example:
( My code is compiled in unix with gcc. I dont use Visual studio because it makes lots of optimization and has lots of flags which prevent overflow in debug mode )
#include stdio.h
#include string.h
void BufferOverFlow( char* str ){
char buffer[4];
//see the stack
printf("stack looks like: %p\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n");
strcpy( buffer , str ); //copy without controlling anything
//see the stack after copying str to buffer
printf("Now stack looks like: %p\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n");
}
void Information(){
printf("My credit card number: 333445566\n");
printf("My password: emreknlk\n");
}
int main(){
//last 4byte is the address of information function
char str[12]={1,2,3,4,5,6,7,8,0xb2,0x83,0x04,0x08};
printf("Address of Information:%p\n",Information);
BufferOverFlow(str); //call only BufferOverFlow function
return 0;
}
Lets look at the code. In the main function we simply call the BufferOverFlow function. Normally, we expect that BufferOverFlow function executes its code and returns to main again.
In our application we also have an important function whose name is Information. We dont want to show this function to anyone without permission.
However in the BufferOverFlow function, there is a bufferover flow exists. We dont control the str string while copying it to local variable buffer! buffer can take at most 4 byte because it is an char array of 4 elements, but we give it 12 elements and change the stacks content by overflow. Indeed, we change the return value which is stored in stack too. We give a new value as the Information function address so that BufferOverFlow function returns to Information function and all our data is displayed on the screen!! Look at the output:
-bash-2.05b$ ./buff
Address of Information:0x80483b2 <--This is the information functions address
stack looks like: 0x8e4062 <--Stack content in BufferOverFlow function
0x9caec0
0x80485b1
0xbfff8b48
0x9ccab8
0xbfff8b68
0x804843b <-- This is the return value that we override
0xbfff8b50
Now stack looks like: 0xbfff8b50 <-- After copying str to buffer
0x9caec0
0x80485b1
0xbfff8b48
0x4030201 <-- 1 2 3 4
0x8070605 <-- 5 6 7 8 ( overflow )
0x80483b2 <-- LOOK! the return value has changed! New value
0x9ccab8 is the address of Information!!!
My credit card number: 333445566 <-- Information function executes its body!
My password: emreknlk <-- my credit card and password are in the hackers
Segmentation fault hand :(
-bash-2.05b$
Yes we call another function by hacking the stack. The address of the functions can be obtained by using a debugger. And input parameter can be prepared by inspecting the code by running it with some input paramters.
In this post, we handle the buffer overrun issue. It has many forms and we show an example to stack buffer overflow. For preventing this issue, the code must be robust and safe libraries and functions should be used when coding the application.
See you again.
When we look at the history of computer software, there were millions of dollars costs of these attacks. By using the buffer overflow attacks a hacker can reach lots of valuable informations or can destroy the whole system at once.
Before starting, i want to give some information about the C calling convention.
When we call a C function for a function, the caller function push the parameters to the stack in reverse order so the first parameter will be on the top of the stack and it is popped first. In addition when the called function wants to return to the caller it pops the return address of the caller from the stack and jump to there by taking this address. At this point, you can imagine that if the called function takes the wrong address from the stack, it jumps somewhere different then it shold jump to!
I want to show you with a simple example:
( My code is compiled in unix with gcc. I dont use Visual studio because it makes lots of optimization and has lots of flags which prevent overflow in debug mode )
#include stdio.h
#include string.h
void BufferOverFlow( char* str ){
char buffer[4];
//see the stack
printf("stack looks like: %p\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n");
strcpy( buffer , str ); //copy without controlling anything
//see the stack after copying str to buffer
printf("Now stack looks like: %p\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n");
}
void Information(){
printf("My credit card number: 333445566\n");
printf("My password: emreknlk\n");
}
int main(){
//last 4byte is the address of information function
char str[12]={1,2,3,4,5,6,7,8,0xb2,0x83,0x04,0x08};
printf("Address of Information:%p\n",Information);
BufferOverFlow(str); //call only BufferOverFlow function
return 0;
}
Lets look at the code. In the main function we simply call the BufferOverFlow function. Normally, we expect that BufferOverFlow function executes its code and returns to main again.
In our application we also have an important function whose name is Information. We dont want to show this function to anyone without permission.
However in the BufferOverFlow function, there is a bufferover flow exists. We dont control the str string while copying it to local variable buffer! buffer can take at most 4 byte because it is an char array of 4 elements, but we give it 12 elements and change the stacks content by overflow. Indeed, we change the return value which is stored in stack too. We give a new value as the Information function address so that BufferOverFlow function returns to Information function and all our data is displayed on the screen!! Look at the output:
-bash-2.05b$ ./buff
Address of Information:0x80483b2 <--This is the information functions address
stack looks like: 0x8e4062 <--Stack content in BufferOverFlow function
0x9caec0
0x80485b1
0xbfff8b48
0x9ccab8
0xbfff8b68
0x804843b <-- This is the return value that we override
0xbfff8b50
Now stack looks like: 0xbfff8b50 <-- After copying str to buffer
0x9caec0
0x80485b1
0xbfff8b48
0x4030201 <-- 1 2 3 4
0x8070605 <-- 5 6 7 8 ( overflow )
0x80483b2 <-- LOOK! the return value has changed! New value
0x9ccab8 is the address of Information!!!
My credit card number: 333445566 <-- Information function executes its body!
My password: emreknlk <-- my credit card and password are in the hackers
Segmentation fault hand :(
-bash-2.05b$
Yes we call another function by hacking the stack. The address of the functions can be obtained by using a debugger. And input parameter can be prepared by inspecting the code by running it with some input paramters.
In this post, we handle the buffer overrun issue. It has many forms and we show an example to stack buffer overflow. For preventing this issue, the code must be robust and safe libraries and functions should be used when coding the application.
See you again.
Thursday, February 08, 2007
Using PIPEs to TEST your application
Today, i will talk about pipes and how to use them. A pipe is a data flow from one direction to another direction and vice versa. Imagine that you have to write an application and this application takes some input parameters from another application which runs at the same time. There are several ways of IPC. One way is that, one application prints its results to a file, and the other application reads from this file and use it as an input provider!
However, today i introduce a different way of speaking. Pipe is a data flow mechanism. We can create pipes between 2 process so that one writes its data to pipe and another reads from that pipe. I give my example for unix systems in C.
Now the story is;
At the lowel level, main process defines an array with two elements:
int descriptors[2];
This array holds our file descriptors. So we must say to kernel that this array is used as a pipe:
pipe( descriptors );
After this call we can use descriptor[0] for reading from the pipe, and descriptor[1] for writing to that pipe. So, we write to one end, read from the other end of the pipe ( a flow is created ).
Using a pipe in a process is meanless unless this process try to communicate with outside. So i create a second process with a fork system call.
fork();
This call returns the pid of the child to the parent process, and returns 0 to the child process. I may explain the fork call at a later time but for now it is enough to know that calling a fork from a process creates a child process. So now we have 2 processes at our memory, these are the main process and its child. Now we pass some arguments from parent ( main ) process to child by using the pipe that we created just before.
In the parent process code, we simply write:
write( descriptors[1] , buff , strlen( buff) );
So look at the first parameter. It is the pipe that we created. We write the buffer to pipe's writable end.
In the child process code, we simply write:
read( descriptors[0] , buff , 22 );
Now we read from the pipe ( descriptors[0] ) and store the buffer in buff.
I hope you understand what the pipe is:) Here i give you complete code then i write a code for testing a program automatically by using pipe.
#include stdio.h
#include unistd.h
int main(){
int isParent;
char buff[255];
int descriptors[2];
if ( pipe( descriptors ) < 0 )
return 1;
isParent = fork();
if( isParent ) {
//parent process
strcpy( buff , "Hi, this is emreknlk!");
write( descriptors[1] , buff , strlen( buff) );
printf("Hi this is parent\n");
}else{
//child process
sleep( 1 );
read( descriptors[0] , buff , 22 );
printf("Hi this is child, buffer is: %s\n",buff);
}
close( descriptors[0] );
close( descriptors[1] );
return 0;
}
The output of this program:
-bash-2.05b$ ./pipe
Hi this is parent
-bash-2.05b$ Hi this is child, buffer is: Hi, this is emreknlk!
-bash-2.05b$
Now are you ready for making some differences!
Suppose that you have to test 100 different code which are written by 100 different person. What would you do? if this number is smaller, lets say 10, you can test it by manually by executing each of them and providing the neccessary inputs.
When the number of codes that must be tested become larger, then a code can be written for testing them automatically.
Assume that, we want to test this simple application:
#include stdio.h
int main(){
int number, i, fact = 1;
scanf("%d",&number); //take the number for calculating factorial
//calculate factorial
for( i = 2; i <= number ; i++)
fact *= i;
//print result to screen
printf("%d\n",fact);
return 0;
}
We will write a code for testing this code. tester should provide input to application and control the result which is calculated by application.
For this purpose it is clear that, in our test code we must create two pipe for writing input parameter from test code to application (i.e number variable ) and writing result from application to test code (i.e fact ).
After we compile the above code we rename it as app. Our test code for testing this factorial code is:
#include stdio.h
#include string.h
#include unistd.h
#include fcntl.h
int main(){
int isParent,n;
char buff[255];
int des1[2];
int des2[2];
if ( pipe( des1 ) < 0 || pipe( des2 ) < 0 )
return 1;
isParent = fork();
if( isParent ) {
//parent process
write( des1[1], "5\n" , 3 ); //send value to our application
do{
n =read( des2[0], buff , 255 ); //get the result!
}while( n<= 0);
if( strncmp(buff,"120\n",n) == 0 )
printf("Test Succeed!\n");
else
printf("Test Failed %s\n",buff);
}else{
//child process
close( STDIN_FILENO );
close( STDOUT_FILENO );
dup2( des1[0] , STDIN_FILENO ); //change des1[0] to stdin of child
dup2( des2[1], STDOUT_FILENO ); //change des2[1] to stdout of the child
close( des1[0] );
close( des1[1] );
close( des2[0] );
close( des2[1] );
if ( execlp("./app","./app",(char*)NULL) < 0 )
exit(1);
}
return 0;
}
In my computer this test passes and Test Succeed message appears on my screen.
Here i give a brief explanation about test code.
For child process: We replace its stdout and stdin with the pipe descriptors so when it wants to write to stdout, the test code can reach it from the pipe by reading it and when it wants to read an input from stdin, it can read the value from the pipe.
execlp command is used for replacing the context of the child process with the our factorial application. Here app is the name of our factorial application.
For parent process: It is the code which tests the factorial application. It simply send an input value ( i.e 5 ) to the factorial application and controls the return value if it is 120.
By using this automatic tool we can test lots of application which take one parameter , calculate the factorial and print the result. So some conventions must be exist. For example if the factorial application prints the result by this way:
"Factorial is 120"
Then our test code will be fail because it expects only the result ( i.e 120 )
I try to give the simplest example for writing a test tool. I plan to write about IO methods and IO multiplexing for writing much detail about this topic. See you later :)
However, today i introduce a different way of speaking. Pipe is a data flow mechanism. We can create pipes between 2 process so that one writes its data to pipe and another reads from that pipe. I give my example for unix systems in C.
Now the story is;
At the lowel level, main process defines an array with two elements:
int descriptors[2];
This array holds our file descriptors. So we must say to kernel that this array is used as a pipe:
pipe( descriptors );
After this call we can use descriptor[0] for reading from the pipe, and descriptor[1] for writing to that pipe. So, we write to one end, read from the other end of the pipe ( a flow is created ).
Using a pipe in a process is meanless unless this process try to communicate with outside. So i create a second process with a fork system call.
fork();
This call returns the pid of the child to the parent process, and returns 0 to the child process. I may explain the fork call at a later time but for now it is enough to know that calling a fork from a process creates a child process. So now we have 2 processes at our memory, these are the main process and its child. Now we pass some arguments from parent ( main ) process to child by using the pipe that we created just before.
In the parent process code, we simply write:
write( descriptors[1] , buff , strlen( buff) );
So look at the first parameter. It is the pipe that we created. We write the buffer to pipe's writable end.
In the child process code, we simply write:
read( descriptors[0] , buff , 22 );
Now we read from the pipe ( descriptors[0] ) and store the buffer in buff.
I hope you understand what the pipe is:) Here i give you complete code then i write a code for testing a program automatically by using pipe.
#include stdio.h
#include unistd.h
int main(){
int isParent;
char buff[255];
int descriptors[2];
if ( pipe( descriptors ) < 0 )
return 1;
isParent = fork();
if( isParent ) {
//parent process
strcpy( buff , "Hi, this is emreknlk!");
write( descriptors[1] , buff , strlen( buff) );
printf("Hi this is parent\n");
}else{
//child process
sleep( 1 );
read( descriptors[0] , buff , 22 );
printf("Hi this is child, buffer is: %s\n",buff);
}
close( descriptors[0] );
close( descriptors[1] );
return 0;
}
The output of this program:
-bash-2.05b$ ./pipe
Hi this is parent
-bash-2.05b$ Hi this is child, buffer is: Hi, this is emreknlk!
-bash-2.05b$
Now are you ready for making some differences!
Suppose that you have to test 100 different code which are written by 100 different person. What would you do? if this number is smaller, lets say 10, you can test it by manually by executing each of them and providing the neccessary inputs.
When the number of codes that must be tested become larger, then a code can be written for testing them automatically.
Assume that, we want to test this simple application:
#include stdio.h
int main(){
int number, i, fact = 1;
scanf("%d",&number); //take the number for calculating factorial
//calculate factorial
for( i = 2; i <= number ; i++)
fact *= i;
//print result to screen
printf("%d\n",fact);
return 0;
}
We will write a code for testing this code. tester should provide input to application and control the result which is calculated by application.
For this purpose it is clear that, in our test code we must create two pipe for writing input parameter from test code to application (i.e number variable ) and writing result from application to test code (i.e fact ).
After we compile the above code we rename it as app. Our test code for testing this factorial code is:
#include stdio.h
#include string.h
#include unistd.h
#include fcntl.h
int main(){
int isParent,n;
char buff[255];
int des1[2];
int des2[2];
if ( pipe( des1 ) < 0 || pipe( des2 ) < 0 )
return 1;
isParent = fork();
if( isParent ) {
//parent process
write( des1[1], "5\n" , 3 ); //send value to our application
do{
n =read( des2[0], buff , 255 ); //get the result!
}while( n<= 0);
if( strncmp(buff,"120\n",n) == 0 )
printf("Test Succeed!\n");
else
printf("Test Failed %s\n",buff);
}else{
//child process
close( STDIN_FILENO );
close( STDOUT_FILENO );
dup2( des1[0] , STDIN_FILENO ); //change des1[0] to stdin of child
dup2( des2[1], STDOUT_FILENO ); //change des2[1] to stdout of the child
close( des1[0] );
close( des1[1] );
close( des2[0] );
close( des2[1] );
if ( execlp("./app","./app",(char*)NULL) < 0 )
exit(1);
}
return 0;
}
In my computer this test passes and Test Succeed message appears on my screen.
Here i give a brief explanation about test code.
For child process: We replace its stdout and stdin with the pipe descriptors so when it wants to write to stdout, the test code can reach it from the pipe by reading it and when it wants to read an input from stdin, it can read the value from the pipe.
execlp command is used for replacing the context of the child process with the our factorial application. Here app is the name of our factorial application.
For parent process: It is the code which tests the factorial application. It simply send an input value ( i.e 5 ) to the factorial application and controls the return value if it is 120.
By using this automatic tool we can test lots of application which take one parameter , calculate the factorial and print the result. So some conventions must be exist. For example if the factorial application prints the result by this way:
"Factorial is 120"
Then our test code will be fail because it expects only the result ( i.e 120 )
I try to give the simplest example for writing a test tool. I plan to write about IO methods and IO multiplexing for writing much detail about this topic. See you later :)
Wednesday, February 07, 2007
A Security Issue - Threat modelling
Today, i talk about an important topic. Currently i ve been reading the book "Writing Secure Code". And one of its chapter is about Threat Modelling. So i want to give a brief summary about Threat Modelling.
Threat Modelling is important when designing a software, because if you dont identify your enemys probably you will fail! Threat modelling covers the concepts of determining threats and how to solve them.
Threat modelling provides extra advanteges to designers like finding bugs at earlier stages, understanding the project and its flow diagram, testing application at later stages.
So, How it should be organized? Threat modelling consists of 7 main parts. These are:
1) Creating the team for threat modelling
2) Separete the project into smaller parts
3) Analyze and find the threats for the project
4) Mark threats according to importance order
5) Determine the responds for threats
6) Identify the correct technique for solving the issue
7) Identify the correct technolgy for this technique.
So, think that we apply once these steps to out project in desing phase. Is it enough? The answer is no because this should be a iterative process. Noone can find all the threats at 1 step.
Lets inspect this 7 stages now...
1) Creating the team for threat modelling
Theat modelling team should contain at least 1 member of every phase of the project teams. So testers ,designers and coders should be included. Also a sales person may be in the team because he can explain the product features to customers more clearly.
Maximum number of members should be approximetly 10. Because too much people in the team may cause complexities.
2) Separete the project into smaller parts
This is a decomposition part of the application. Data flow diagrams (DFDs) can be used for this purpose. Level of the DFD is also important. Too much decomposition is not required in the threat modelling. In the application design phase this decomposition goes deeper than threat modelling.
Another issue at this step is, while decomposing there should be no conservation about the how to fix this threat, or the solutions. This step exists just for identifying threats while decomposing the modules.
3) Analyze and find the threats for the project
STRIDE
STRIDE is a technique for determining the threats in the application.
S : Spoofing identfy : Attackers show theirselves as another person who have grant privileges or access to the system. In http authentication it is possible to behave like this.
T: Tampering with data: Attackers change the data intentionally. Databases are the common targets.
R: Repudiation: This concept is important especially in e-commerce. Denying some operations because of being illegal. So for instance attackers may try to buy some things from the e-commerce web pages
I: Information disclosure: Attackers may try to access some informations like files, passwords although they havent any access privileges. This concept is related with spoofing. By spoofing, anyone can access any information on remote machine.
D: Denial of Service: Sometimes valid users for the systems can be blocked for the system. The usage of the system for them may be restriceted. This attacks generally occured to the servers so normal users are prevented to login.
E: Elevation of privilege: Probably this is the worst thing that attackers gain the administrator privileges and all system may be collapsed!
So i try to explain STRIDE method for determining the threats in applications. Certainly there can be another criteria that should be considered. STRIDE method is just a classification of common threats.
4) Mark threats according to importance order
After determining the threats for the application, now it is time to give marks to each of them. The highest mark the threat has, the biggest importance it has.
DREAD method
D: Damage potential: If attackes attacks with this threat, what will be the degree of the damage to the system.
R: Reproducibility: Is it easy to produce same attacks again and again.
E: Exploitability: Is it required much effort or experiment to make an attack
A: Affected Users: What is the percentege of the persons that will be affeceted by this threat
D: Discoverability: is this attack can be easly discovered? It is hard to meausre this metric.
After giving points to eeach of this metric, we find the total mark for this threat. Every threat which is determined in step three, should be marked by using a mark method like DREAD. So we can give decisions with these threats.
5) Determine the responds for threats
We analyze, determine and give marks. So it is time to take an action:) Basicly we can do 4 things:
A) Take no action :) This is not a good idea ;)
B) Say everything to your customers : this is not a good idea too. Because users generally skip the warning messages or they cant give the right decisions.
C) Disable the feature: Remove the threat with the feature from the project.
D) Fix it! : i think it is the best solution :)
6) Identify the correct technique for solving the issue
Every threat has different solution technique. According to STRIDE categories, choose the appropriate technique for the threat.
7) Identify the correct technolgy for this technique.
Finally, choose the technolgy for implementing the technique that is determined in step 6.
So, at this post i try to give you a small knowledge about the threat modelling. It is useful for applications because if we dont know our enemies, the project couldnt be successful. Threat modelling has lot of advantages and easy to process.
Reference: Writing Secure Code Second Edition.
See you soon :)
Threat Modelling is important when designing a software, because if you dont identify your enemys probably you will fail! Threat modelling covers the concepts of determining threats and how to solve them.
Threat modelling provides extra advanteges to designers like finding bugs at earlier stages, understanding the project and its flow diagram, testing application at later stages.
So, How it should be organized? Threat modelling consists of 7 main parts. These are:
1) Creating the team for threat modelling
2) Separete the project into smaller parts
3) Analyze and find the threats for the project
4) Mark threats according to importance order
5) Determine the responds for threats
6) Identify the correct technique for solving the issue
7) Identify the correct technolgy for this technique.
So, think that we apply once these steps to out project in desing phase. Is it enough? The answer is no because this should be a iterative process. Noone can find all the threats at 1 step.
Lets inspect this 7 stages now...
1) Creating the team for threat modelling
Theat modelling team should contain at least 1 member of every phase of the project teams. So testers ,designers and coders should be included. Also a sales person may be in the team because he can explain the product features to customers more clearly.
Maximum number of members should be approximetly 10. Because too much people in the team may cause complexities.
2) Separete the project into smaller parts
This is a decomposition part of the application. Data flow diagrams (DFDs) can be used for this purpose. Level of the DFD is also important. Too much decomposition is not required in the threat modelling. In the application design phase this decomposition goes deeper than threat modelling.
Another issue at this step is, while decomposing there should be no conservation about the how to fix this threat, or the solutions. This step exists just for identifying threats while decomposing the modules.
3) Analyze and find the threats for the project
STRIDE
STRIDE is a technique for determining the threats in the application.
S : Spoofing identfy : Attackers show theirselves as another person who have grant privileges or access to the system. In http authentication it is possible to behave like this.
T: Tampering with data: Attackers change the data intentionally. Databases are the common targets.
R: Repudiation: This concept is important especially in e-commerce. Denying some operations because of being illegal. So for instance attackers may try to buy some things from the e-commerce web pages
I: Information disclosure: Attackers may try to access some informations like files, passwords although they havent any access privileges. This concept is related with spoofing. By spoofing, anyone can access any information on remote machine.
D: Denial of Service: Sometimes valid users for the systems can be blocked for the system. The usage of the system for them may be restriceted. This attacks generally occured to the servers so normal users are prevented to login.
E: Elevation of privilege: Probably this is the worst thing that attackers gain the administrator privileges and all system may be collapsed!
So i try to explain STRIDE method for determining the threats in applications. Certainly there can be another criteria that should be considered. STRIDE method is just a classification of common threats.
4) Mark threats according to importance order
After determining the threats for the application, now it is time to give marks to each of them. The highest mark the threat has, the biggest importance it has.
DREAD method
D: Damage potential: If attackes attacks with this threat, what will be the degree of the damage to the system.
R: Reproducibility: Is it easy to produce same attacks again and again.
E: Exploitability: Is it required much effort or experiment to make an attack
A: Affected Users: What is the percentege of the persons that will be affeceted by this threat
D: Discoverability: is this attack can be easly discovered? It is hard to meausre this metric.
After giving points to eeach of this metric, we find the total mark for this threat. Every threat which is determined in step three, should be marked by using a mark method like DREAD. So we can give decisions with these threats.
5) Determine the responds for threats
We analyze, determine and give marks. So it is time to take an action:) Basicly we can do 4 things:
A) Take no action :) This is not a good idea ;)
B) Say everything to your customers : this is not a good idea too. Because users generally skip the warning messages or they cant give the right decisions.
C) Disable the feature: Remove the threat with the feature from the project.
D) Fix it! : i think it is the best solution :)
6) Identify the correct technique for solving the issue
Every threat has different solution technique. According to STRIDE categories, choose the appropriate technique for the threat.
7) Identify the correct technolgy for this technique.
Finally, choose the technolgy for implementing the technique that is determined in step 6.
So, at this post i try to give you a small knowledge about the threat modelling. It is useful for applications because if we dont know our enemies, the project couldnt be successful. Threat modelling has lot of advantages and easy to process.
Reference: Writing Secure Code Second Edition.
See you soon :)
Saturday, February 03, 2007
Decision...
Hi all my friends,
I have a new decision about my blog that i ll write my new posts in english. I want to write in english because i see that some portion of the world miss the chance of reading my blog :))
So, the first thing that i want to say that, we have a wonderful saturday in this weekend. We had a small workshop in OraTurk about the oracle cost base optimizer. And the enjoyest part of this is being together with my friends and sharing the knowledge!
I try to explain the B-tree indexes and clustering factor to my collegues at this workshop. Also i had taken lots of pictures with my 6.0Mp casio camera:) So here i put one of them here..
see you till the next post :)
I have a new decision about my blog that i ll write my new posts in english. I want to write in english because i see that some portion of the world miss the chance of reading my blog :))
So, the first thing that i want to say that, we have a wonderful saturday in this weekend. We had a small workshop in OraTurk about the oracle cost base optimizer. And the enjoyest part of this is being together with my friends and sharing the knowledge!
I try to explain the B-tree indexes and clustering factor to my collegues at this workshop. Also i had taken lots of pictures with my 6.0Mp casio camera:) So here i put one of them here..
see you till the next post :)
Tuesday, January 16, 2007
Convert Constructor
Bircogunuz başlığa bakınca convert constructor mı o da ne diyordur herhalde, aslında convert constructor C++ da bildiğimiz içerisine parametre alan constructor a denmektedir. Peki sizce neden böyle bir ad konulma gereği duyulmuş bu parametre alan constructor tipine? sadece constructor denseydi parametre almayan constructordan nasıl ayrılacaktı... peki böyle bir ayrıma ihtiyaç var mı? peki copy constructor var bide o ne? etrafta bu kadar constructor dolanırken neden bir tanecik destructor var:) Bu soruların cevabını merak ediyorsanız bu yazımı okuyabilirsiniz, merak etmiyorsanız bir aşağıdaki yazıya alalım sizi:)
Şimdi siz aşağıda ki kodu bir inceleyin ve bu kodun çalışıp çalışmayacağını kendinize söyleyin ( Bu şekilde sorduğuma göre kodun çalışmasını bekliyorsunuzdur herhalde :) )
#include<iostream>
using namespace std;
class Test{
int var;
public:
Test( int v = 0 ) : var( v ) {}
int getVar() const { return this->var; }
};
void function( Test t1 ){
cout << t1.getVar()<< endl;
}
int main(){
function( 50 );
return 0;
}
Evet gelelim cevaba. Bu kodun çalışacağını söyleyenler malasef kaybeden tarafta yer almıycaklar:) evet çalışır bu kod. Peki;
gördüğünüz üzere function fonksyonunun giriş parametresi class Test tipinden tanımlanmıştır. Ozaman demek ki bizim function fonksiyonuna Test nesnesi vermemiz gerekir. Oysa biz napıyoruz açıkça bir int parametre veriyoruz...
İşte burada convert constructor dediğimiz olay devreye giriyor! 50 sayısı Test için yazdığımız constructora yada birden fazla varsa sırayla, verilerek Test nesnesine cast edilmeye yani 50 sayısı ile bir giriş parametresi oluşturulmaya çalışılıyor...
Burada argümanlar bire bir uyuştuğu için, Test sınıfının constructor ı bir tane int parametre alır, bu casting işi başarılı oluyor ve Yığında bir adet Test nesnesi oluşturuluyor. Bu yüzden bu constructorlar convert constructor olarak adlandırılmıştır.
Peki gelelim bu durumu engellemek için yapılabileceklere. Bazen böyle bir durumun gerçekleşmesini istemeyebiliriz. Fonksiyonun sadece bir nesneyi direk olarak vererek çalışmasını istediğimiz zamanlar olabilir. Bunu engellemek için ben 3 tane yol önerecem sizin aklına gelirse commentleyin lütfen:)
1) Fonksyionun giriş parametresini referance yapalım. Yani:
void function( Test& t1 ); şeklinde tanımlarsak, yığında nesne oluşturmak işe yaramayacaktır. Çünkü t1 nesnesi direk nesnenin kendisi olmalıdır ( kopyası değil! ) Bu yüzden bu şekilde bir fonksyon için function( 50 ); çağrısı hata verecektir.
2) Hiç convert constructor yazmayalım.. Bu mümkün bir çözüm olmasına rağmen akıldan uzaktır. 3. yöntem e bakın siz:)
3) explicit anahtar sözcüğünü kullanırsak derleyiciye function( 50 ); çağrısının mümkün olamayacağını söylemiş oluruz.
Peki nedir bu explicit? eğer bir convert constructor ı explicit olarak yazarsak, bu constructorın dışarıdan dolaylı olarak nesne üretmesini engellemiş oluruz. Yani function( 50 ); çağrısında ki 50 değeri bu constructor a giremez.
explicit Test( int v=0 ) : var( v ) {}
Evet işte convert constructlar hakkında kısaca söyleceğim şeyler bunlardı. convert constructorlardan başka bir de copy contructorlar var. copy constructor lar ise, bir nesneden bir nesne üretilceği zaman çalıştırılır. örneğin
Test t1;
Test t2 = t1; //copy constructor çalışır.
eğer sınıfınızın üyeleri arasında dinamik bellek ile ayrılmış değişkenler yoksa ( işaretçiler), sizin açıkça bir copy constructor yazmanız çoğu zaman gereksizdir ( gerekli olduğu durumlar da vardır uygulamanıza göre ). Çünkü derleyici otomatik olarak size bir adet constructor ve copy constructor sağlayacaktır. derleyicinin sağladığı copy constructor basitçe değişkenlerin değerlerini kopyalanandan kopyalayana atar. Eğer siz bir copy constructor ve constructor yazarsanız sınıfınıza, derleyici size default olarak bu fonksiyonları artık sağlamaz ve sizin yazdıklarınız geçerli olur. O yüzden eğer bilmeyerek constructor ı private alana koyarsanız, niye nesne oluşturamıyorum diye şaşırmayın:) her zaman nesne oluşturmak istiyorsanız constructorlarınız public alanda olmalı ki dışarıdan çağrılabilsin.
Constructorlarda ki bu çeşitliliğe karşılık destructor her zaman 1 tanedir. Bunun sebebi burada yapılacak işlemelerin genelde kaynakları geri iade etmek ve giriş çıkış işlemlerini tamamlamak gibi giriş parametresine bağlı olmayan değerlerdir. ve nesne yok edilirken kendiliğinden otomatik olarak çağrılmasıdır.
Bu yazımda sizlere convert constructorların bir özelliğinden bahsetmeye ve diğer constructorlar hakkında kısaca bilgi vermeye çalıştım. Bir sonraki yazımda görüşmek üzere...
Şimdi siz aşağıda ki kodu bir inceleyin ve bu kodun çalışıp çalışmayacağını kendinize söyleyin ( Bu şekilde sorduğuma göre kodun çalışmasını bekliyorsunuzdur herhalde :) )
#include<iostream>
using namespace std;
class Test{
int var;
public:
Test( int v = 0 ) : var( v ) {}
int getVar() const { return this->var; }
};
void function( Test t1 ){
cout << t1.getVar()<< endl;
}
int main(){
function( 50 );
return 0;
}
Evet gelelim cevaba. Bu kodun çalışacağını söyleyenler malasef kaybeden tarafta yer almıycaklar:) evet çalışır bu kod. Peki;
gördüğünüz üzere function fonksyonunun giriş parametresi class Test tipinden tanımlanmıştır. Ozaman demek ki bizim function fonksiyonuna Test nesnesi vermemiz gerekir. Oysa biz napıyoruz açıkça bir int parametre veriyoruz...
İşte burada convert constructor dediğimiz olay devreye giriyor! 50 sayısı Test için yazdığımız constructora yada birden fazla varsa sırayla, verilerek Test nesnesine cast edilmeye yani 50 sayısı ile bir giriş parametresi oluşturulmaya çalışılıyor...
Burada argümanlar bire bir uyuştuğu için, Test sınıfının constructor ı bir tane int parametre alır, bu casting işi başarılı oluyor ve Yığında bir adet Test nesnesi oluşturuluyor. Bu yüzden bu constructorlar convert constructor olarak adlandırılmıştır.
Peki gelelim bu durumu engellemek için yapılabileceklere. Bazen böyle bir durumun gerçekleşmesini istemeyebiliriz. Fonksiyonun sadece bir nesneyi direk olarak vererek çalışmasını istediğimiz zamanlar olabilir. Bunu engellemek için ben 3 tane yol önerecem sizin aklına gelirse commentleyin lütfen:)
1) Fonksyionun giriş parametresini referance yapalım. Yani:
void function( Test& t1 ); şeklinde tanımlarsak, yığında nesne oluşturmak işe yaramayacaktır. Çünkü t1 nesnesi direk nesnenin kendisi olmalıdır ( kopyası değil! ) Bu yüzden bu şekilde bir fonksyon için function( 50 ); çağrısı hata verecektir.
2) Hiç convert constructor yazmayalım.. Bu mümkün bir çözüm olmasına rağmen akıldan uzaktır. 3. yöntem e bakın siz:)
3) explicit anahtar sözcüğünü kullanırsak derleyiciye function( 50 ); çağrısının mümkün olamayacağını söylemiş oluruz.
Peki nedir bu explicit? eğer bir convert constructor ı explicit olarak yazarsak, bu constructorın dışarıdan dolaylı olarak nesne üretmesini engellemiş oluruz. Yani function( 50 ); çağrısında ki 50 değeri bu constructor a giremez.
explicit Test( int v=0 ) : var( v ) {}
Evet işte convert constructlar hakkında kısaca söyleceğim şeyler bunlardı. convert constructorlardan başka bir de copy contructorlar var. copy constructor lar ise, bir nesneden bir nesne üretilceği zaman çalıştırılır. örneğin
Test t1;
Test t2 = t1; //copy constructor çalışır.
eğer sınıfınızın üyeleri arasında dinamik bellek ile ayrılmış değişkenler yoksa ( işaretçiler), sizin açıkça bir copy constructor yazmanız çoğu zaman gereksizdir ( gerekli olduğu durumlar da vardır uygulamanıza göre ). Çünkü derleyici otomatik olarak size bir adet constructor ve copy constructor sağlayacaktır. derleyicinin sağladığı copy constructor basitçe değişkenlerin değerlerini kopyalanandan kopyalayana atar. Eğer siz bir copy constructor ve constructor yazarsanız sınıfınıza, derleyici size default olarak bu fonksiyonları artık sağlamaz ve sizin yazdıklarınız geçerli olur. O yüzden eğer bilmeyerek constructor ı private alana koyarsanız, niye nesne oluşturamıyorum diye şaşırmayın:) her zaman nesne oluşturmak istiyorsanız constructorlarınız public alanda olmalı ki dışarıdan çağrılabilsin.
Constructorlarda ki bu çeşitliliğe karşılık destructor her zaman 1 tanedir. Bunun sebebi burada yapılacak işlemelerin genelde kaynakları geri iade etmek ve giriş çıkış işlemlerini tamamlamak gibi giriş parametresine bağlı olmayan değerlerdir. ve nesne yok edilirken kendiliğinden otomatik olarak çağrılmasıdır.
Bu yazımda sizlere convert constructorların bir özelliğinden bahsetmeye ve diğer constructorlar hakkında kısaca bilgi vermeye çalıştım. Bir sonraki yazımda görüşmek üzere...
Monday, January 15, 2007
Sabitler de artık değişmeye başladı...
yarın finale gireceğim için bu gece karışık bişeyler yazmamaya karar verdim, ve kısaca size C de programlamanın ne kadar çok programcıya bırakıldığını göstermeye kadar verdim. Güzel bir trick yaparak const tanımlanan değişkenleri değiştirelim ne derseniz....
Derleyici const olarak tanımlanmış bir değişkene direk olarak bir atama yaptığınız da şöyle bir hata verecektir:
#include<iostream>
using namespace std;
int main(){
const int thisIsConstant = 10;
thisIsConstant = 9;
cout << thisIsConstant << endl ;
return 0;
}
error C2166: l-value specifies const object
gördüğünüz gibi l-value ( left value ) bir const nesneye işaret ediyor dedi derleyicim. Bunun anlamı sen atama yapamazsın çünkü sol da bir const değişken var, nereye atama yapıon sen filan diyor :)
ama derleyiciye sezdirmeden bu const değişkeni değiştirebiliriz... Değiştirdikten sonra bir soru sorucam bilene benden bi çikolata:)
işte değiştiriyoruz const değişkeni:
#include<iostream>
using namespace std;
int main(){
int* ptr;
const int thisIsConstant = 10;
ptr = ( int* ) &thisIsConstant; //cast ediyoruz
*ptr = 9; //degistirdikkkk
cout << thisIsConstant << endl;
return 0;
}
Evet thisIsConstant değişkenin bellek gözünde 9 mu yazıyor dersiniz??? Benim sistemimde ekran çıktısı:
10
Press any key to continue
10 yazıyor! Bunun sebebi, derleyicinin object kodunu oluştururken thisIsConstant değişkeni gördüğü yerleri değeriyle değiştirmesidir, derleyici kodda bu değişkenin değerinin 10 olduğunu ve const tanımlandığını görünce nasolsa bu const değişken değişemez diyip değerini object koda gömdü. ( Systems programming ders1 : studying assembly language provides; deeper understanding how the compiler works! )
Bu arada merak ediyosanız, debug yaparak değişkenin içeriğine baktığınız da ( ben baktım :) ) içerisinde 9 yazdığını göreceksiniz. Ama object kodda değişkenin kullanıldığı yerde değişkenin adresi değil derlendiği anda ki içeriği yani 10 değeri olduğu için aslında
cout << thisIsConstant << endl; satırı
cout << 10 << endl;
satırı ile yer değiştirmiştir ;) Derleyicim VS 6.0, başka derleyiciler de derlediğiniz zaman bu optimize yapılmıyorsa ekran da 9 sayısını görürsünüz. Mesala ben bu denemeden sonra, cpp uzantılı dosyayı c olarak değiştirdim, tabi iostream i de stdio ile
#include<stdio.h>
int main(){
int a = 5;
int* ptr;
const int thisIsConstant = 10;
ptr = ( int* ) &thisIsConstant; //cast ediyoruz
*ptr = 9; //degistirdikkkk
printf("%d\n",thisIsConstant);
return 0;
}
işte ekran çıktısı:
9
Press any key to continue
Demek ki neymiş efendim, vs 6.0 da C ve C++ derlenirken kodun optimize edilişi farklıymış.
Aynı kodları, ssh ile itu ye bağlanip gcc ile derledim.
Sonuçlar:
C kodu için ekrana 9 değeri bastı,
C++ kodu için ekrana 10 değeri bastı.
Buradan genelleme yaparsak, C++ derleyicileri kodu optimize ederken, const değişkenlerin değerlerini object koda direk gömerler. C derleyicileri değişkenlerin adresilerini kullanmaya devam ederler. ( En azından VS 6.0 ve Gcc/G++ öle yapıo ).
Const değişkenler ve pointerlar hakkında söylenecek bayağı bir şey var aslında,
mesala;
const int* ptr ile int* const ptr nin farklı şeyler olduğunu biliyor muydunuz?
const int* ptr ; ile işaretçinin içeriğini değiştirebilirsiniz ama işaret ettiği verinin içeriği değişemez.
int x, y;
const int* ptr = &x ; //işaretçi oluşturulurken değer atanıyor OK
*ptr = 111; //işaretçinin gösterdiği yerdeki veri değişemez, HATA
ptr = &y; //işaretçi farklı bir değişkene işaret ettirildi, OK
int* const ptr; de ise işaretçinin işaret ettiği adres değişemez, ama içerdiği veri değişebilir:
int x,y;
int* const ptr = &x ; //işaretçi oluşturulurken değeri atandı, OK
*ptr = 111; //işaretçinin verisi değiştirilebilir OK
ptr = &y; //işaretçi const tanımlanmıştı, başka bir yere işaret edemez, HATA
Son olarakta const int* const ptr; ile tanımlanmış bir işaretçinin ne içerdiği veri, ne de gösterdiği yer değişebilir. ( yukarıda ki iki işaretçinin birleşimi gibi )
Bu yazımda da sizlerle birlikte const değişkenleri/işaretçileri tartışmış olduk.
Bir sonra ki yazımda C++ da convert constructor larla ilgili ilginç bir konuya değinmek istiyorum inşallah:) Görüşmek üzere...
Derleyici const olarak tanımlanmış bir değişkene direk olarak bir atama yaptığınız da şöyle bir hata verecektir:
#include<iostream>
using namespace std;
int main(){
const int thisIsConstant = 10;
thisIsConstant = 9;
cout << thisIsConstant << endl ;
return 0;
}
error C2166: l-value specifies const object
gördüğünüz gibi l-value ( left value ) bir const nesneye işaret ediyor dedi derleyicim. Bunun anlamı sen atama yapamazsın çünkü sol da bir const değişken var, nereye atama yapıon sen filan diyor :)
ama derleyiciye sezdirmeden bu const değişkeni değiştirebiliriz... Değiştirdikten sonra bir soru sorucam bilene benden bi çikolata:)
işte değiştiriyoruz const değişkeni:
#include<iostream>
using namespace std;
int main(){
int* ptr;
const int thisIsConstant = 10;
ptr = ( int* ) &thisIsConstant; //cast ediyoruz
*ptr = 9; //degistirdikkkk
cout << thisIsConstant << endl;
return 0;
}
Evet thisIsConstant değişkenin bellek gözünde 9 mu yazıyor dersiniz??? Benim sistemimde ekran çıktısı:
10
Press any key to continue
10 yazıyor! Bunun sebebi, derleyicinin object kodunu oluştururken thisIsConstant değişkeni gördüğü yerleri değeriyle değiştirmesidir, derleyici kodda bu değişkenin değerinin 10 olduğunu ve const tanımlandığını görünce nasolsa bu const değişken değişemez diyip değerini object koda gömdü. ( Systems programming ders1 : studying assembly language provides; deeper understanding how the compiler works! )
Bu arada merak ediyosanız, debug yaparak değişkenin içeriğine baktığınız da ( ben baktım :) ) içerisinde 9 yazdığını göreceksiniz. Ama object kodda değişkenin kullanıldığı yerde değişkenin adresi değil derlendiği anda ki içeriği yani 10 değeri olduğu için aslında
cout << thisIsConstant << endl; satırı
cout << 10 << endl;
satırı ile yer değiştirmiştir ;) Derleyicim VS 6.0, başka derleyiciler de derlediğiniz zaman bu optimize yapılmıyorsa ekran da 9 sayısını görürsünüz. Mesala ben bu denemeden sonra, cpp uzantılı dosyayı c olarak değiştirdim, tabi iostream i de stdio ile
#include<stdio.h>
int main(){
int a = 5;
int* ptr;
const int thisIsConstant = 10;
ptr = ( int* ) &thisIsConstant; //cast ediyoruz
*ptr = 9; //degistirdikkkk
printf("%d\n",thisIsConstant);
return 0;
}
işte ekran çıktısı:
9
Press any key to continue
Demek ki neymiş efendim, vs 6.0 da C ve C++ derlenirken kodun optimize edilişi farklıymış.
Aynı kodları, ssh ile itu ye bağlanip gcc ile derledim.
Sonuçlar:
C kodu için ekrana 9 değeri bastı,
C++ kodu için ekrana 10 değeri bastı.
Buradan genelleme yaparsak, C++ derleyicileri kodu optimize ederken, const değişkenlerin değerlerini object koda direk gömerler. C derleyicileri değişkenlerin adresilerini kullanmaya devam ederler. ( En azından VS 6.0 ve Gcc/G++ öle yapıo ).
Const değişkenler ve pointerlar hakkında söylenecek bayağı bir şey var aslında,
mesala;
const int* ptr ile int* const ptr nin farklı şeyler olduğunu biliyor muydunuz?
const int* ptr ; ile işaretçinin içeriğini değiştirebilirsiniz ama işaret ettiği verinin içeriği değişemez.
int x, y;
const int* ptr = &x ; //işaretçi oluşturulurken değer atanıyor OK
*ptr = 111; //işaretçinin gösterdiği yerdeki veri değişemez, HATA
ptr = &y; //işaretçi farklı bir değişkene işaret ettirildi, OK
int* const ptr; de ise işaretçinin işaret ettiği adres değişemez, ama içerdiği veri değişebilir:
int x,y;
int* const ptr = &x ; //işaretçi oluşturulurken değeri atandı, OK
*ptr = 111; //işaretçinin verisi değiştirilebilir OK
ptr = &y; //işaretçi const tanımlanmıştı, başka bir yere işaret edemez, HATA
Son olarakta const int* const ptr; ile tanımlanmış bir işaretçinin ne içerdiği veri, ne de gösterdiği yer değişebilir. ( yukarıda ki iki işaretçinin birleşimi gibi )
Bu yazımda da sizlerle birlikte const değişkenleri/işaretçileri tartışmış olduk.
Bir sonra ki yazımda C++ da convert constructor larla ilgili ilginç bir konuya değinmek istiyorum inşallah:) Görüşmek üzere...
Sunday, January 14, 2007
Struct ve Union da ne ola ki...
Neredeyse yazdığım programların hiç birinde union kullanmadım, hatta bu cümleyi okurken bazılarımız union da ne ola ki diyebilir.
struct C de yaygin olarak kullanilan, C++ da ise class olarak bilinen bir veri tipidir. C++ da ki class ile C de ki struct in farklari tabi ki vardır, orneğin default olarak class yapısında değişkenler private tanımlanır. yani;
class emreknlkCokClass{ //:))
int var;
};
emreknlkCokClass sınıfının içerisinde tanımlanmış var değişkeni private dır ve dışarıdan erişelemez. struct da ise varsayılan erişim tipi public olup dışardan değişkenlere erişilebilir.
Neyse benim bu akşam canım sıkıldı. O yüzden struct ve union ı kendime oyuncak seçtim, hadi biraz oynayalım ;)
1) Makineniz little endian mı big endian mı öğrenmek istermisiniz...
Little endian ve big endian kavramları nedir bilmeyenler için özetleyim: Verilerinizin bellekte nasıl saklandığına yönelik kavramlardır. Big endian kısmında verinin en yüksek anlamlı kısmı düşük adreste, düşük anlamlı kısmı yüksek adreste saklanır. Bu insanların da rahatça anlayabileceği bir yöntemdir. Mesala $4579 verisini 1000 no lu bellek gözüne yazmak isterseniz, 1000 nolu bellek gözünün içeriği 45 , 1001 no lu bellek gözünün içeriği 79 olacaktır.
Little endian da tahmin edeceğiniz üzere bunun tam tersi, 1000 nolu bellek gözünde 79, 1001 nolu bellek gözünde 45 yazacak...
işte union yapısı ile:
#include<stdio.h>
union WhichEndian{
int number; //4 byte
char byte; //1 byte
};
int main(){
union WhichEndian find;
int i;
char* ptr;
find.number = 0x12345678;
ptr = &find.byte;
for(i = 0; i < sizeof( int ) ; i++)
printf("Memory address: %p\tData: %x\n",ptr, (int)*(ptr++));
return 0;
}
ve benim makinemde işte size ekran çıktısı:
Memory address: 0012FF7C Data: 78
Memory address: 0012FF7D Data: 56
Memory address: 0012FF7E Data: 34
Memory address: 0012FF7F Data: 12
Press any key to continue
Gördüğünüz gibi little endian çıktı benim toşibam :))
Gelelim kodaa, union ne yapar, ne işe yarar kısaca bir özet geçeyim: union içerisinde ki bütün değişkenleri bellekte aynı offset değerinden başlayarak yerleştirir. Kodda bir integer ve bir char kullandık, bunların ikisininde başlangıç adresleri aynıdır. aslında bu kodda char byte değişkenine gerek yoktu, aslında yukardaki sonucu elde etmek için union kullanmak bile gereksizdi, ama burada dikkatinizi çekmek istediğim nokta:
ptr = &find.byte;
satırıdır. Burada ptr adresi byte değişkenin adresini içeriyorsa da aslında number değişkeni ile byte değişkeni aynı bellek gözünden başlayarak tanımlandıkları için, for döngüsü içerisinde number değişkeninin içeriğini sıra ile düzgün bastırabildik.
Eğer burada union değil de struct kullanacak olsaydık bu kod çalışmayacaktı. Çünkü byte değişkeni number ın bittiği yerden başlayacaktı...
2) Struct ile bit show
Diyelim ki elimiz de 8 tane led var. Bu ledlerin durumunu ( on/off ) kontrol etmek istiyoruz. Her bir led için 1 integer değer tutarsak 8*sizeof(int) kadar bellek bosuna gitmis olacak. Ben diyorum ki gelin her led için 1 bit kullanalım ve toplam 1 byte yer ayırarak bu işi bitirelim...
işte gerekli struct yapısı ve program kodu
#include<stdio.h>
#define OFF 0
#define ON 1
struct LedControl
{
unsigned char led1: 1;
unsigned char led2: 1;
unsigned char led3: 1;
unsigned char led4: 1;
unsigned char led5: 1;
unsigned char led6: 1;
unsigned char led7: 1;
unsigned char led8: 1;
};
int main(){
struct LedControl vecii; //LED leri vecii yaksin kapasin...
printf("Led Control %d byte yer kaplar..\n",sizeof(vecii));
vecii.led1 = OFF;
printf("1. led: %d\n",vecii.led1);
vecii.led1 = ON;
printf("1. led: %d\n",vecii.led1);
return 0;
}
Görüldüğü gibi struct içinde tanımlana led lerin her biri 1 bit yer kaplar. Bu bitlere erişim normal değişkenlere erişim gibidir. her değişken için bir bit ayırmak zorunluluğu yoktur.
Şimdi ekran çıktısını inceleyip struct için gerçekten 1 byte yer ayrıldığını görebiliriz:
Led Control 1 byte yer kaplar..
1. led: 0
1. led: 1
Press any key to continue
Peki nerelerde kullanılır bu yapı, herhalde enterprise edition lı program yazarken pek işimiz düşmez bu yapılara:) Ama sistem programlama da, driver yazmada , embeded sistemler için yazılım geliştirme de ciddi derece başvurulabilen bir metot olarak karşımıza çıkar.
Bu yazımda da size basitçe struct ve union ı tekrar hatırlatmak istedim. Basit şeyler olmasına karşın pek başvurulmadığı için zamanla fonksyonları unutulabiliyor. Diğer yazılarımda görüşmek üzere...
struct C de yaygin olarak kullanilan, C++ da ise class olarak bilinen bir veri tipidir. C++ da ki class ile C de ki struct in farklari tabi ki vardır, orneğin default olarak class yapısında değişkenler private tanımlanır. yani;
class emreknlkCokClass{ //:))
int var;
};
emreknlkCokClass sınıfının içerisinde tanımlanmış var değişkeni private dır ve dışarıdan erişelemez. struct da ise varsayılan erişim tipi public olup dışardan değişkenlere erişilebilir.
Neyse benim bu akşam canım sıkıldı. O yüzden struct ve union ı kendime oyuncak seçtim, hadi biraz oynayalım ;)
1) Makineniz little endian mı big endian mı öğrenmek istermisiniz...
Little endian ve big endian kavramları nedir bilmeyenler için özetleyim: Verilerinizin bellekte nasıl saklandığına yönelik kavramlardır. Big endian kısmında verinin en yüksek anlamlı kısmı düşük adreste, düşük anlamlı kısmı yüksek adreste saklanır. Bu insanların da rahatça anlayabileceği bir yöntemdir. Mesala $4579 verisini 1000 no lu bellek gözüne yazmak isterseniz, 1000 nolu bellek gözünün içeriği 45 , 1001 no lu bellek gözünün içeriği 79 olacaktır.
Little endian da tahmin edeceğiniz üzere bunun tam tersi, 1000 nolu bellek gözünde 79, 1001 nolu bellek gözünde 45 yazacak...
işte union yapısı ile:
#include<stdio.h>
union WhichEndian{
int number; //4 byte
char byte; //1 byte
};
int main(){
union WhichEndian find;
int i;
char* ptr;
find.number = 0x12345678;
ptr = &find.byte;
for(i = 0; i < sizeof( int ) ; i++)
printf("Memory address: %p\tData: %x\n",ptr, (int)*(ptr++));
return 0;
}
ve benim makinemde işte size ekran çıktısı:
Memory address: 0012FF7C Data: 78
Memory address: 0012FF7D Data: 56
Memory address: 0012FF7E Data: 34
Memory address: 0012FF7F Data: 12
Press any key to continue
Gördüğünüz gibi little endian çıktı benim toşibam :))
Gelelim kodaa, union ne yapar, ne işe yarar kısaca bir özet geçeyim: union içerisinde ki bütün değişkenleri bellekte aynı offset değerinden başlayarak yerleştirir. Kodda bir integer ve bir char kullandık, bunların ikisininde başlangıç adresleri aynıdır. aslında bu kodda char byte değişkenine gerek yoktu, aslında yukardaki sonucu elde etmek için union kullanmak bile gereksizdi, ama burada dikkatinizi çekmek istediğim nokta:
ptr = &find.byte;
satırıdır. Burada ptr adresi byte değişkenin adresini içeriyorsa da aslında number değişkeni ile byte değişkeni aynı bellek gözünden başlayarak tanımlandıkları için, for döngüsü içerisinde number değişkeninin içeriğini sıra ile düzgün bastırabildik.
Eğer burada union değil de struct kullanacak olsaydık bu kod çalışmayacaktı. Çünkü byte değişkeni number ın bittiği yerden başlayacaktı...
2) Struct ile bit show
Diyelim ki elimiz de 8 tane led var. Bu ledlerin durumunu ( on/off ) kontrol etmek istiyoruz. Her bir led için 1 integer değer tutarsak 8*sizeof(int) kadar bellek bosuna gitmis olacak. Ben diyorum ki gelin her led için 1 bit kullanalım ve toplam 1 byte yer ayırarak bu işi bitirelim...
işte gerekli struct yapısı ve program kodu
#include<stdio.h>
#define OFF 0
#define ON 1
struct LedControl
{
unsigned char led1: 1;
unsigned char led2: 1;
unsigned char led3: 1;
unsigned char led4: 1;
unsigned char led5: 1;
unsigned char led6: 1;
unsigned char led7: 1;
unsigned char led8: 1;
};
int main(){
struct LedControl vecii; //LED leri vecii yaksin kapasin...
printf("Led Control %d byte yer kaplar..\n",sizeof(vecii));
vecii.led1 = OFF;
printf("1. led: %d\n",vecii.led1);
vecii.led1 = ON;
printf("1. led: %d\n",vecii.led1);
return 0;
}
Görüldüğü gibi struct içinde tanımlana led lerin her biri 1 bit yer kaplar. Bu bitlere erişim normal değişkenlere erişim gibidir. her değişken için bir bit ayırmak zorunluluğu yoktur.
Şimdi ekran çıktısını inceleyip struct için gerçekten 1 byte yer ayrıldığını görebiliriz:
Led Control 1 byte yer kaplar..
1. led: 0
1. led: 1
Press any key to continue
Peki nerelerde kullanılır bu yapı, herhalde enterprise edition lı program yazarken pek işimiz düşmez bu yapılara:) Ama sistem programlama da, driver yazmada , embeded sistemler için yazılım geliştirme de ciddi derece başvurulabilen bir metot olarak karşımıza çıkar.
Bu yazımda da size basitçe struct ve union ı tekrar hatırlatmak istedim. Basit şeyler olmasına karşın pek başvurulmadığı için zamanla fonksyonları unutulabiliyor. Diğer yazılarımda görüşmek üzere...
Saturday, January 13, 2007
Template C++
Bu postumda size belki biraz sıradışı ama ilginizi çekecek bir konuyu tanıtacağım. Yazıyı okumadan önce C++ ve template bilginizi tazeleyin derim, basit tutucam örneği ama yine de anlamayabilirsiniz:)
Bildiğiniz gibi template kavramı c++ nın güçlü ve önemli bir yanıdır. generic dediğimiz fonksyonlar sınıflar veritipleri oluşturmak için kullanırız genelde. örneğin bir absolute fonksyonu yazacaksak template kullandığımız zaman bütün veri tipleri için ( int float double ... ) ayrı ayrı yazmamıza gerek kalmaz. programcı bir kere yazar bu fonksyonu derleyici sağolsun çevirir....
Benim burada bahsetmek istediğim şey ise daha uçuk bişi:)
Önce gelin template i unutalım ve normal babadan kalma yöntemle bir fibonacci serisinin 20. terimini sadece 50 kere hesaplatalım ve ne kadar zaman alıo ölçelim:
#include < iostream >
using namespace std;
class Fibbooo{
public:
static int FindNth( int n ){
if( n == 0 )
return 0;
if( n == 1 )
return 1;
else
return FindNth( n - 1 ) + FindNth( n - 2);
}
};
int main(){
int result,i;
for( i = 0; i < 50; i++ )
result = Fibbooo::FindNth( 20 );
cout << result;
return 0;
}
Kodun çıktısında 20.terim: 6765 ve en önemlisi toplam süre:
Program Statistics ------------------
Command line at 2007 Jan 14 01:56:
"D:\ITU\c++\fib\Debug\fib"
Total time: 482,825 millisecond
Time outside of functions: 17,381 millisecond
Call depth: 21
Total functions: 1088
Total hits: 1095805
Function coverage: 26,2%
Overhead Calculated 3
Overhead Average 3
Module Statistics for fib.exe
-----------------------------
Time in module: 465,444 millisecond
Percent of time in module: 100,0%
Functions in module: 1088
Hits in module: 1095805
Module function coverage: 26,2%
Görüldüğü gibi modülün yürütülmesi 465,4 milisaniye, tüm program da 482 milisaniye de bitti.
Şimdi gelelim Template Meta Programming e, kodu daha sonra açıklayacağım. önce heycanlanmanız için kodu ve sonucu veriyim. işte kod:
#include <iostream>
template<n>
struct fibonacci
{
enum { result = fibonacci < n-1 >::result + fibonacci < n - 2 >::result};
};
template <>
struct fibonacci<0>
{
enum { result = 0 };
};
template <>
struct fibonacci<1>
{
enum { result = 1 };
};
int main() {
int result,i;
for( i = 0; i < 50; i++)
result = fibonacci<20>::result;
std::cout << result;
return 0;
}
ekran çıktısı bir önceki ile aynı. peki zamanlamalara göz atalım:
Program Statistics ------------------
Command line at 2007 Jan 14 01:59:
"D:\ITU\c++\fib\Debug\template"
Total time: 32,450 millisecond
Time outside of functions: 19,927 millisecond
Call depth: 15
Total functions: 1087
Total hits: 1255
Function coverage: 26,1%
Overhead Calculated 3
Overhead Average 3
Module Statistics for template.exe
----------------------------------
Time in module: 12,522 millisecond
Percent of time in module: 100,0%
Functions in module: 1087
Hits in module: 1255
Module function coverage: 26,1%
Evet, bazılarınız gözlerinize inanamamış olabilir, hatta benim bir yerlerde yannış yaptığımı da düşünebilir:) ama gördükleriniz doğru. program 32,5 milisaniyede ( ilk koda göre yaklaşık 15 kat daha hızlı) modül ise 12,5 milisaniyede ( yaklaşık 35 kat daha hızlı ) bir şekilde çalıştı.
Peki neden? Bunun sebebi aslında çok basit, siz execute tuşuna bastınız, yada exe ye çift tıkladınız, program loader programı belleğe aldı. Aslında tam bu noktada, yani loader programı belleğe yüklerken fibonacci serisinin 20. terimi hazırdı!!! evet hazırdı, program run time da hesaplamaz onu, compile ettiğiniz anda dosyayı 20.terim hesaplanır ve object kodunun içine yazılır. Nasıl mı? template ler compile aşamasında derleyici tarafından değiştirilerek yerine gelmesi gereken kod konulur. örneğin yukarıda bahsettiğim absolute fonksyonunu template ile yazıp, main içerisinde double için fonksyonu çağırırsanız, derlerken derleyici sizin bu fonksyonu double için çağırdığınızı görüp, template ifadelerini atıcak ve yerine sadece double için çalışan absolute fonk. nu yerleştirecektir.
Hal böyleyken, siz derlemeye başladığınız anda derleyici aşağıda ki ifadeyi görecek:
fibonacci<20>::result;
ve fibonacci<20>::result değerini hesaplamaya başlayacaktır. bunun için rekürsif olarak sürekli fibonacci < n > li struct ın değerini hesaplayacak, ne zaman ki n = 0 veya n = 1 olunca fibonacci<0> veya fibonacci<1> in değerlerini ( result değişkenlerini ) alabilecek, ozaman hesaplama bitmiş olacak...
Templateler ile programlama bir çok algoritmayı önemli derecede hızlandırabilmektedir. Uygulama da c++ programları bir run time da hesaplar yapan bölüm, bir de template meta programming dediğimiz bu şekilde kodlama ile yazılır ve compile time denilen yerde işlerin bir bölümü halledilmiş olunur.
Umarım bir miktar açıklayabilmişimdir TMP yi. daha fazlası için bknz google :) Sonraki yazılarımda görüşmek üzere...
Bildiğiniz gibi template kavramı c++ nın güçlü ve önemli bir yanıdır. generic dediğimiz fonksyonlar sınıflar veritipleri oluşturmak için kullanırız genelde. örneğin bir absolute fonksyonu yazacaksak template kullandığımız zaman bütün veri tipleri için ( int float double ... ) ayrı ayrı yazmamıza gerek kalmaz. programcı bir kere yazar bu fonksyonu derleyici sağolsun çevirir....
Benim burada bahsetmek istediğim şey ise daha uçuk bişi:)
Önce gelin template i unutalım ve normal babadan kalma yöntemle bir fibonacci serisinin 20. terimini sadece 50 kere hesaplatalım ve ne kadar zaman alıo ölçelim:
#include < iostream >
using namespace std;
class Fibbooo{
public:
static int FindNth( int n ){
if( n == 0 )
return 0;
if( n == 1 )
return 1;
else
return FindNth( n - 1 ) + FindNth( n - 2);
}
};
int main(){
int result,i;
for( i = 0; i < 50; i++ )
result = Fibbooo::FindNth( 20 );
cout << result;
return 0;
}
Kodun çıktısında 20.terim: 6765 ve en önemlisi toplam süre:
Program Statistics ------------------
Command line at 2007 Jan 14 01:56:
"D:\ITU\c++\fib\Debug\fib"
Total time: 482,825 millisecond
Time outside of functions: 17,381 millisecond
Call depth: 21
Total functions: 1088
Total hits: 1095805
Function coverage: 26,2%
Overhead Calculated 3
Overhead Average 3
Module Statistics for fib.exe
-----------------------------
Time in module: 465,444 millisecond
Percent of time in module: 100,0%
Functions in module: 1088
Hits in module: 1095805
Module function coverage: 26,2%
Görüldüğü gibi modülün yürütülmesi 465,4 milisaniye, tüm program da 482 milisaniye de bitti.
Şimdi gelelim Template Meta Programming e, kodu daha sonra açıklayacağım. önce heycanlanmanız için kodu ve sonucu veriyim. işte kod:
#include <iostream>
template<n>
struct fibonacci
{
enum { result = fibonacci < n-1 >::result + fibonacci < n - 2 >::result};
};
template <>
struct fibonacci<0>
{
enum { result = 0 };
};
template <>
struct fibonacci<1>
{
enum { result = 1 };
};
int main() {
int result,i;
for( i = 0; i < 50; i++)
result = fibonacci<20>::result;
std::cout << result;
return 0;
}
ekran çıktısı bir önceki ile aynı. peki zamanlamalara göz atalım:
Program Statistics ------------------
Command line at 2007 Jan 14 01:59:
"D:\ITU\c++\fib\Debug\template"
Total time: 32,450 millisecond
Time outside of functions: 19,927 millisecond
Call depth: 15
Total functions: 1087
Total hits: 1255
Function coverage: 26,1%
Overhead Calculated 3
Overhead Average 3
Module Statistics for template.exe
----------------------------------
Time in module: 12,522 millisecond
Percent of time in module: 100,0%
Functions in module: 1087
Hits in module: 1255
Module function coverage: 26,1%
Evet, bazılarınız gözlerinize inanamamış olabilir, hatta benim bir yerlerde yannış yaptığımı da düşünebilir:) ama gördükleriniz doğru. program 32,5 milisaniyede ( ilk koda göre yaklaşık 15 kat daha hızlı) modül ise 12,5 milisaniyede ( yaklaşık 35 kat daha hızlı ) bir şekilde çalıştı.
Peki neden? Bunun sebebi aslında çok basit, siz execute tuşuna bastınız, yada exe ye çift tıkladınız, program loader programı belleğe aldı. Aslında tam bu noktada, yani loader programı belleğe yüklerken fibonacci serisinin 20. terimi hazırdı!!! evet hazırdı, program run time da hesaplamaz onu, compile ettiğiniz anda dosyayı 20.terim hesaplanır ve object kodunun içine yazılır. Nasıl mı? template ler compile aşamasında derleyici tarafından değiştirilerek yerine gelmesi gereken kod konulur. örneğin yukarıda bahsettiğim absolute fonksyonunu template ile yazıp, main içerisinde double için fonksyonu çağırırsanız, derlerken derleyici sizin bu fonksyonu double için çağırdığınızı görüp, template ifadelerini atıcak ve yerine sadece double için çalışan absolute fonk. nu yerleştirecektir.
Hal böyleyken, siz derlemeye başladığınız anda derleyici aşağıda ki ifadeyi görecek:
fibonacci<20>::result;
ve fibonacci<20>::result değerini hesaplamaya başlayacaktır. bunun için rekürsif olarak sürekli fibonacci < n > li struct ın değerini hesaplayacak, ne zaman ki n = 0 veya n = 1 olunca fibonacci<0> veya fibonacci<1> in değerlerini ( result değişkenlerini ) alabilecek, ozaman hesaplama bitmiş olacak...
Templateler ile programlama bir çok algoritmayı önemli derecede hızlandırabilmektedir. Uygulama da c++ programları bir run time da hesaplar yapan bölüm, bir de template meta programming dediğimiz bu şekilde kodlama ile yazılır ve compile time denilen yerde işlerin bir bölümü halledilmiş olunur.
Umarım bir miktar açıklayabilmişimdir TMP yi. daha fazlası için bknz google :) Sonraki yazılarımda görüşmek üzere...
Analiz: Nasıl kod yazabiliriz?
Anladım ki benim yazılarım genel de yaşadığım güncel olaylar üzerine çıkıyor. Şimdi size geçenler de itü bilgisayar grubumuz da konustugumuz bir konuyu hararetli bicimde aktaricam hazir olun:) Yaziyi sonuna kadar takip etmenizi oneririm sonunda şaşırabilirsiniz...
Ozgur Macit arkadasimin deyimiyle;
Efendim gecen gun grubumuza bir mail geldi bi arkadasimiz tarafindan. Mail de herkesin bildiği bir swap islemi C/C++ dilinde farkli bir sekilde yazilmisti. Buyrun inceleyelim:
#include
int main(void)
{
int a = 3, b = 5;
a = a^b;
b = a^b;
a = a^b;
printf("%d\n", a);
printf("%d\n", b);
return 0;
}
// ^ XOR islemi bu arada :)
Alinti: Onur Dolu
Goruldugu gibi 3 tane XOR islemi yapilarak a ve b değiskenlerinin yeri değiştirimiş. Xor işlemi matematikte/programlamada çok işe yarıyor daha sonra başka örnekler yazmayı düşünüyorum da biz gelelim esas konuya: Bu kodu bilinen temp değişkeni üzerinden eski değeri saklayıp, daha sonra a ve b değişkenlerinin değerini değiştiren swap koduna tercih edilebilr mi?
Bu arada yukarıda yazılmış olan kod aşağıda ki şekilde de kısaltılabiliyor:
int a = 3, b = 5;
a ^= b ^=a ^=b;
Grupta bulunan sevdiğim bir arkadaşım bu koda doğal olarak karşı çıktı. Nedenini ise şu şekilde açıkladı:
"Okuması da oldukça zor oluyor..
Kod bir kere yazılır ama defalarca okunur..
Senden sonra programda değişlik yapması gereken bir programcı aşağıdaki satırı anlıyamıyabileceğ inden veya daha kötüsü yanlış anlıyabileceğinden saatler veya günler kaydebilir.. Proje başarızlığa uğrayabilir."...
Yukarıda aynen alıntısını aldığım yazı hakkında herhalde herkes fikir birliğine varmıştır. Bu kodu okumak matematik bilgisi çok iyi olmayan bir programcı için imkansıza yakın olduğu gibi, matematik bilgisi olan bir programci için de zaman alıcı ve can sıkıcıdır.
Bunun üzerine ne yapılabilir diye düşündüm. Kod daha efektif duruyordu. Yukardaki probleme bir çözüm üretmeden önce bunu teyid etmem gerekti ( Hazırsanız birazdan kendinizi sonu gelmeyen bir kaosun içinde bulacaksınız!! ) :
Test1 ( yeni swap )
#include //stdio gereksiz bu kodda ama printf koymustum kalmis oyle:)
int main(void)
{
int a = 3, b = 5;
int i;
for( i = 0; i < 100001; i++ ){
a ^= b ^= a ^= b;
}
return 0;
}
Bu kodun çalışması sonucunda şu değerler elde edildi:
Program Statistics
------------------
Command line at 2007 Jan 13 11:28: "D:\ITU\c++\fib\Debug\swap"
Total time: 17,024 millisecond
Time outside of functions: 15,794 millisecond
Call depth: 1
Total functions: 1
Total hits: 1
Function coverage: 100,0%
Overhead Calculated 3
Overhead Average 3
Module Statistics for swap.exe
------------------------------
Time in module: 1,231 millisecond
Percent of time in module: 100,0%
Functions in module: 1
Hits in module: 1
Module function coverage: 100,0%
Func Func+Child Hit
Time % Time % Count Function
---------------------------------------------------------
1,231 100,0 1,231 100,0 1 _main (swap.obj)
Görüldüğü gibi döngü yaklaşık 1,231 milisaniye de tamamlandı.
Şimdi ikinci swap işlemini incelemeye geldi sıra. Bildiğmiz temp değişkeni üzerinden elde edilen swap....
Test1 ( klasik swap )
#include
int main(void)
{
int a = 3, b = 5;
int i, temp;
for( i = 0; i < 100001; i++ ){
temp = a;
a = b;
b = a;
}
return 0;
}
Evet heycanla bu kodu yazdım ve sonucu gerçekten çok merak ediyordum. Eğer tahminlerim doğruysa bu kodun çalışması en az 2 kat yavaş olacaktı çünkü fazladan bir temp değişkenine erişim vardı. Belleğe erişimin registerlar üzerinde xor işleminden çok daha fazla sürmesi gerekirdi. Şimdi sonuca bakalım:
Program Statistics
------------------
Command line at 2007 Jan 13 11:35: "D:\ITU\c++\fib\Debug\swap"
Total time: 2,228 millisecond
Time outside of functions: 1,538 millisecond
Call depth: 1
Total functions: 1
Total hits: 1
Function coverage: 100,0%
Overhead Calculated 3
Overhead Average 3
Module Statistics for swap.exe
------------------------------
Time in module: 0,690 millisecond
Percent of time in module: 100,0%
Functions in module: 1
Hits in module: 1
Module function coverage: 100,0%
Func Func+Child Hit
Time % Time % Count Function
---------------------------------------------------------
0,690 100,0 0,690 100,0 1 _main (swap.obj)
Evet herkes gördü sonucu, bende gördüm, gördüm sonra gözlerime inanamadım, inanamadım bir daha çalıştırdım, çalıştırdım yine aynı sonucu gördüm, gördüm gözlerime inanamadım...
Sonuç beni hem hayal kırıklığına uğratmıştı hem de heycanlandırmıştı. Bu sonucu beklemediğim açıktı. Demek ki ya ben bi yerlerde yanlış yaptım, ya da...
Evet ya da derleyici de bir sorun vardı:) Bu arada derleyici MSVS 6.0
Ve derleyiciden şüphe duydum! Daha önceki tecrübelerimden biliyordum ki derleyici assembly kodunu oluştururken arada geçici bellek atamaları yapıyordu. Kodu tekrar yazmalıydım ama bu sefer inline assembly kullanacaktım, kalp atışlarım hızlanmıştı.
Ama, evet ama önce söylediğim şeyin gerçek olduğunu size göstermeliydim. Test1 kodunun disassemblysine baktım işte size de göstereyim ( sadece for döngüsü içinde ki tek satır xor işlemleri) :
00401041 mov ecx,dword ptr [ebp-4] <- varan 1
00401044 xor ecx,dword ptr [ebp-8] <- varan 2
00401047 mov dword ptr [ebp-4],ecx <- varan 3
0040104A mov edx,dword ptr [ebp-8] ....
0040104D xor edx,dword ptr [ebp-4] ....
00401050 mov dword ptr [ebp-8],edx ....
00401053 mov eax,dword ptr [ebp-4] ....
00401056 xor eax,dword ptr [ebp-8] ....
00401059 mov dword ptr [ebp-4],eax <- varan 9
Evet kod gerçekten beni "in a horror" vaziyetine düşürmüştü! ben saf ve masum bir şekilde registerlar da xor işlemi yapiyordum. Ama derleyici her işlem sonucunu bir bellek gözünde saklıyordu!!! bu ise performansı inanılmaz bir şekilde düşürüyordu. Gerçekten vs 6.0 dan korkmaya başlamıştım...
ve test2 deki temp kullanarak gerçekleştirilen 3 atama işlemi ile swap yaptığım kodu disassembly ettim ve işte sonuc:
9: temp = a;
00401041 mov ecx,dword ptr [ebp-4]
00401044 mov dword ptr [ebp-10h],ecx
10: a = b;
00401047 mov edx,dword ptr [ebp-8]
0040104A mov dword ptr [ebp-4],edx
11: b = a;
0040104D mov eax,dword ptr [ebp-4]
00401050 mov dword ptr [ebp-8],eax
Bu kod kesinlikle belleğe daha az erişiyordu. Aslında tam da yapması beklenen işlemi yapıyordu. önce a değişkenini ecx e atıyor sonra ecx i temp bellek gözüne atıyordu ve diğer a=b ve b=a içinde aynılarını yapıyordu.
test2 nin kodu bu kadar iyi çalıştığı halde test1 neden bukadar kötüydü? Şeytan beni iyice kodu inline assembly de yazmaya itiyordu...
Ama önce son bir kez daha brain storming yapmak istedim. Düşündüm... registerlar üzerinde xor işlemi yapmak istiyordum. Ama yazdığım kod registerlar üzerinde değil bellek üzerinde, bellekten değerleri registerlar a çekerek, daha sonra registerlar da gerçekleştirdiği xor işlemlerini tekrar belleğe yazarak gerçekleştiriyordu. Evet yazarken farkediyodum herşeyi, bir emreknlk nasil bu kadar dikkatsizce davranabilirdi, bir emreknlk bu hatayi nasil yapabilirdi!
VS 6.0.. O kadar günahını aldığım derleyici. Aslında ben ne söylüyorsam onu yapıyordu! Onun hiç bir suçu yoktu tek suçlu vardı o da emreknlk! Kodu yazarken dikkat ederseniz değişkenler int a ve int b olarak tanımlamıştım. Yani ben dedim ki: Git esp den ( stack pointer) 8 çıkar! Değişkenleri yığında oluşturuyordum!!! Yani bellekte! sonra nasıl olurda bütün işlemleri registerlar üzerinde yapmasını bekleyebilirdim ki... Bellekten a ve b yi alıp tekrar a ve b yi belleğe yazması gerekiyor derleyicinin çünkü ben ona öyle yap demiştim!
Peki şimdi İnline assembly de yazmama gerek varmıydı? İnline assembly de yazarsam kodun hızlanacağını biliyordum. Çünkü asm kodunu ben gömecektim C nin içine. Ama C nin de bana sunduğu yetenekleri göz ardı edemezdim.
Evet register anahtar sözcüğü ( keyword ) değişkenleri bellekte değil, eğer mümkünse registerlar da oluşturuyordu. hemen
int a, b; ifadesini register int a, b; ifadesi olarak değiştirdim.
Sonucu merak ediyor olmalısınız. Sonuç değişmedi... Bir kaos içinde kalmıştım. Düşündüğüm hiç bir şey çalışmıyordu. Başarısızlık ard arda geliyordu. Belki de beni C ye bu kadar bağlayan şey buydu.
Araştırmaya başladım. niye çalışmadı niye sonuç aynıydı. tekrar disassebly ye baktım sonuçta yine a ve b değişkeni registerlarda değil yığında oluşturulmuştu! peki bu nasıl olabilirdi? Oysa ben açıkça registerlarda oluştur demiştim değişkenleri. Sonunda kendimi msdn de buldum. işte açıklamaları:
"The compiler does not accept user requests for register variables; instead, it makes its own register choices when global register-allocation optimization (/Oe option) is on. However, all other semantics associated with the register keyword are honored."
Evet vs 6.0 register anahtar sözcüğü için bizi sallamıyordu. belki de güvenmiyordu. Bu C nin esnekliğinin bir yerde kaybolması demekti. Düşündüm, kodu linux de derlesem çalışacaktı. Bundan şüphem yoktu. register anahtar sözcüğü ile test1 de yazdığımız xor lu kod hızlı çalışacaktı.
Ama bir sorun vardı! Birden adil davranmadığımı anladım. test2 içinde temp, a ve b değişkenlerini register anahtar sözcüğü ile tanımlasam daha o daha da hızlı çalışacaktı. 3 register da sadece 3 atama işlemi. işte hız buydu...
Peki son bir çırpınış, bu yazıma başlarken hatırlarsanız kodun okunabilirliğini sağlamak için yapılması gerekenleri anlatmak ama önce o kodun hızlı çalışması gerektiğini ispatlamak istiyordum. hızlı çalıştığını ispatlamadan nasıl olurda bu kodu kullanın diyebilirdim ki size, üstelik daha da okunabilirliği azaltırken.
Evet iki koda da adil olarak davrandığım sürece her yola başvurmalıydım. register fikri çok iyi bir fikir değildi. Büyük programlarda bu yöntem patlayabilirdi. Şimdi geriye malesef tek bir çözüm kaldı. Bu çözüm de başarısızlığa uğrarsa... düşünmek bile istemiyorum.
Inline assembly
Evet heycan dorukta. Bu sefer vs60 var bir de ben varım...
işte test 1, xor işlemleri ile yapılan swap işlemi ( Not: bu kod main içinde tanımlanmış ama swap fonksyonu içerisinen alınabilir, testleri yapmak için bu şekilde yazdım) :
#include
int main(void)
{
int a = 3, b = 5;
int i;
for( i = 0; i < 100001; i++ ){
__asm{
mov eax, [a]
mov ebx, [b]
xor eax, ebx
xor ebx, eax
xor eax, ebx
mov [a], eax
mov [b], ebx
}
}
return 0;
}
Gördüğünüz gibi asm kodu visual studio 6.0 içinde main içinde duruyor. şimdi bakalım sonuca:
Profile: Function timing, sorted by time
Date: Sat Jan 13 12:50:05 2007
Program Statistics
------------------
Command line at 2007 Jan 13 12:50: "D:\ITU\c++\fib\Debug\swap"
Total time: 1,702 millisecond
Time outside of functions: 1,112 millisecond
Call depth: 1
Total functions: 1
Total hits: 1
Function coverage: 100,0%
Overhead Calculated 3
Overhead Average 3
Module Statistics for swap.exe
------------------------------
Time in module: 0,589 millisecond
Percent of time in module: 100,0%
Functions in module: 1
Hits in module: 1
Module function coverage: 100,0%
Func Func+Child Hit
Time % Time % Count Function
---------------------------------------------------------
0,589 100,0 0,589 100,0 1 _main (swap.obj)
Evetttt, işte bu mudur? budur dedirten sonuç kendini gösterdi!!! 0.589 milisaniye şu ana kadar elde edilen en iyi süre. Şimdi biraz sevinç duyuyorum ama daha herşey bitmedi diğer kodu da bu şekilde yazıp sonuçları karşılaştırmam gerek.
işte temp kullanılarak gerçekleştirilen swap işleminin inline assembly li hali:
#include
int main(void)
{
int a = 3, b = 5;
int i,temp;
for( i = 0; i < 100001; i++ ){
__asm{
mov eax, [a]
mov [temp], eax ; a nin degeri temp de
mov ebx, [b]
mov [a], ebx ; b nin degeri a da
mov eax, [temp]
mov [b], eax ;temp in degeri b de
}
}
return 0;
}
ve sonuç geliyor:
Profile: Function timing, sorted by time
Date: Sat Jan 13 12:56:28 2007
Program Statistics
------------------
Command line at 2007 Jan 13 12:56: "D:\ITU\c++\fib\Debug\swap"
Total time: 21,834 millisecond
Time outside of functions: 21,197 millisecond
Call depth: 1
Total functions: 1
Total hits: 1
Function coverage: 100,0%
Overhead Calculated 3
Overhead Average 3
Module Statistics for swap.exe
------------------------------
Time in module: 0,637 millisecond
Percent of time in module: 100,0%
Functions in module: 1
Hits in module: 1
Module function coverage: 100,0%
Func Func+Child Hit
Time % Time % Count Function
---------------------------------------------------------
0,637 100,0 0,637 100,0 1 _main (swap.obj)
Evet en sonunda istediğim sonuç... 0.637 milisaniye . Biraz acımasız davrandım ikinci swap a kabul ediyorum ama en iyiye ulaşmak kolay olmuyor tabi :))
Dahası var...
inline assembly kullanarak temp i de atabilirz! işte 3. yol:
#include
int main(void)
{
int a = 3, b = 5;
int i;
for( i = 0; i < 100001; i++ ){
__asm{
mov eax, [a]
mov ebx, [b]
mov [a], ebx ; b nin degeri a da
mov [b], eax ; a nin degeri b de
}
}
return 0;
}
ve işte en kısası şimdi geliyorrrr :)
Profile: Function timing, sorted by time
Date: Sat Jan 13 13:00:59 2007
Program Statistics
------------------
Command line at 2007 Jan 13 13:00: "D:\ITU\c++\fib\Debug\swap"
Total time: 1,597 millisecond
Time outside of functions: 1,095 millisecond
Call depth: 1
Total functions: 1
Total hits: 1
Function coverage: 100,0%
Overhead Calculated 4
Overhead Average 4
Module Statistics for swap.exe
------------------------------
Time in module: 0,501 millisecond
Percent of time in module: 100,0%
Functions in module: 1
Hits in module: 1
Module function coverage: 100,0%
Func Func+Child Hit
Time % Time % Count Function
---------------------------------------------------------
0,501 100,0 0,501 100,0 1 _main (swap.obj)
Yani bu yazıdan çıkaracağımız pek çok sonuç var, ama en önemlisi vazgeçememek bana sorarsanız...
Sonuç olarak gelmek istediğim, asıl anlatmak istediğin noktaya gelemedim aslında, yazımın amacı kodu okunabilir yapmaktı. Bu konu hakkında bir cümleyle şunu söyleyebilirim ki: eğer elinizde daha efektif bir kod varsa, ve bu kodun okunabilirliği diğer algoritmalara göre daha kötüyse, o kodu, isminden görevinin anlaşılacağı bir fonksyon içerisine gömmek ( tek satır olsa dahi kodunuz) ve programınızı yazarken o koda ihtiyacınız olduğu yerde yazdığınız fonksyonu çağırmaktır. Fonskyonun ismini güzelce belirlerseniz, kodunuzu okuyanın fonksyon içerisinde yazdığınız kodu okuması gerekmeyecektir. Black box olara düşünülebilir fonksyonunuz.
Bir başka önemli nokta da, burada kodu zamanlama bakımından değerlendirdik. Günümüz bilgisayarlarında bellek sorunu olmamaktadır. ANCAK, gömülü sistemlerde, gerçek zaman sistemlerinde ciddi bellek kısıtlamaları vardır. Bu durumda 4 byte lık bir temp değişkenini bile kullanmak istemeyebilirsiniz. Bu durumda xor ile swap işlemi yapmak gerçekten işinize gelebilir. Kodu da büyük ihtimalle assembly ile yazacağınız için bu yöntem daha efektif olacaktır.
Yazı biraz uzadı ama ben yazarken gerçtekten keyif aldım, umarım sizde sıkılmamışsınızdır. Sonraki yazılarımda görüşmek dileğiyle...
Ozgur Macit arkadasimin deyimiyle;
Efendim gecen gun grubumuza bir mail geldi bi arkadasimiz tarafindan. Mail de herkesin bildiği bir swap islemi C/C++ dilinde farkli bir sekilde yazilmisti. Buyrun inceleyelim:
#include
int main(void)
{
int a = 3, b = 5;
a = a^b;
b = a^b;
a = a^b;
printf("%d\n", a);
printf("%d\n", b);
return 0;
}
// ^ XOR islemi bu arada :)
Alinti: Onur Dolu
Goruldugu gibi 3 tane XOR islemi yapilarak a ve b değiskenlerinin yeri değiştirimiş. Xor işlemi matematikte/programlamada çok işe yarıyor daha sonra başka örnekler yazmayı düşünüyorum da biz gelelim esas konuya: Bu kodu bilinen temp değişkeni üzerinden eski değeri saklayıp, daha sonra a ve b değişkenlerinin değerini değiştiren swap koduna tercih edilebilr mi?
Bu arada yukarıda yazılmış olan kod aşağıda ki şekilde de kısaltılabiliyor:
int a = 3, b = 5;
a ^= b ^=a ^=b;
Grupta bulunan sevdiğim bir arkadaşım bu koda doğal olarak karşı çıktı. Nedenini ise şu şekilde açıkladı:
"Okuması da oldukça zor oluyor..
Kod bir kere yazılır ama defalarca okunur..
Senden sonra programda değişlik yapması gereken bir programcı aşağıdaki satırı anlıyamıyabileceğ inden veya daha kötüsü yanlış anlıyabileceğinden saatler veya günler kaydebilir.. Proje başarızlığa uğrayabilir."...
Yukarıda aynen alıntısını aldığım yazı hakkında herhalde herkes fikir birliğine varmıştır. Bu kodu okumak matematik bilgisi çok iyi olmayan bir programcı için imkansıza yakın olduğu gibi, matematik bilgisi olan bir programci için de zaman alıcı ve can sıkıcıdır.
Bunun üzerine ne yapılabilir diye düşündüm. Kod daha efektif duruyordu. Yukardaki probleme bir çözüm üretmeden önce bunu teyid etmem gerekti ( Hazırsanız birazdan kendinizi sonu gelmeyen bir kaosun içinde bulacaksınız!! ) :
Test1 ( yeni swap )
#include
int main(void)
{
int a = 3, b = 5;
int i;
for( i = 0; i < 100001; i++ ){
a ^= b ^= a ^= b;
}
return 0;
}
Bu kodun çalışması sonucunda şu değerler elde edildi:
Program Statistics
------------------
Command line at 2007 Jan 13 11:28: "D:\ITU\c++\fib\Debug\swap"
Total time: 17,024 millisecond
Time outside of functions: 15,794 millisecond
Call depth: 1
Total functions: 1
Total hits: 1
Function coverage: 100,0%
Overhead Calculated 3
Overhead Average 3
Module Statistics for swap.exe
------------------------------
Time in module: 1,231 millisecond
Percent of time in module: 100,0%
Functions in module: 1
Hits in module: 1
Module function coverage: 100,0%
Func Func+Child Hit
Time % Time % Count Function
---------------------------------------------------------
1,231 100,0 1,231 100,0 1 _main (swap.obj)
Görüldüğü gibi döngü yaklaşık 1,231 milisaniye de tamamlandı.
Şimdi ikinci swap işlemini incelemeye geldi sıra. Bildiğmiz temp değişkeni üzerinden elde edilen swap....
Test1 ( klasik swap )
#include
int main(void)
{
int a = 3, b = 5;
int i, temp;
for( i = 0; i < 100001; i++ ){
temp = a;
a = b;
b = a;
}
return 0;
}
Evet heycanla bu kodu yazdım ve sonucu gerçekten çok merak ediyordum. Eğer tahminlerim doğruysa bu kodun çalışması en az 2 kat yavaş olacaktı çünkü fazladan bir temp değişkenine erişim vardı. Belleğe erişimin registerlar üzerinde xor işleminden çok daha fazla sürmesi gerekirdi. Şimdi sonuca bakalım:
Program Statistics
------------------
Command line at 2007 Jan 13 11:35: "D:\ITU\c++\fib\Debug\swap"
Total time: 2,228 millisecond
Time outside of functions: 1,538 millisecond
Call depth: 1
Total functions: 1
Total hits: 1
Function coverage: 100,0%
Overhead Calculated 3
Overhead Average 3
Module Statistics for swap.exe
------------------------------
Time in module: 0,690 millisecond
Percent of time in module: 100,0%
Functions in module: 1
Hits in module: 1
Module function coverage: 100,0%
Func Func+Child Hit
Time % Time % Count Function
---------------------------------------------------------
0,690 100,0 0,690 100,0 1 _main (swap.obj)
Evet herkes gördü sonucu, bende gördüm, gördüm sonra gözlerime inanamadım, inanamadım bir daha çalıştırdım, çalıştırdım yine aynı sonucu gördüm, gördüm gözlerime inanamadım...
Sonuç beni hem hayal kırıklığına uğratmıştı hem de heycanlandırmıştı. Bu sonucu beklemediğim açıktı. Demek ki ya ben bi yerlerde yanlış yaptım, ya da...
Evet ya da derleyici de bir sorun vardı:) Bu arada derleyici MSVS 6.0
Ve derleyiciden şüphe duydum! Daha önceki tecrübelerimden biliyordum ki derleyici assembly kodunu oluştururken arada geçici bellek atamaları yapıyordu. Kodu tekrar yazmalıydım ama bu sefer inline assembly kullanacaktım, kalp atışlarım hızlanmıştı.
Ama, evet ama önce söylediğim şeyin gerçek olduğunu size göstermeliydim. Test1 kodunun disassemblysine baktım işte size de göstereyim ( sadece for döngüsü içinde ki tek satır xor işlemleri) :
00401041 mov ecx,dword ptr [ebp-4] <- varan 1
00401044 xor ecx,dword ptr [ebp-8] <- varan 2
00401047 mov dword ptr [ebp-4],ecx <- varan 3
0040104A mov edx,dword ptr [ebp-8] ....
0040104D xor edx,dword ptr [ebp-4] ....
00401050 mov dword ptr [ebp-8],edx ....
00401053 mov eax,dword ptr [ebp-4] ....
00401056 xor eax,dword ptr [ebp-8] ....
00401059 mov dword ptr [ebp-4],eax <- varan 9
Evet kod gerçekten beni "in a horror" vaziyetine düşürmüştü! ben saf ve masum bir şekilde registerlar da xor işlemi yapiyordum. Ama derleyici her işlem sonucunu bir bellek gözünde saklıyordu!!! bu ise performansı inanılmaz bir şekilde düşürüyordu. Gerçekten vs 6.0 dan korkmaya başlamıştım...
ve test2 deki temp kullanarak gerçekleştirilen 3 atama işlemi ile swap yaptığım kodu disassembly ettim ve işte sonuc:
9: temp = a;
00401041 mov ecx,dword ptr [ebp-4]
00401044 mov dword ptr [ebp-10h],ecx
10: a = b;
00401047 mov edx,dword ptr [ebp-8]
0040104A mov dword ptr [ebp-4],edx
11: b = a;
0040104D mov eax,dword ptr [ebp-4]
00401050 mov dword ptr [ebp-8],eax
Bu kod kesinlikle belleğe daha az erişiyordu. Aslında tam da yapması beklenen işlemi yapıyordu. önce a değişkenini ecx e atıyor sonra ecx i temp bellek gözüne atıyordu ve diğer a=b ve b=a içinde aynılarını yapıyordu.
test2 nin kodu bu kadar iyi çalıştığı halde test1 neden bukadar kötüydü? Şeytan beni iyice kodu inline assembly de yazmaya itiyordu...
Ama önce son bir kez daha brain storming yapmak istedim. Düşündüm... registerlar üzerinde xor işlemi yapmak istiyordum. Ama yazdığım kod registerlar üzerinde değil bellek üzerinde, bellekten değerleri registerlar a çekerek, daha sonra registerlar da gerçekleştirdiği xor işlemlerini tekrar belleğe yazarak gerçekleştiriyordu. Evet yazarken farkediyodum herşeyi, bir emreknlk nasil bu kadar dikkatsizce davranabilirdi, bir emreknlk bu hatayi nasil yapabilirdi!
VS 6.0.. O kadar günahını aldığım derleyici. Aslında ben ne söylüyorsam onu yapıyordu! Onun hiç bir suçu yoktu tek suçlu vardı o da emreknlk! Kodu yazarken dikkat ederseniz değişkenler int a ve int b olarak tanımlamıştım. Yani ben dedim ki: Git esp den ( stack pointer) 8 çıkar! Değişkenleri yığında oluşturuyordum!!! Yani bellekte! sonra nasıl olurda bütün işlemleri registerlar üzerinde yapmasını bekleyebilirdim ki... Bellekten a ve b yi alıp tekrar a ve b yi belleğe yazması gerekiyor derleyicinin çünkü ben ona öyle yap demiştim!
Peki şimdi İnline assembly de yazmama gerek varmıydı? İnline assembly de yazarsam kodun hızlanacağını biliyordum. Çünkü asm kodunu ben gömecektim C nin içine. Ama C nin de bana sunduğu yetenekleri göz ardı edemezdim.
Evet register anahtar sözcüğü ( keyword ) değişkenleri bellekte değil, eğer mümkünse registerlar da oluşturuyordu. hemen
int a, b; ifadesini register int a, b; ifadesi olarak değiştirdim.
Sonucu merak ediyor olmalısınız. Sonuç değişmedi... Bir kaos içinde kalmıştım. Düşündüğüm hiç bir şey çalışmıyordu. Başarısızlık ard arda geliyordu. Belki de beni C ye bu kadar bağlayan şey buydu.
Araştırmaya başladım. niye çalışmadı niye sonuç aynıydı. tekrar disassebly ye baktım sonuçta yine a ve b değişkeni registerlarda değil yığında oluşturulmuştu! peki bu nasıl olabilirdi? Oysa ben açıkça registerlarda oluştur demiştim değişkenleri. Sonunda kendimi msdn de buldum. işte açıklamaları:
"The compiler does not accept user requests for register variables; instead, it makes its own register choices when global register-allocation optimization (/Oe option) is on. However, all other semantics associated with the register keyword are honored."
Evet vs 6.0 register anahtar sözcüğü için bizi sallamıyordu. belki de güvenmiyordu. Bu C nin esnekliğinin bir yerde kaybolması demekti. Düşündüm, kodu linux de derlesem çalışacaktı. Bundan şüphem yoktu. register anahtar sözcüğü ile test1 de yazdığımız xor lu kod hızlı çalışacaktı.
Ama bir sorun vardı! Birden adil davranmadığımı anladım. test2 içinde temp, a ve b değişkenlerini register anahtar sözcüğü ile tanımlasam daha o daha da hızlı çalışacaktı. 3 register da sadece 3 atama işlemi. işte hız buydu...
Peki son bir çırpınış, bu yazıma başlarken hatırlarsanız kodun okunabilirliğini sağlamak için yapılması gerekenleri anlatmak ama önce o kodun hızlı çalışması gerektiğini ispatlamak istiyordum. hızlı çalıştığını ispatlamadan nasıl olurda bu kodu kullanın diyebilirdim ki size, üstelik daha da okunabilirliği azaltırken.
Evet iki koda da adil olarak davrandığım sürece her yola başvurmalıydım. register fikri çok iyi bir fikir değildi. Büyük programlarda bu yöntem patlayabilirdi. Şimdi geriye malesef tek bir çözüm kaldı. Bu çözüm de başarısızlığa uğrarsa... düşünmek bile istemiyorum.
Inline assembly
Evet heycan dorukta. Bu sefer vs60 var bir de ben varım...
işte test 1, xor işlemleri ile yapılan swap işlemi ( Not: bu kod main içinde tanımlanmış ama swap fonksyonu içerisinen alınabilir, testleri yapmak için bu şekilde yazdım) :
#include
int main(void)
{
int a = 3, b = 5;
int i;
for( i = 0; i < 100001; i++ ){
__asm{
mov eax, [a]
mov ebx, [b]
xor eax, ebx
xor ebx, eax
xor eax, ebx
mov [a], eax
mov [b], ebx
}
}
return 0;
}
Gördüğünüz gibi asm kodu visual studio 6.0 içinde main içinde duruyor. şimdi bakalım sonuca:
Profile: Function timing, sorted by time
Date: Sat Jan 13 12:50:05 2007
Program Statistics
------------------
Command line at 2007 Jan 13 12:50: "D:\ITU\c++\fib\Debug\swap"
Total time: 1,702 millisecond
Time outside of functions: 1,112 millisecond
Call depth: 1
Total functions: 1
Total hits: 1
Function coverage: 100,0%
Overhead Calculated 3
Overhead Average 3
Module Statistics for swap.exe
------------------------------
Time in module: 0,589 millisecond
Percent of time in module: 100,0%
Functions in module: 1
Hits in module: 1
Module function coverage: 100,0%
Func Func+Child Hit
Time % Time % Count Function
---------------------------------------------------------
0,589 100,0 0,589 100,0 1 _main (swap.obj)
Evetttt, işte bu mudur? budur dedirten sonuç kendini gösterdi!!! 0.589 milisaniye şu ana kadar elde edilen en iyi süre. Şimdi biraz sevinç duyuyorum ama daha herşey bitmedi diğer kodu da bu şekilde yazıp sonuçları karşılaştırmam gerek.
işte temp kullanılarak gerçekleştirilen swap işleminin inline assembly li hali:
#include
int main(void)
{
int a = 3, b = 5;
int i,temp;
for( i = 0; i < 100001; i++ ){
__asm{
mov eax, [a]
mov [temp], eax ; a nin degeri temp de
mov ebx, [b]
mov [a], ebx ; b nin degeri a da
mov eax, [temp]
mov [b], eax ;temp in degeri b de
}
}
return 0;
}
ve sonuç geliyor:
Profile: Function timing, sorted by time
Date: Sat Jan 13 12:56:28 2007
Program Statistics
------------------
Command line at 2007 Jan 13 12:56: "D:\ITU\c++\fib\Debug\swap"
Total time: 21,834 millisecond
Time outside of functions: 21,197 millisecond
Call depth: 1
Total functions: 1
Total hits: 1
Function coverage: 100,0%
Overhead Calculated 3
Overhead Average 3
Module Statistics for swap.exe
------------------------------
Time in module: 0,637 millisecond
Percent of time in module: 100,0%
Functions in module: 1
Hits in module: 1
Module function coverage: 100,0%
Func Func+Child Hit
Time % Time % Count Function
---------------------------------------------------------
0,637 100,0 0,637 100,0 1 _main (swap.obj)
Evet en sonunda istediğim sonuç... 0.637 milisaniye . Biraz acımasız davrandım ikinci swap a kabul ediyorum ama en iyiye ulaşmak kolay olmuyor tabi :))
Dahası var...
inline assembly kullanarak temp i de atabilirz! işte 3. yol:
#include
int main(void)
{
int a = 3, b = 5;
int i;
for( i = 0; i < 100001; i++ ){
__asm{
mov eax, [a]
mov ebx, [b]
mov [a], ebx ; b nin degeri a da
mov [b], eax ; a nin degeri b de
}
}
return 0;
}
ve işte en kısası şimdi geliyorrrr :)
Profile: Function timing, sorted by time
Date: Sat Jan 13 13:00:59 2007
Program Statistics
------------------
Command line at 2007 Jan 13 13:00: "D:\ITU\c++\fib\Debug\swap"
Total time: 1,597 millisecond
Time outside of functions: 1,095 millisecond
Call depth: 1
Total functions: 1
Total hits: 1
Function coverage: 100,0%
Overhead Calculated 4
Overhead Average 4
Module Statistics for swap.exe
------------------------------
Time in module: 0,501 millisecond
Percent of time in module: 100,0%
Functions in module: 1
Hits in module: 1
Module function coverage: 100,0%
Func Func+Child Hit
Time % Time % Count Function
---------------------------------------------------------
0,501 100,0 0,501 100,0 1 _main (swap.obj)
Yani bu yazıdan çıkaracağımız pek çok sonuç var, ama en önemlisi vazgeçememek bana sorarsanız...
Sonuç olarak gelmek istediğim, asıl anlatmak istediğin noktaya gelemedim aslında, yazımın amacı kodu okunabilir yapmaktı. Bu konu hakkında bir cümleyle şunu söyleyebilirim ki: eğer elinizde daha efektif bir kod varsa, ve bu kodun okunabilirliği diğer algoritmalara göre daha kötüyse, o kodu, isminden görevinin anlaşılacağı bir fonksyon içerisine gömmek ( tek satır olsa dahi kodunuz) ve programınızı yazarken o koda ihtiyacınız olduğu yerde yazdığınız fonksyonu çağırmaktır. Fonskyonun ismini güzelce belirlerseniz, kodunuzu okuyanın fonksyon içerisinde yazdığınız kodu okuması gerekmeyecektir. Black box olara düşünülebilir fonksyonunuz.
Bir başka önemli nokta da, burada kodu zamanlama bakımından değerlendirdik. Günümüz bilgisayarlarında bellek sorunu olmamaktadır. ANCAK, gömülü sistemlerde, gerçek zaman sistemlerinde ciddi bellek kısıtlamaları vardır. Bu durumda 4 byte lık bir temp değişkenini bile kullanmak istemeyebilirsiniz. Bu durumda xor ile swap işlemi yapmak gerçekten işinize gelebilir. Kodu da büyük ihtimalle assembly ile yazacağınız için bu yöntem daha efektif olacaktır.
Yazı biraz uzadı ama ben yazarken gerçtekten keyif aldım, umarım sizde sıkılmamışsınızdır. Sonraki yazılarımda görüşmek dileğiyle...
Subscribe to:
Posts (Atom)