воскресенье, 11 января 2015 г.




               Մեկ տեղում ենք հավաքում բոլոր անծանոթ բառերը․ "Reminder_Wrods" -ի  նոր տարբերակը


     Նախորդ փոքրիկ տերմինալային ծրագիրը, որն օգնում է մեկ տեղում հավաքել օտար լեզվով գրված տեքստն ու թարգմանել այն, ինչպես նաև հիշել անծանոթ բառերը, գրելուց հետո բավականին լավ արձագանքներ ստացվեցին։ Դրական արձագանքների հետ մեկտեղ, կային որոշ դիտողություններ։ 
     Մարդիկ, որոնք օգտագործել են նախորդ ծրագիրը, ուզում են ունենալ պատուհանային ինտերֆեյս (gui), հնարավորություն լինի առանձնացված բառերը հիշել ֆայլի մեջ (save), փոխել պատուհանում բառերի font֊ը և այլն։ 
     Ծրագրի ներկա տարբերր քրոսսպլատֆորմային է, այսինքն այն կարելի է աշխատացնել և՛ windows և՛ linux օհ֊ներում,  ամբողջությամբ փոխված են ցանցի և clipboard֊ի հետ աշխատանքի շարժիչները, զգալի փոփոխվել է բառերի թարգմանման ֆունկցիոնալությունը, ինչպես նաև ավելացվել է պարզ պատուհանային ինտերֆեյս և օգտագործողի menu։ Ներկա և նախորդ տարբերակների միջև միակ ընդհանրությունը, թերևս միայն ծրագրի նպատակն է․ օգնել մեկ տեղում հավաքել և պահել տեքստում հանդիպած անհասկանալի բառերը։



     "Reminder_Wrods" -ի պատուհանային ինտերֆեյսով ներկայացված տարբերակը  դեռ նախնական տարբերակն է և ունի բազմաթիվ լրացումների և սխալների ուղղման կարիք, իսկ լրացումներ, իրոք շատ֊շատ կարող են լինել (ամբողջական տեքստում առանձնացված բառերի վիզուալիզացիա և  անալիզից, բառերի վիճակագրություն և այլն)։      
     Շատ լավ կլինի, եթե գտնվեն մարդիկ, որոնք կցանկանան մասնակցել տվյալ open-source ծրագրի հետագա համատեղ մշակմանը։
Ծրագրի կոդերը կարելի է քաշել հետևյալ հղմամբ 

воскресенье, 4 января 2015 г.



                                                   Սովորում ենք նոր բառեր


Շատ հաճախ կարդում էս անգլերեն տեքստը, հանդիպում են անհասկանալի բառեր և բնականաբար անմիջապես թարգմանում էս դրանք, հասկանում նախադասության իմաստը և անցնում առաջ։ Իսկ երբ հաջորդ անգամ նորից էս հանդիպում այդ անհասկանալի բառերին, ապա նորից չես հիշում :) : Որոշեցի փոքր ինչ շտկել իրավիճակը  և հեշտացնել իմ գործը։ Դրա համար գրեցի փոքրիկ ծրագիր, որը հիշում է անհասկանալի բառերը, թարգմանում է դրանք և վերջում սկուտեղի վրա ցուցակի տեսքով տալիս է քեզ :)
Բառերը պահելու համար ուղղակի պետք է copy անել դրանք (past պետք չէ, դա ավտոմատ է արվում), իսկ վերջում է պետք է փակել ծրագիրը տերմինալից ctrl-C անելով և վերջ։





Ծրագիրը փակելու ընթացքում առանձնացված բառերը թարգմանվում են և սկզբնական բառերի հետ մեկտեղ պահվում են ֆայլում։
Ծրագիրը գրված է linux ubuntu օհ֊ի համար, բայց եթե չալարեմ կգրեմ նաև windows ֊ի տարբերակը :) .


Բառերի շարքի վերևում գրվում է նաև թարգմանման ամսաթիվն ու ժամը։

Այժմ, ունենալով տվյալ տեքստում հանդիպած բոլոր անհասկանալի բառերն ու դրանց թարգմանությունները, կարելի է հետշությամբ դրանք սովորել, ինչպես նաև ունենալ ֆայլի տեսքով :) Ծրագրի կոդերը կարելի է քաշել հետևյալ հղմամբ https://github.com/VardanGrigoryan/Remember_Words ։ Ուրախ կլինեմ լսել առաջարկություններ ու դիտողություններ :)

вторник, 19 ноября 2013 г.

C++ hack կամ private -ը այնքան էլ չի պաշտպանում ձեր տվյալները


Շատերի համար C++ լեզուն առաջին հերթին հետաքրքիր է նրանով, որ թույլ է տալիս անմիջականորեն գործ ունենալ հիշողության հետ ՝ օգտագործելով ցուցիչները (pointers)։ Բայց հիշողությանը ուղղակիորեն դիմելը նաև անկանխատեսելի հետևանքներ կարող է առաջացնել ;)։
Ցուցիչները առաջին հերթին հետաքրքիր են նրանով, որ թույլ են տալիս դիմել հիշողության որոշակի հասցեների և խմբագրել դրանք։ Եվ ահա այստեղ է, որ առաջանում են բազմաթիվ գլխացավանքներ, որոնք կարող են հանգեցնել անկանխատեսելի հետևանքների։ Այս հոդվածում կներկայացնենք մի իրավիճակ, երբ տվյալների պարզ տիպի ցուցիչի (օրինակ int*) միջոցով  կարելի է ստանալ և փոխել դասի private անդամ փոփոխականի արժեքը, այդպիսով խախտելով դասի ինկապսուլացումը !: Դա անելու համար հարկավոր է իմանալ, թե ինչպես է դասի օբյեկտը (նմուշը) պահվում հիշողության մեջ:


Դիցուք ունենք հետևյալ դասը․
class foo
{
int a;
char c;
void bar();
};
Ինչպես ցույց է տրված նկարում, դասի օբյեկտը հիշողության մեջ զբաղեցնում է անընդհատ տիրույթ և այդ տիրույթը զբաղեցնում են միայն դասի անդամ փոփոխականները, տվյալ դեպքում օբյեկտի չափը կլինի 5 բայթ ( int a 4 բայթ + char c 1 բայթ )՝ չհաշված հավասարեցումը ! : Եթե անգամ դասը չի պարունակում ոչ մի անդամ փոփոխական, դրա օբյեկտի չափը տարբեր է զրոյից, իսկ թե որքան է դրա չափը, կախված է կոնկրետ մեքենայից և կոմպիլյատորից։
Դասի անդամ ֆունկցիաները որևէ կերպ չեն անդրադառնում հիշողությամ մեջ օբյեկտի կառուցվածքի վրա։ Պարզ ասած, օբյեկտի զբաղեցրած չափը հիշողության մեջ կախված է միայն դրա անդամ փոփոխականների գումարային չափից, իսկ եթե դասը նաև պարունակում է վիրտուալ ֆունկցիաներ և/կամ գտնում է ժառանգման շղթայի մեջ այնպիսի դասերից, որոնք պարունակում են վիրտուալ ֆունկցիաներ, ապա  օբյեկտի չափի մեջ ներառվում է նաև վիրտուլ ֆունկցիաների աղյուսակի ցուցիչի (vptr) չափը (եթե դասը գտնվում է ժառանգման շղթայի մեջ այնպիսի դասերից, որոնք պարունակում են վիրտուալ ֆունկցիաներ, ապա տվյալ դասի օբյեկտը պարունակում է նաև բազային դասերի վիրտուալ ֆունկցիաների աղյուսակների ցուցիչները)։ Հիմա կասեք, իսկ ինչպե՞ս է այդ դեպքում կանչվում է bar()  ֆունկցիան, եթե այն չի մտնում օբյեկտի հիշողության մեջ։ Ամեն ինչ շատ պարզ է :) ։ Իրականում low level տեսակետից դասի անդամ ֆունկցիան քիչ է տարբերվում գլոբալ ֆունկցիայից։ Տարբերությունը կայանում է նրանում, որ դասի անդամ ֆունկցիայի կանչի դեպքում կոմպիլյատորը գիտի այն օբյեկտի հասցեն, որի միջոցով կանչվում է տվյալ ֆունկցիան և որպես թաքնված պարամետր տվյալ անդամ ֆունկցիան կոմպիլյատորի կողմից ավտոմատ ստանում է this ցուցիչը (տվյալ օբյեկտի հասցեն)։ Այսինքն, եթե ունենք․
foo f;
f.bar();
պսևդոկոդով f.bar()-ը կարելի է գրել հետևյալ կերպ․
foo::bar(&f);
Լավ թողնենք ֆունկցիաների վերաբերյալ փոքրիկ լիրիկական զեղումը և անցնենք private -ին։ Ինչպես գիտենք, եթե դասի փոփոխականները հայտարարված են private, ապա դրանք հասանելի են միայն տվյալ դասի անդամ ֆունկցիաների և friend -ների համար։ Բայց, եթե կարողանանք ստանալ դասի օբյեկտի հասցեն և հաշվի առնելով այն, որ օբյեկտը հիշողության մեջ զբաղեցնում է հաջորդական անընդհատ տիրույթ, ապա կկարողանանք նաև դիմել դրա անդամներին !!! Ասվածը գրված է․
#include <iostream>

class foo
{
    private:
    int a;
    public:
    int get_a(){return a;}
};

int main()
{
   foo obj;                                                            // 1
   int* p = (int*)&obj;                                                // 2 
   *p = 27;                                                            // 3
   std::cout << "Data member value  " << obj.get_a() << std::endl;           // 4

   return 0;
}
Բերված կոդի 1 տողում սովորականի պես ստեղծում ենք foo դասի obj օբյեկտը, իսկ ամենահետաքրքիրը կատարվում է 2-րդ տողում․  2 - րդ տողում (int*)&obj գրառման միջոցով ստանում ենք obj օբյեկտի հասցեն, կամ որ նույնն է, օբյեկտի հասցեական շեղումը հիշողության մեջ։ Օբյեկտի հասցեն հիշողության մեջ ցույց է տալիս այն կետը, որտեղից սկսում է օբյեկտի պարունակութունը, հետևաբար այն միաժամանակ հանդիսանում է նաև օբյեկտի առաջին փոփոխականի հասցեն (մեր մոտ int a անդամ փոփոխականի), այսինքն դիցուք obj օբյեկտի հասցեն է 0x40000000, դա նշանակում է, որ a փոփոխականի հասցեն նույնպես կլինի 0x40000000 կամ որ նույնն է &obj, իսկ c փոփոխականի հասցեն կլինի 0x40000004 (char - տվյալների տիպը հիշողության մեջ զբաղեցնում է 1 բայթ)։ 2 -րդ տողում մենք int* տիպի p ցուցիչին վերագրում ենք մեր օբյեկտի հասցեն, դրանից հետո p ցուցիչը կհղվի մեր obj օբյեկտի շեղման վրա (հիշողության մեջ օբյեկտի սկզբի վրա), իսկ դա նշանակում է, որ 3-րդ տողում *p = 27 գրառումով մենք փոխում ենք p հասցեով 4 բայթ տարածք հիշողության մեջ, դե կռահեք խնդրեմ, թե դա որ փոփոխականի տարածքն է հիշողության մեջ ;): Եվ վերջապես 4-րդ տողում տպելով մեր a private անդամ փոփոխականի արժեքը իրոք համոզվում ենք որ այն հավասար է 27 -ի :D ;)

пятница, 9 августа 2013 г.

Հեշ-աղյուսակ կամ ինչպես շրջանցել կապակցված ցուցակը հաստատուն ժամանակում։ Մաս 1 դասական մոտեցում։

Այս հոդվածում կծանոթանանք այնպիսի դինամիկ բազմության հետ, որի համար սահմանված են բառարանային գործողություններ՝ տարրի ավելացում, ջնջում և փնտրում։ Տվյալների այս կառուցվածքում, տարրի ավելացման և ջնջման ալգորիթմական բարդությունը O(1) -է, իսկ փնտրման ալգորիթմական բարդությունը՝ O(1+a), որտեղ a -ն իրենից ներկայացնում է հեշ աղյուսակի լրացման գործակիցը կամ a = n/m (n -ը տարրերի քանակն է, m-ը հեշ աղյուսակում դիրքերի քանակն է)։ Տվյալների այս կառուցվածքը կոչվում է հեշ-աղյուսակ։  Այստեղ O(1+a) գրառումը չի նշանակում գծային ալգորիթմական բարդություն․ այն նշանակում է ալգորիթմի հաստատուն բարդություն (constant time), քանի որ, եթե համարենք, որ հեշ աղյուսակում դիրքերի քանակը ուղիղ համեմատական է տարերի քանակին, n = O(m) և a = n/m=O(1), ապա Օ(1+a) = O(1): Նաղքան բուն հեշ աղյուսակի մասին խոսելը՝ բերենք մի խնդիր, որը կարելի է լուծել հեշ աղյուսակի կիրառմամբ։


Գրել կոդ, որը չտեսակավորված (unsorted) կապակցված ցուցակից կջնջի կրկնվող տարրերը։ Ալգորիթմի բարդությունը՝  O(n):

Եթե մի պահ ուշադրություն չդարձնենք խնդրի այն պայմանին, որ լուծման ալգորիթմական բարդությունը պետք է լինի O(n), կարելի է խնդիրը լուծել հետևյալ կերպ․

template<class T>
void list<T>::delete_doublicates()
{
 node<T>* c = m_head;
 node<T>* t = c->m_next;
 while(c != 0)
 {
  while(t != 0)
  {
   if( t != c && t->m_data == c->m_data)
   {
    t->m_next->m_prev = t->m_prev; // t տարրի հաջորդ տարրի m_prev ցուցիչին վերագրում ենք t տարրի նախորդ տարրը
    t->m_prev->m_next = t->m_next; // t տարրի նախորդ տարրի m_next ցուցիչին վերագրում ենք t տարրի հաջորդ տարրը
   }
   t = t->m_next;
  }
  c = c->m_next;
  t = m_head;
 }
}
Բայց այս լուծումը չի համապատասխանում խնդրի պահանջին, քանզի այն ունի է O(n^2) ալգորիթմական բարդություն, պահանջվող O(n) փոխարեն, քանի որ ունենք երկու ցիկլ, որոնցից մեկը ներդրված է մյուսի մեջ։ Ահա այստեղ  կանգնում ենք հակասության առջև․ մի կողմից հետևելով դասական եղանակին, ցուցակից հերթական n-րդ տարրը ստանալու համար պետք է այն շրջանցել, այլ կերպ ասած կատարել n քայլ և հետո նոր միայն վերցնել այդ տարրի արժեքը, մյուս կողմից O(n^2)  քառակուսային բարդությունից խուսափելու համար պետք է բացառել երկրորդ ներդրված ցիկլը ( while(t != 0) ... ), իսկ դա նշանակում է ստանալ ցուցակից հերթական տարրը հաստատուն ժամանակում O(1), այս դեպքում ընդհանուր ալգորիթմական բարդությունը կլինի O(n): Բայց ինչպես կապակցված ցուցակից մուտք գործել կամայական տարրի արժեքին ՝ չշրջանցելով ցուցակը տարր առ տարր։ Ահա այստեղ է, որ մեզ օգնության է հասնում «հեշ աղյուսակ» կոչվող տվյալների կառուցվածքը: 
Երկրորդ մասում կներկայացնենք «հեշ-աղյուսակ» տվյալների կառուցվածքն ու դրա իրականացումը (implementation), ինչպես նաև վերը բերված խնդրի լուծումը O(n) ալգորիթմական բարդությամբ։

суббота, 3 августа 2013 г.

Տվյալների տարրական կառուցվածքներ։ Երկկապակցված գծային ցուցակ (doubly linked list)


Գծային ցուցակում տարրերը հաջորդականորեն կարգավորված են, բայց տարրերի հաջորդման կարգը որոշվում է ոչ թե ինդեքսով, այլ ցուցիչով, որոնք հանդիսանում են «ցուցակ» տվյլաների կառուցվածքի անբաժանելի մաս և դրանով  է պայմանավորված տվյալների այս կառոցվածքի հիմնական տարբերությունը մյուսից՝ վեկտորից ( պարզ ասած՝ զանգվածից)։ 

Վեկտոր 
  1. դիմում ցանկացած տարրին ինդեքսով, ալգորիթմի բարդությունը O(1),
  2. վեկտորի սկզբում կամ միջնամասում նոր տարր ավելացնելու ալգորիթմի բարդութույունը՝ O(n),
  3. վեկտորի վերջում տարրի ավելացման ալգորիթմի բարդութույունը՝ O(1),
  4. վեկտորում տարրի փնտրման ալգորիթմի բարդութույունը՝ O(n):
Ցուցակ
  1. ցուցակի ցանկացած տարրին դիմումը կատարվում է մնացած տարրերի վրայով հաջորդական շրջանցման միջոցով, այդ պատճառով ալգորիթմի բարդութույունն է O(n),
  2. ցուցակում ցանկացած հատվածում նոր տարր ավելացնելու ալգորիթմի բարդութույունն է O(1),
  3. ցուցակում տարրի փնտրման ալգորիթմի բարդութույունն է ՝ O(n):

Կախված խնդրից՝ կարելի, որպես տվյալների կառուցվածք ընտրել սրանցից ցանկացածը կամ համատեղել դրանք։
Օրինակ՝ եթե մեզ նախորոք հայտնի է մուտքի տվյալների չափը, ապա նախընտրելի է օգտվել վեկտորից, քանի որ այն ավելի արագ տվյալների կառուցված է (տես վերը բերված հիմնական գործողությունների ալգորիթմական բարդությունները), իսկ եթե տվյալների չափը դինամիկ փոփոխվում է կատարման ընթացքում, ապա այստեղ ավելի հարմար է օգտվել ցուցակից, քանի որ այն չի պահանջում նախորոք նշել իր մեջ պահվող տարրերի առավելագույն քանակը, բայց այստեղ մենք ունենում ենք արագության կորուստ (տես վերը բերված հիմնական գործողությունների ալգորիթմական բարդությունները)։ Իսկ օրինակ՝ հեշ-աղյուսակներում և գրաֆերում (գրաֆի կապակցված ցուցակներով պահման դեպքում) այս երկու տվյալների կառուցվածքները համատեղվում են։



Ցուցակի յուրաքանչյուր տարր ունի key արժեք և prev ու next ցուցիչներ: «key» այն տվյալն է, որը պարունակում է տվյալ տարրը, prev- ը ցուցիչ է տվյալ տարրին նախորդղ տարրի վրա, next -ը ցուցիչ է, որը ցույց է տալիս տվյալ տարրին հաջորդող տարրի վրա։ Բացի այդ ցուցակը ունի ևս երկու ցուցիչներ, դրանք են head -ը և tail -ը։ head -ը հանդիսանում է տարր, որը պարունակում է ցուցիչ ցուցակի առաջին տարրի վրա կամ այլ կերպ ասած head-ը ցուցակի առաջին տարրն է կամ գլուխը։
tail-ը հանդիսանում է ցուցակի վերջին տարրը կամ պոչը։
Կապակցված ցուցակները լինում են մի քանի տեսակի․
  1. միակապակցված ցուցակ, երբ ամեն տարր պարունակում է միայն next ցուցիչ,
  2. երկկապակցված ցուցակ, երբ ամեն տարր պարունակում է և՛ netx և՛ prev ցուցիչները,
  3. xor կապակցված ցուցակ, երբ ամեն տարր հանդիսանում է նախորդ և հաջորդ տարրերի հասցեների միջև կիրառված տրամաբանական xor գործողության արդյունք,
  4. շրջանաձև ցուցակ, երբ ցուցակի վերջին տարրը պարունակում է ցուցիչ առաջին տարրի վրա և այլն։
Անկախ կապակցված ցուցակի կոնկրետ տիպից, այն պարունակում է ընդհանուր բոլոր տիպերի համար գործողություններ, որոնք փոխում են «կապակցված ցուցակ» աբստրակցիայի նմուշ հանդիսացող օբյեկտների ներքին վիճակը։ Այդ գործողություններն են․

  1. List-Search(L, k), կամ L ցուցակում K բանալով տարրի փնտրում․
          Այս գործողությունը ունի Ѳ(n) ալգորիթմական բարդություն կամ f(n) = Ѳ(g(n)), որտեղ n = g(n),սա նշանակում է, որ գոյություն ունեն այնպիսի c1 և c2 թվեր, որ c1*n =  f(n) <= c2*n, իսկ վերջինս էլ նշանակում է, որ այս գործողության ալգորիթմական բարդության Ѳ(n) ֆունկցիան սահմանափակված է և վերևից և ներքից, օրինակ, եթե f(n) = n, այսինքն տվյալ k տարր ըգտնելու համար պետք է անցնել n հատ տարրերի վրայով, ապա միշտ կարելի է գտնել այնպիսի երկու C1 (C1 = c1*n) C2 (C2 = c2*n) թվեր, որ C1 <= n <= C2, օրինակ՝ եթե փնտրվող թիվը ցուցակում հենց առաջին տարրն է, ապա ալգորիթմում կկատարվի ըմդամենը մեք քայլ կամ c1*n = c1, իսկ վատագույն դեպքում, երբ փնտրվող տարրը ցուցակի վերջում է կկատարվի n քայլ կամ c2*n = c2*n և կստանանք՝ c1 <= n <= c2*n,այստեղ կարող ենք ընտրել կամայական երկու թիվ, այնպես, որ պահպանվի բերված անհավասարությունը․ օրինակ՝ c1 = 1, c2 = 100, կստանանք՝ 1 <= n <= 100*n

      2. List-Insert(L, x) կամ x արժեքով տարրի  ավելացում ցուցակի սկզբում

      
Այս գործողության ալգորիթմական բարդությունն է O(1), այսինքն կատարվում է հաստատուն ժամանակում։ Ընդանհրապես հաստատուն ժամանակը չի նշանակում, որ բոլոր համակարգիչների վրա, այս գործողությունը կատարվում է միևնույն ժամանակում, այլ նշանակում է, որ տվյալ ալգորիթմը  անկախ է մուտքի տվյալների չափից և կատարվում է սահմանափակ ժամանակահատվածում, իսկ ահա այդ սահմանափակ ժամանակահատվածը կարող է տարբեր լինել տարբեր համակարգիչներչ վրա։ 
      3. List-Delete(L, x) կամ x արժեքով տարրի ջնջում ցուցակից․


Զուտ տարրի ջնջման ալգորիթմական բարդությունը O(1) է, բայց քանի որ սկզբում նախ պետք է փնտրել և գտնել տարրը (կատարվում է List-Search(L, k) գործաղությունը Ѳ(n) բարդությամբ և այնուհետև նոր ջնջել, (կատարվում է զուտ ջնջումը Ѳ(1) ժամանակում ապա գործողության ալգորիթմական բարդությունը դառնում Ѳ(n), քանի որ Ѳ(f(n)) + Ѳ(g(n)) = Ѳ(max(f(n), g(n))) կամ Ѳ(n) + Ѳ(1) = Ѳ(max(n, 1)) = Ѳ(n)
Այս հոդվածում մենք կներկայացնենք երկապակցված ցուցակի իրականացումը։ Ցուցակը բաղկացած է միմյանց հետ կապակցված տարրերից (node-երից)․
Բերենք node.hpp ֆայլի կոդը․

#ifndef NODE_HPP
#define NODE_HPP

template <class T >
class list;

template <class T >
class node
{
 private:
  friend class list<T>;

 private:
  node* m_next;
  node* m_prev;
  T m_data;

 public:
  node() :   m_next(0)
           , m_prev(0)
           , m_data(0) {}

  explicit node(T d) :  m_next(0)
                      , m_prev(0)
                      , m_data(d) {}
  T get_data()
  {
       return m_data;
  }
};

#endif // NODE_HPP
Ցուցակը կպարունակի հետևյալ հիմնական գործողությունները․
  1. node<T>* find(T data) const,
  2. void insert_at_front(T data),
  3. void insert_at_back(T data),
  4. void delete_at_front(),
  5. void delete_at_back(),
  6. bool is_empty() const ։
Բերենք ցուցակի կոդը list.hpp
    #ifndef LIST_HPP
    #define LIST_HPP
    
    #include <cassert>
    #include <iostream>
    
    #include "node.hpp"
    
    template <class T >
    class list
    {
     private:
      node<T>* m_head;
      node<T>* m_tail;
      unsigned m_size;
    
     public:
      list() :   m_size(0) 
         , m_head(0)
         , m_tail(0){}
    
      node<T>* get_new_node(T d) const;
      node<T>* find(T data) const;
      void insert_at_front(T data);
      void insert_at_back(T data);
      void delete_at_front();
      void delete_at_back();
      bool is_empty() const;
      void print() const;
      node<T>* get_begin() const
      {
       return m_head;
      }
      node<T>* get_end() const
      {
       return m_tail;
      }
      unsigned get_size() const
      {
       return m_size;
      }
      ~list();
    
     public:
      list(const list&);
      list<T>& operator=(const list&);
    };
    
    template <class T >
    list<T>::list(const list& o)
    {
     this->m_size = o.m_size;
     this->m_head = new node<T>();
     this->m_tail = new node<T>();
     *(this->m_head) = *(o.m_head);
     *(this->m_tail) = *(o.m_tail);
    }
    template <class T >
    list<T>::~list()
    {
     delete m_head;
     delete m_tail;
     m_head = 0;
     m_tail = 0;
    }
    
    template <class T >
    list<T>& list<T>::operator=(const list& o)
    {
     if(this == &o)
     {
      return *this;
     } else
     {
      this->m_size = o.m_size;
      delete this->m_head;
        delete this->m_tail;
      this->m_head = new node<T>();
      this->m_tail = new node<T>()
      *(this->m_head) = *(o.m_head);
      *(this->m_tail) = *(o.m_tail);
      return *this;
     }
    }
    template <class T >
    node<T>* list<T>::get_new_node(T data) const 
    {
     node<T>* n = new node<T>(data);
     assert(n != 0);
     return n;
    }
    
    template <class T >
    bool list<T>::is_empty() const
    {
     if(m_head == 0)
     {
      return true;
     }
     return false;
    }
    
    template <class T >
    void list<T>::print() const 
    {
     if(is_empty())
     {
      return;
     }
     node<T>* t = m_head;
     while(t != 0)
     {
      std::cout << t->m_data << " ";
      t = t->m_next;
     }
    }
    
    template <class T >
    node<T>* list<T>::find(T data) const
    {
     if(is_empty())
     {
      return 0;
     }
     node<T>* t = m_head;
     while(t != 0 && t->m_data != data)
     {
      t = t->m_next;
     }
     return t;
    }
    
    template <class T >
    void list<T>::insert_at_front(T data)
    {
     node<T>* n = get_new_node(data);
     if(m_head == 0)
     {
      m_head = m_tail = n;
      n->m_next = n->m_prev = 0;
      ++m_size;
     }
     else
     {
      n->m_next = m_head;
      if(m_head != 0)
      {
       m_head->m_prev = n;
      }
      m_head = n;
      n->m_prev = 0;
      ++m_size;
     }
    }
    
    template <class T >
    void list<T>::insert_at_back(T data)
    {
     node<T>* n = get_new_node(data);
     if(m_tail == 0)
     {
      m_head = m_tail = n;
      n->m_next = n->m_prev = 0;
      ++m_size;
     }
     else
     {
      m_tail->m_next = n;
      n->m_prev = m_tail;
      m_tail = n;
      ++m_size;
     }
    
    }
    
    template <class T >
    void list<T>::delete_at_front()
    {
     if(is_empty())
     {
      return;
     }
     else
     {
      node<T>* t = m_head->m_next;
      t->m_prev = 0;
      delete m_head;
      m_head = t;
      --m_size;
     }
    }
    
    template <class T >
    void list<T>::delete_at_back()
    {
     if(is_empty())
     {
      return;
     }
     else
     {
      node<T>* t = m_tail->m_prev;
      t->m_next = 0;
      delete m_tail;
      m_tail = t;
      --m_size;
     }
    }
    
    #endif // LIST_HPP
    

понедельник, 22 июля 2013 г.

Խելացի ցուցիչներ (smart pointers), auto_ptr implementation


C++ ծրագրավորման լեզուն երևի թե այդքան էլեգանտ չեր լինի, եթե նրանում չլինեին ցուցիչնեչը, որոնց միջոցով ծրագրավորողը հնարավորություն է ստանում անմիջականորեն աշխատել հիշողության հասցեների հետ և ճիշտ օգտագործման դեպքում ցուցիչը հզոր զենք է հանդիսանում ծրագրավորողի ձեռքում։ Բայց ինչպես և ցանկացած զենքի դեպքում ծրագրավորողը ապահովագրված չէ սեփական ոտքին կրակելուց :) :
C++ ծրագրավորման պրակտիկայում սեփական ոտքին կրակելու դեպքերը առավել շատ հանդիպում են հենց ցուցիչների ոչ ճիշտ օգտագործման դեպքում։ Ցուցիչների ոչ ճիշտ օգտագործման առավել հաճախ հանդիպող դեպքերից են․

  1. ջնջված ցուցիչի կրկնակի ջնջում,
  2. դիմում զրոյական ցուցիչի պարունակությանը,
  3. հիշողության արտահոսք։

Խելացի ցուցիչների օգտագործումը թույլ է տալիս խուսափել վերը նշված սխալներից։ Ասվածն ավելի համոզիչ դարձնելու համար բերենք հետևյալ օրինակը․ դիցուք ունենք հետևյալ ֆունկցիան․


void calculate(int n)
{
	int* p = new my_class();
	int j = 0;
	…......................
	if(j == 5)
	{
		return;
	}
	delete p;
}
Բերված օրինակում կարծես թե ամեն ինչ գրագետ է գրված, բայց հաշվի չի առնված մեկ բան․ եթե յ փոփոխականի արժեքը կոդում ինչ-ինչ պատճառներով լինի 5, ապա ֆունկցիան կավարտի իր աշխատանքը։
Հիմա կասեք է թող ավարտի, մեկ է int* p -ն ստեկում է, իսկ ֆունկցիայից դուրս գալուց հետո ստեկում՝ ֆունկցիայի արգումենտների և լոկալ փոփոխականների կողմից զբաղեցրած հիշողությունը ազատվում է (ՈՒՇԱԴՐՈՒԹՅՈՒՆ շատերին թվում է թե ստեկում հիշողություն ազատել նշանակում է լոկոլ փոփոխականների և արգումենտների արժեքների ջնջում, բայց այդպիսի բան տեղի չի ունենում, փոխարենը հիշողության այդ մասը սկսումէ դիտարկվել որպես ազատ և ենթակա է կրկնակի զբաղեցման, իսկ ամենահետաքրքիրն այն է, որ եթե տրված տիրույթում վերագրում տեղի չի ունեցել, շնորհիվ ցուցիչների մենք հանգիստ կարող ենք ստանալ “ջնջված” փոփոխականների արժեքները)։ Իսկ իրականում լոկալ փոփոխական է հանդիսանում միայն ցուցիչը՝ int* p, որը և պահվում է ստեկում, իսկ այ դրա արժեքը, այսինքն հիշողության այն հատվածը, որտեղ իսկապես պահվում է p ցուցիչի արժեքը, գտնվում է heep -ում և ֆունկցիայից դուրս
ոչ մի կերպ չի ազդում heep -ի պարունակության վրա։ Այդ պատճառով p փոփոխականի զբաղեցրած տիրույթը այդպես էլ մնում է զբաղված։ Իրականում դա չգիտեմ ինչ կարգի վտանգավոր սխալ չէ, բայց որպես կանոն, եթե ծրագիրը աշխատում է բավականին երկար ժամանակ կամ հիշողությունը հատկացվումէ ցիկլում կամ մենք գործ ունենք բավականին մեծ և լուրջ պրոյեկտի հետ նման սխալները հանգեցում են ծրագրի անկանխատեսելի վարքի և ծրագրի debug -ը վերածվում է չափազանց անդուր պրոցեսի։ Այդ պատճառով հարկավոր է իսկզբանե աչքաթող չանել մնան դեպքերը և դա կերաշխավորի ծրագրի հետագա ավելի հեշտ սպասարկումն ու օգտագործումը։Ելնելով վերը բերված դատողություններից կարող ենք գրել հետևյալ կոդը․
void calculate(int n)
{
	int* p = new m_class();
	int j = 0;
	…......................
	…......................
	if(j == 5)
	{
		delete p;
		return;
	}
	delete p;
}
Բայց ցավոք, թե բարեբախտաբար այս տարբերակը ևս համապիտանի լուծում չէ, քանի որ բավական մեծ պրոյեկտի և կոդի դեպքում, ծրագրի շատ մասեր զուտ մարդկային գործոնի պատճառով ֆիլտրվում են ծրագրավորողի ենթագիտակցության կողմից և դա բերում է վերը նկարագրված վատ հետևանքներին։ Այդ պատճառով բոլոր հնարավոր տեղերում delete p;-ի ավելացումը ևս չի կարող համարվել համապիտանի լուծում։
Այդ պատճառով ծրագրավորման պրակտիկայում ներմուծվել է խելացի ցուցիչների գաղափարը, երբ տեղի է ունենում ցուցիչի ինկափսուլացում օբյեկտի մեջ և մենք արդեն գործ ենք ոնենում ոչ թե սովորական փոփոխականի հետ, այլ օբյեկտի հետ։ Հիմա էլ կասեք, ինչ տարբերություն սովորական փոփոխականն էլ է հիշողության մեջ զբաղեցնում անընդհատ տիրույթ, օբյեկտն էլ։ Այդ դուք ճիշտ էք, բայց օբյեկտի դեպքում տեղի է ունենում կոնստրուկտորի և դեստրուկտորի կանչ, համապատախանաբար օբյեկտի ստեղծման և վերացման դեպքերում։ Ահա այդ հատկությունն է, որ իր իրականացում է գտնում խելացի ցուցիչների իրականացման դեպքում և ծրագրավորողին ապահովագրում է գլխացավանքից։
Առավել տարածված խելացի ցուցիչնեռի գաղափարի իրականացում են հանդիսանում.

  1. std::auto_ptr,
  2. std::tr1::shared_ptr,
  3. boost::scoped_ptr:

std::auto_ptr խելացի ցուցիչներից ամենապարզն է, այստեղ չկա հիշողության միևնույն տիրույթի վրա հղվող ցուցիչների հաշվարկ։ Ցանկացած նոր օգտագործում է բերում է ցուցիչի հղման փոփոխություն․
template <class T> class auto_ptr
{
	T* ptr;
	public:
	explicit auto_ptr(T* p = 0) : ptr(p) {}
	~auto_ptr()
	{
		delete ptr;
	}
	T& operator*()
	{
		return *ptr;
	}
	T* operator->()
	{
		return ptr;
	}
};
template <class T>
auto_ptr<T>& auto_ptr<T>::operator=(auto_ptr<T>& rhs)
{
	if(this!= &rhs)
	{
		delete ptr; // ջնջում ենք հին արժեքը
		ptr = rhs.ptr; // վերագրում են նոր արժեքը
		rhs.ptr = 0; // զրոյացնում ենք պարամետրով տրված օբյեկտը
	}
	return *this;
}
Ահա վերը բերված կոդում ներկայացված է auto_ptr -ի հայտարարությունը, այստեղից ակնհայտ է, որ auto_ptr կոնստրուկտորի ցանցացած կանչ նոր պարամետրով կբերի ptr -ի արժքեի փոփոխության, այսինքն, եթե ունենք
1. std::auto_ptr,
2. std::tr1::shared_ptr,
3. boost::scoped_ptr:
ապա a -ն կսկսի հղվել p2 -ի վրա։

Այս դեպքում վերը բերված օրինակում կարող ենք հանգիստ վարվել հետևյալ կերպ․
void calculate(int n)
{
	int* p = new int;
	auto_ptr<my_class>a(p);
	int j = 0;
	…......................
	…......................
	if(j == 5)
	{		
		return;
	}	
}
Ֆոկուսը կայանում է նրանում, որ ցանկացած դեպքում և՛ ֆունկցիայից նորմալ դուրս գալու և՛ օրինակ բացառիկ դեպքերում կկանչվի օբյեկտի դեստրուկտորը և կկատարվի զբաղեցված հիշողության ազատում, քանի որ անգամ բացառիկ դեպքերում երաշխավորվում է ստեկային փոփոխականների վերացում։

среда, 10 июля 2013 г.

cmd (command prompt) հրամաններ և օրինակներ


cmd -ի հրամանների հակիրճ ցանկը
ASSOC     բերում է հրամանի կողքը նշված ձևաչափի ֆայլերը ցուցակը
ATTRIB    ֆայլի ատրիբուտների փոփոխում/տպում էկրանին
BREAK    CTRL+C ստեղների սեղման մշակման ռեժիմի միացում/անջատում
CD    ընթացիկ դիրեկտորիայի փոփոխություն
CHDIR    ընթացիկ դիրեկտորիայի փոփոխություն
CHKDSK    Սկավառակի ստուգում և ստատիստիակյի արտածում
CLS    մաքրում է էկրանը
CMD    բացում է ևս մեկ cmd պատուհան
COLOR    cmd -ի Տեքստի և ֆոնի գույների սահմանում
COMP    համեմատում է երկու տարբեր ֆայլերի պարունակությունը, բայց միայ երբ դրանց չափերը նույնն են
CONVERT    FAT ֆայլային համակարգով տրամաբանական հատորների փոխակերպում NTFS ֆայլային համակարգի
COPY    COPY- է անում մեկ կամ/և ավելի ֆայլեր ուրիշ տեղ
DATE    ընթացիկ ժամանակի սահմանումը կամ էկրանին արտածումը
DEL    մեկ կամ/և ավելի ֆայլերի ջնջում
DIR    ցույց է տալիս ընթացիկ կամ պարամետրով տրված դիրեկտորիայի (պանակի) պարունակւթյունը list-ով
ECHO    տպում է էկրանին պարամետրով տրված տողը
ERASE   մեկ կամ մի քանի ֆայլերի ջնջում
EXIT    փակում է CMD -ն
FC    երկու ֆայլերի համեմատում և նրանց միջև տարբերությունների արտածում, ինչքան տարբերությունները մեծ են, այդքան համեմատման արդյունքը, որը արտածվում է էկրանին մեծ կլինի
FIND    տրված բառի փնտրում տրված պանակում (տրված path-ով)
FINDSTR    ֆայլերում տողերի փնտրում
FOR    for ցիկլ, կարելի է օրինակ օգտագրոծել տվյալ հրամանը մի քանի ֆայլի հետ կատարելու համար
HELP    արտածում է Windows-ի հրամանների ցանկը և կարճ նկարագրությունը
IF    պայմանական անցման օպերատոր
MD    պանակի (directory) ստեղծում
MKDIR    պանակի (directory) ստեղծում
MODE    com և lpt պորտերով կպած սարքերի պարամետրերի արտածում և կարգաբերում
MOVE     մեկ կամ մի քանի ֆայլերի տեղափոխում այլ path-ով
PRINT    տեքստային ֆայլի տպում (տպում ոչ թե էկրանին, այլ տպում տպիչ սարքով)
RD    պանակի (directory) ջնջում
RECOVER    վնասված սկավառակից ինֆորմացիայի վերականգնում
REN     ֆայլերի և պանակների անվանափոխում
RENAME     ֆայլերի և պանակների անվանափոխում
REPLACE    մեկ ֆայլի փոխարինումը մյուսով
RMDIR    պանակի ջնջում
START    ծրագրի բացումը նոր cmd պատուհանից
TIME    համկարգային ժամացույցի արտածում և կարգաբերում
TITLE    ընթացիկ CMD պատուհանի անվանափոխում պարամետրով տրված արժեքով
TREE    պարամետրով տրված պանակի կամ սկավառակի path -ի գրաֆիկական պատկերումը ծառի տեսքով
TYPE    ըեքստային ֆայլերի պարունակության արտածում էկրանին
VER    Windows -ի տարբերակի մասին տեղեկատվության արտածում
XCOPY    պանակների ծառի և ֆայլերի copy
Եվ վերջում մի քանի օրինակներ
1. փոխում ենք ընթացիկ դիրեկտորիան օրինակ C:\ -ից C:\Program files` cd C:\Program FIles, կարող էք cd -ից հետո հավաքել բառի կեսը և սեղմել “tab” և եթե նման անունով դիրեկտորիա կամ ֆայլ գոյությու ունի ավտոմատ կլրացվի windows -ի կողմից,
2. Եթե ուզում ենք մեկ մակարդակ հետ գնալ ընթացիկ դիրեկտորիայից գրում ենք cd.., cd և “..” -ի արանքում բացակ (space, пробел) կարող է լինել, կարող է և չլինել: Կարող ենք նաև ավելի շատ հետ գնալ. օրինակ՝ եթե ուզում ենք 3 մակարդակ հեը գնալ գրում ենք cd…… (3 զույգ “..”)
3. փնտրում ենք տող նշված ֆայլում find “movl (%ebx), %eax” C:\Documents and Settings\docs\exploit.asm, “movl (%ebx), %eax”-ի փոխարեն կարող էք նշել ցանկացած տող օրինակ “exanake ajsor cerek@ ev vaxy gisher@…” :D
4. Ստեղծում ենք դիրեկտորիա (folder, папка) ` mkdir my_folder, կստեղծվի my_folder անունով պանակ
5. Պանակի ջնջում ` rd my_folder, կջնջվի my_folder-ը իր պարունակությամբ
6. Ֆայլի ստեղծում ` type nul > my_file.doc, արդյունքում կստեղծվի  my_file.doc անունով ֆայլ,
7. ֆայլի ջնջում՝ del my_file, արդյունքում կջնջվի  my_file ֆայլը,
8.Ընթացիկ պանակի ֆայլերի և պանակների ցուցակի դիտում՝ dir , արդյունքում էկրանին կտպվի ընթացիկ պանակի պարունակությունը ցուցակի տեսքով,
9. Տրված պանակի ֆայլերի և պանակների ցուցակի դիտում՝ dir C:\Program Files, արդյունքում էկրանին կտպվի C:\Program Files ճանապարհով պանակի պարունակությունը ցուցակի տեսքով,
10. ֆայլի տեղափոխում՝ move aaa.txt   ./.. ,  արդյունքում aaaa.txt ֆայլը ընթացիկ պանակից կտեղափոխվի մեկ մակարդակ հետ, դրա փախարեն կարող էինք տալ ցանկացած այլ բացարձակ ճանապարհ, օրինակ՝ move aaa.txt C:\Documents and Settings\docs\my_docs\bbb
11. cmd -ի փակում ՝ exit
12. տողի տպում՝ echo my_string, արդյունքում էկրանին կտպվի my_string -ը,
13. cmd -ով կարելի է նաև ծրագրել թողարկել: Դրա համար cmd ից cd էք (գնում էք էլի :D ) լինում տվյալ exe-ն պարունակող դիրեկտորիա և այդտեղից գրում եք exe ֆայլի անունը և տալիս enter: Օրինակ՝ ենթադրենք մեր ընթացիկ պանակը C:\> -ն է և մենք ուզում ենք աշխատացնել C:\Program files\Microsofr office\windord.exe -ն: Դրա համար գրում ենք cd C:\Program files\Microsofr office\ և հետո գրում ենք՝ windord.exe: Կամ կարող ենք մեր ծրագրի դիրեկտորիան դարձնել միջավայրի փոփոխական գրելով միայն ծրագրի անունը ցանկացած պանակից աշխատացնել մեր ծրագիրը: Դրա համար անում ենք հետևյալը՝ set PATH=C:\Program files\Microsofr office\ : Հիմա, երբ մենք գրենք ցանկացած տեղից ընդամենը մեր exe -ի անունը windows -ը այն կփնտրի առաջին հերթին PATH փոփոխականի վերագրված պանակներում, իսկ այնտեղ կա նաև մեր պանակը ուստի առանց խնդիրների կգտնի մեր winword.exe-ն և կաշխատացնի :D
Մի խոսքով շատ կարելի է խորանալ. ասեմ նաև որ ցանկացած հրամանի վերաբերյալ տեղեկատվություն ստանալու համար գրում էք հրամանը և կողքը գրում էք /? նշանները, օրինակ՝ move /?:
Ընդանհրապես shell -ի հրամանների իմացությունը լայն հնարավորություններ է տալիս (եթե իհարկե cmd կարելի է անվանել shell……….) նամանավանդ ֆիզիկապես այլ տեղ գտնվող համակարգչի հետ աշխատանքի դեպքում:

вторник, 9 июля 2013 г.

Linux (*nix) հիմնական հրամաններ:


Այստեղ բերված են debian դիստրիբյուտիվին պատկանող  Linux ubuntu օհ-ի հիմնական հրամանները, բայց դրանց ճնշող մեծամասնությունը նաև այլ դիստրիբյուտվների linux օհ-ի հրամաններ են:
archՑույց է տալիս համակարգչի ճարտարապետությունը
uname -m
uname -rՑույց է տալիս օհ-ի միջուկի տարբերակը
hdparm -i /dev/sda1(կամ sd2, sd3, այստեղ նշում էք,
տրամաբանական սկավառակիid-նորըբերված
օրինակում sd1-ն է)
Ցույց է տալիս կոշտ սկավառակի պարամետրերը
hdparm -tT /dev/sda1(կամ sd2, sd3,այստեղ նշում էքտրամաբանական
սկավառակիid-նորը բերված օրինակում sd1-նէ)  
Ցույց է տալիս կոշտ սկավառակից տվյալների ընթերցման արդյունավետությունը
cat /proc/cpuinfoՑույց է տալիս ինֆորմացիա պրոցեսորի վերաբերյալ
cat /proc/interruptsՑույց է տալիս ինֆորմացիա ընդհատումների վերաբերյալ
cat /proc/meminfoՑույց է տալիս օգտագործված հիշողության մասին ինֆորմացիա
cat /proc/swapsՑույց է տալիս ինֆորմացիա swap –ի վերաբերյալ
cat /proc/versionՑույց է տալիս միջուկի տարբերակը
cat /proc/net/devՑույց է տալիս ցանցային ինտերֆեյսները
cat /proc/mountsՑույց է տալիս mount արված ֆայլային համակարգերը
lspci -tvԾառի տեսքով ցույց է տալիս PCI ինտերֆեյսով սարքավորումները
lsusb -tvԾառի տեսքով ցույց է տալիս USB ինտերֆեյսով սարքավորումները
dateՑույց է տալիս համակարգային ժամանակը
shutdown -h nowԿանգնեցնում են օպերացիոն (գործառնական)համակարգը
init 0
telinit 0
shutdown -h hours:minutes &Կանգնեցնում է օպերացիոնհամակարգը (օհ)նշվածժամանակով
shutdown -cչեղյալ է համարում օհ –ի կանգնեցումը
shutdown -r nowՕհ –ի վերաբեռնում (restart, reset, reboot, перегрузка)
reboot
logoutԴուրս է գալիս համակարգից
cd /homeԳնում է  ’/home’ դիրեկտորիա
cd ..Գնում է մեկ մակարդակ ավելի վեր գտնվող դիրեկտորիա
cd ../..Գնում է երկու մակարդակ ավելի վեր գտնվող դիրեկտորիա
cdԳնում է  ’/home’ դիրեկտորիա
 cd /home/user/cods/ /home/user/cods/ բացարձակ ճանապարհով գնում է cods դիրեկտորիա
cd –Գնում է նախորդ դիրեկտորիան, որտեղից եկել էինք ընթացիկ դիրեկտորիա
pwdՑույց է տալիս ընթացիկ դիրեկտորիայի ճանապարհը
lsՑույց է տալիս ընթացիկ դիրեկտորիայի պարունակությունը
ls -lՑուցակի ձևով ցույց է տալիս ընթացիկ դիրեկտորիայի պարունակությունը, ինչպես նաև ֆայլերի և դիրեկտորիաների permission-երը
ls -aՑույց է տալիս բացի ընթացիկ դիրեկտորիայի պարունակությունից նաև թաքնված ֆայլերն ու դիրեկտորիաները
mkdir dir1Ստեղծել ‘dir1′ անունով դիրեկտորիա
mkdir dir1 dir2Ստեղծել 2  դիրեկտորիաներ ‘dir1′ և ‘dir’ անուններով
mkdir -p /tmp/dir1/dir2Ստեղծել դիրեկտորիաների ծառ (tmp-ի մեջ ստեղծվում է dir1-ը, dir1-ի մեջ էլ dir2-ը),
rm -f file1Ջնջում է ‘file1′ անունով ֆայլը (‘-f ’-ը նշանակում է ջնջել անկախ ամեն ինչից կամ force)
rmdir dir1Ջնջում է ‘dir1′ անունով դիրեկտորիան (սա նույնն է ինչ գրես rm –r dir1, ‘-r’-ը նշանակում է ռեկուրսիվ ջնջել dir1 դիրեկտորիան և նրա ողջ պարունակությունը՝ նրա մեջ պարունակվող այլ ֆայլերի և դիրեկտորիաների հետ հանդերձ)
rm -rf dir1Ջնջել dir1 դիրեկտորիան և նրա ողջ պարունակությունը՝ նրա մեջ պարունակվող այլ ֆայլերի և դիրեկտորիաների հետ հանդերձ
rm -rf dir1 dir2Ջնջել dir1 և dir2 դիրեկտորիաները
mv dir1 new_dir mv dir1 /home/ubuntu/Անվանափոխել dir1 –ը new_dir անունովՏեղափոխել dir1 –ը /home/ubuntu/ ճանապարհով (գցում է /home/ubuntu/ դիրեկտորիայի մեջ dir1-ը)
cp file1 file2Copy է անում file1-ը file2 –ի մեջ
cp dir/* .Copy է արվում ընթացիկ դիրեկտորիայում գտնվող dir դիրեկտորիայի ողջ պարունակությունը ընթացիկ դիրեկտորիայիում՝ dir –ից մեկ մակարդակ վերև
ln  /home/cods/file1 /home/ubuntu/Desktop/lnk_file1Սարքում է /home/cods/file1 ճանապարհով file1 –ի սիմվոլային link-ը /home/ubuntu/Desktop/ ճանապարհով  և lnk_file1 անունով,
Ln -s /home/cods/file1 /home/ubuntu/Desktop/lnk_file1Սարքում է /home/cods/file1 ճանապարհով file1 –ի կոշտ link-ը /home/ubuntu/Desktop/ ճանապարհով  և lnk_file1 անունով,
touch file1Ստեղծում է նոր ֆայլ ընթացիկ դիրեկտորիայում
find / -name file1Փնտրում է file1  անունով ֆայլը սկսած root(/) –ից, այսինքն ֆայլային համկարգի ամենավերևից
find /home/user -name “*.bin”Գտնել այն բոլոր ֆայլերը, որոնք վերջանում են .bin ով, փնտրումը սկսաել  /home/user դիրեկտորիայից
find /usr/bin -type f -atime +15Գտնել բոլոր այն ֆայլերը՝ սկսած /usr/bin դիրեկտորիայից, որոնց դիմելու վերջին օրից անցել է ավելի քան 15 օր (-type ֆլագի միջոցով նշում ենք թե ինչ օբյեկտ ենք փնտրում, տվյալ դեպքում f-ը նշանակում է, որ փնտրում ենք միայն ֆայլեր)
find /usr/bin -type f -mtime -15Գտնել բոլոր այն ֆայլերը՝ սկսած /usr/bin դիրեկտորիայից, որոնք ստեղծվել կամ փոփոխվլ են վերջին  15 օրվա ընթացքում(-type ֆլագի միջոցով նշում ենք թե ինչ օբյեկտ ենք փնտրում, տվյալ դեպքում f-ը նշանակում է, որ փնտրում ենք միայն ֆայլեր)
find / -name *.c -exec chmod 755 ‘{}’ \;Գտնել բոլոր այն ֆայլերը, որոնք վերջանում են .c -ով և փոխել դրանց permission –երը,
grep  jmp entry_point”  /home/ubuntu/cods/injects/* -RՌեկուրսիվ կերպով (-R ֆլագ) գտնել՝ սկսած /home/ubuntu/cods/injects/ դիրեկտորիայից, բոլոր այն ֆայլերը, որոնք պարունակում են jmp entry_point տողը
whereis file1Ցույց է տալիս բոլոր այն ֆայլերը, որոնք դիմում են file1 -ին
which file1Ցույց է տալիս file1 տեղակայման բացարձակ ճանապարհը
mount /dev/sda2 /mnt/sda2mount  -է անում (կցում է) կոշտ սկավառակի sda2 տրամաբանական բաժինը /mnt/sda2-ում, աիսինքն /mnt դիրեկտորիայում գտնվող sda2 դիրեկտորիայում,
umount /dev/sda2Ֆայլային համակարգից հեռացնում է (umount) նախկինում կցված sda2 տրամաբանական բաժինը
fuser -km /mnt/hda2Հարկադրական կերպով ֆայլային համակարգից հեռացնում է (umount)  նախկինում կցված sda2 տրամաբանական բաժինը (սա օգտագործվում է, երբ տվյալ բաժինը զբաղված է այլ օգտագործողի կողմից) ,
mount /dev/fd0 /mnt/floppyԿցել  floppy :D :D :D
mount /dev/cdrom /mnt/cdromկցել  CD/DVD
mount -o loop file.iso /mnt/isosկցել (սարել) ISO-պատկեր (image)
df -hՑույց է տալիս mount արված բաժինները
du -sh dir1Ցույց է տալիս dir1 –ի (dir1-ի փոխարեն կարող ենք տալ ցանկացած դիրեկտորիայի ճանապարհ անկախ նրանից, թե այն որտեղ է գտնվում, բավական է միայն տալ դրա բացարձակ ճանապարհը, օրինակ՝ du -sh /home/ubuntu/cods/ )
groupadd  new_groupՍտեղծել նոր խումբ new_group անունով
groupdel new_groupՋնջել new_group անունով խումբը
groupmod -n new_group_name old_group_nameԱնվանափոխել old_group_name անունով խումբը new_group_name նոր անունով
useradd -c -g admin -d /home/user1 -s /bin/bash user1Ստեղծել նոր user-ի, նրան տալ /home/user1 տնային դիրկետորիան,  /bin/bash shell-ը, ներառել նրան admin խմբի մեջ
useradd user1Ստեղծել user1 անունով user,
userdel -r user1Ջնջել user1 անունով user -ին,
passwdՓոխել ծածկագիրը(եթե root ով չենք մտած, ապա գրում ենք sudo passwd)
passwd user1Փոխել  user1 –ի ծածկագիրը(եթե root ով չենք մտած, ապա գրում ենք sudo passwd)