یادداشت های برنامه نویس آماتور | Amateur programmer notes
61 subscribers
92 photos
10 videos
34 files
88 links
اندازه ی آرزو هات مهم نیست
مهم اینه تو چقد میبینیش
آرزو هاتو بزرگ ببین


</create> by <ashkan\>
Download Telegram
#بدون_شرح
تا حالا به این فکر افتادین یه چیزی شبیه
cout
بسازین
(بسازین خو 😄 اینم یه نمونش که اهمیت
operator
ها رو نشون میده)
#include <cstdio>

struct M{
M& operator<<(const char* in)
{
printf("%s",in);
return *this;
}
M& operator<<(int in)
{
printf("%d",in);
return *this;
}
M& operator<<(char in)
{
printf("%c",in);
return *this;
}

}myout;
const char el = '\n';

int main(){
myout<<"Hello! "<<10<<el;
return 0;
}

البته
cout
خیلی گسترده تره اما اینجا هدف چیزه دیگه ای هست😉

شب و روزتون بی باگ
#بیشتر_بدانید
تا حالا فکر کردید چجوری بی نهایت آرگومان داشته باشید؟
دونوعش رو بهتون معرفی میکنم

variadic arguments
بچه ها

بچه ها
variadic arguments
😄
اینم معرفی

یه تابع میخوام باهاش بنویسم که هرتعداد براش عدد بفرستیم جمع کلشون رو برامون برگردونه
#include <iostream>

#include <stdarg.h>

using namespace std;


//! جمع اعدادی که براش میفرستیم رو برمیگردونه

//! count همون تعداد آرگومان هایی که براش میفرستیم

//! ... همون آرگومان بی نهایت

int FOO(int count,...)

{

    int out=0;//خروجی

    va_list args;// باید آرگومان ها رو توی این بریزیم

    va_start(args,count);// آرگومان ها رو توی اون میریزیم

    //count

    // نقطه شروع هست نه تعداد اشتباه نکنید


    for(int i=0;i<count;i++)//این حلقه به تعداد آرگومان ها اجرا میشه

    {

        int a = va_arg(args,int);//متغیر بعدی رو به عنوان عدد صحیح لود میکنیم

        out+=a;// به خروجی اضافه میکنیم

    }


    va_end(args);//تموم شد

    return out;

}


int main(){

   cout<<FOO(6,2,3,2,3,4,5)<<endl;//2+3+2+3+4+5=19

   return 0;

}


جالب بود نه؟
بیایید ازقصد اشتباه کنیم و آرگومان اول که تعداد هست رو اشتباه بفرستیم
FOO(8,2,3,2,3,4,5)

اوه
عجب وضعیت باقالی شد😂

برای اینکه دیگه اشتباه نکنیم با استفاده از یه ماکرو آرگومان ها رو میفرستیم که آرگومان اولش رو خودش برامون حساب کنه
جالبه بهتون بگم اونم با آرگومان های بی نهایت کار میکنه😄
#define CountArgs(...)

اگه
__VA_ARGS__رو جلوش بذاریم تمام آرگومان بی نهایت ها رو عیننا مثل قالب میذاره جلوش
اول یه ماکرو مینویسیم تا تعدادش رو بشماریم

#define CountArgs(...) CountArgs2( (__VA_ARGS__,10,9,8,7,6,5,4,3,2,1) )

#define CountArgs2(args) CountArgs3 args

#define CountArgs3(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,argsc,...) argsc

کد بالا تا حداکثر 10 تا آرگومان رو میتونه بشماره اگه بیشتر میخواهید
بیشتر بنویسید
بیایید تستش کنیم
cout<<CountArgs(1,1,1,1,1)<<endl;//5

خروجی 5 میده
بذارید اول تحلیلش کنیم
هر خط یه مرحله از ماکرو رو نشون میده
اگه نمیدونید ماکرو چیه
چند تا پست بالاتر یه توضیح ساده ازش آماده کردم
CountArgs(1,1,1,1,1)


CountArgs2( (1,1,1,1,10,9,8,7,6,5,4,3,2,1) ) // دوتا پرانتز


CountArgs2(args) CountArgs3 args


CountArgs2(args) CountArgs3 (1,1,1,1,10,9,8,7,6,5,4,3,2,1) //args جابه جا میشه


CountArgs3(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,argsc,...) argsc


CountArgs3(1,1,1,1,1,10,9,8,7,6,5 ,...) argsc  // غیره ش رو ننوشتم دیگه احتیاجی نیست


argc نیتجه ی 5 رو تحویلمون میده

امیدوارم متوجه شده باشید
حالا فقط کافیه یه ماکرو دیگه تعریف کنیم
چیزی که تولید میکنه این باشه
اولش تعداد آرگومان ها رو برامون بشماره با همون ماکرو بالایی
بعد خود آرگومان ها رو دقیق بفرسته
حواستون جمع باشه این ماکرو یی که نوشتم بیشتر از 10 تا نمیونه حساب کنه
شما خودتون میتونید بیشترش کنید

#define MYARGCOUNTER(...) CountArgs(__VA_ARGS__),__VA_ARGS__

حالا کدمون رو تغییر میدیم

cout<<FOO(MYARGCOUNTER(2,3,2,3,4,5))<<endl;

MYARGCOUNTER
کاری که میکنه اینه که
CountArgs(2,3,2,3,4,5) , 2,3,2,3,4,5

این رو برامون میسازه و
CountArgs
هم که میشمره پس میشه
6,2,3,2,3,4,5

که مثل همون کد اولمون میشه

نکته اشکانی😐
CountArgs
به خاطر اینکه در بعضی کامپایلر ها درست کامپایل نمیشه سه بار بازخوانی کردم وگرنه مثلا با 2 بار هم روی جی سی سی عم
ل میکنه
#define CountArgs(...) CountArgs2(__VA_ARGS__,10,9,8,7,6,5,
4
,3,2,1)
#define CountArgs2(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,argsc,..
.) argsc
ولی کد بالا در ویژوال
2012
درست کامپایل نخواهد شد

نکته دو:
تابع بالا با نام
variadic function
و ماکرو بالا با نام
variadic macro
شناخته میشن

نکته سه:
توابعی مثل
printf
نوعی
variadic function
هستن

اینم یه مبحثی بود هزار ماشالا مخ درگیر کن
امیدوارم لذت برده باشید

♥️تک تک ارور های زندگی تون پرپر
#لازمه_بدانید !

لازمه بدانید 😄 که خاصیت استفاده مجدد از کد چیه
خیلی هم لازمه

من در پایین با روش شی گرایی خیلی ابتدایی 😅 این کار رو انجام خواهم داد و با دو روش مختلف ران تایم یا کامپایل تایم اون ها رو لود میکنم

مثالی که میزنم ساده هست
یه استراکت که کارش لود کتاب خانه ریاضی هست
البته نترسید قرار نیست همه چیز داشته باشه😄
برای مثال ما فقط یه تابع برای محاسبه قدر مطلق داره
یه بار با استفاده از کتاب خانه استاندارد سی لود میکنیم و بار دیگه یه تابع که خودمون نوشتیم و مثلا کتابخانه خودمونه (الکی مثلا😅)

یه ماکرو برای کامپایل تایم
#define USING_MY_MATH_LIB

این اگه صفر بود از کتابخانه سی استفاده میکنیم وگرنه اگه 1 بود از کتاب خانه خودمون استفاده میکنیم

#include <iostream>
#include <cmath>
using namespace std;

#define USING_MY_MATH_LIB 0 // الان مثلا میخوایم از کتاب خانه استاندارد استفاده شه

#if USING_MY_MATH_LIB == 1
int myabs(int in) // این همون کتابخونه الکیه هستش
{
cout<<"myabs called!"<<endl;// مطمئن شیم فراخوانی شده
if(in<0)
return -in;
return in;
}
#endif // USING_MY_MATH_LIB

struct themathlib // استراکت بیس برای شی گرایی
{
virtual int Abs(int in)=0;// virtual برای شی گرایی راحت تر شه
};

#if USING_MY_MATH_LIB == 1 // از کتاب خانه خودمون استفاده میکنیم
struct mymathlib: themathlib // یه استراکت فرزند مشتق از بیس کار برای کتاب خانه خودمون
{
int Abs(int in){ return myabs(in); } // همون تابع خودمون رو صدا زدیم
};
#endif // USING_MY_MATH_LIB

#if USING_MY_MATH_LIB == 0// از کتابخانه استاندارد استفاده میکنیم
struct stdmathlib: themathlib
{
int Abs(int in){ return abs(in); }// تابع کتابخانه استاندارد
};
#endif // USING_MY_MATH_LIB

themathlib* intializemathlib()// برای ایجاد کردن یه شی جدید
{
themathlib* out=NULL;// خروجی
#if USING_MY_MATH_LIB ==1
out = new mymathlib; // کتابخانه خودمون
#endif // USING_MY_MATH_LIB

#if USING_MY_MATH_LIB ==0
out = new stdmathlib; // کتاب خانه استاندارد
#endif // USING_MY_MATH_LIB
return out;
}

int main()
{
themathlib* obj = intializemathlib();// یه شی ایجاد میکنیم
int in;
cout<<"Enter Integer:"<<endl;
cin>>in;
cout<<obj->Abs(in)<<endl;// به همین راحتی استفاده میکنیم
delete obj;
return 0;
}



حالا فرض کنیم تصمیمون عوض شد و خواستیم از کتابخانه خودمون استفاده کنیم
چی کار باید کنیم؟
خب خیلی راحته فقط کافیه
#define USING_MY_MATH_LIB 1
باشه

هر دو حالت رو حتما تست کنید

همونطور که میدونید ماکرو ها برای موقع کامپایل هستن و تکلیفشون رو برنامه نویس مشخص میکنه
حالا فرض کنیم کاربر نهایی قراره تکلیف رو مشخص کنه
و به اصطلاح کارها همون موقع اجرا انجام شه
#include <iostream>
#include <cmath>
using namespace std;

int myabs(int in)
{
cout<<"myabs called!"<<endl;
if(in<0)
return -in;
return in;
}

struct themathlib
{
virtual int Abs(int in)=0;
};

struct mymathlib: themathlib
{
int Abs(int in){ return myabs(in); }
};

struct stdmathlib: themathlib
{
int Abs(int in){ return abs(in); }
};

themathlib* intializemathlib(bool usingmylib)// اگه صحیح بود از کتابخانه خودمون استفاده میشه وگرنه از استاندارد
{
themathlib* out=NULL;// خروجی
if(usingmylib)
out = new mymathlib; // کتابخانه خودمون
else
out = new stdmathlib; // کتاب خانه استاندارد
return out;
}

int main()
{
bool type;
int in;
cout<<"Enter the Math library type:\n 0: standard library\n 1: my library\n";// پیام راهنمایی
cin>>type; //نوع کتابخانه
cout<<"Enter Integer:"<<endl;// پیام راهنمایی
cin>>in;//ورودی
themathlib* obj = intializemathlib(type);// ایجاد یک شی
cout<<obj->Abs(in)<<endl;//اجرای تابع
delete obj;
return 0;
}


یه مقایسه سطحی از حالت اول و دوم:
در حالت اول فقط وقتی کلاس ایجاد میشه و کامپایل میشه که استفاده شده باشه ولی حالت دوم اینطور نیست پس مسلما حجم برنامه زیاد خواهد شد و چون شرط ها موقع اجرا استفاده میشن پس سرعت اجرا یه ذره کم میشه
اما در عوض کاربر نهایی تکلیف رو مشخص میکنه و دستش بازتره
در صورتی که تکلیف حالت اول رو فقط برنامه نویس میتونه مشخص کنه

یه چیزی برای اونایی که میخوان یاد بگیرن :
بد نیست خودتون هم یه کتابخانه سوم بنویسید و اضافه کنید تا خوب استفاده مجدد رو بشناسین
1396/5/10
امیدوارم مفید بوده باشه
دنیای بی باگی رو براتون
#بیشتر_بدانید

اینجا میخوام همزمان هم بهتون
static assertion
رو معرفی کنم
هم بهتون بگم چجوری میتونید برای آرگومان های توابعی که از
template
ها استفاده میکنن استثنا تعیین کنید

معرفی:
static assertion
بچه ها

بچه ها
static assertion
😄

کد
#include <iostream>

#include <cmath>

using namespace std;


template <typename T>

T Foo(T in) // یه تابع که عدد میگیره و دوبرابرش رو برمیگردونه

{

    return in *((T)2.0f);

}


int main()

{

    int a = 50;

    cout<<Foo(a)<<endl;

    return 0;

}



حالا بیایید یه مقدار باگ برای خودمون بسازیم
int *a = new int(50);

    cout<<Foo(a)<<endl;



همونطور که دیدیم ارور داد اما برای اینکه ارور رو واضح تر کنیم خودمون پیام ارور رو بسازیم

#include <iostream>

#include <cmath>

#include <type_traits>

using namespace std;


template <typename T>

T Foo(T in)

{

    static_assert(!is_pointer<T>::value,"haaji az pointer estefade nakon!");

    return in *((T)2.0f);

}


int main()

{

    int *a=new int(50);

    cout<<Foo(a)<<endl;

    return 0;

}


وقتی کامپایل کنین ارور میده و میگه :"حاجی . از اشاره گر استفاده نکن!"😄

حالا ارور رو تعمیر میکنیم:
int *a=new int(50);

    cout<<Foo(*a)<<endl;


حالا باز از قصد یه چیز دیگه میفرستیم
مثلا استرینگ
و براش ارور رو هم مینویسیم

#include <iostream>

#include <cmath>

#include <type_traits>

#include <string>

using namespace std;


template <typename T>

T Foo(T in)

{

    static_assert(!is_pointer<T>::value,"haaji az pointer estefade nakon!"); //ارور استفاده از اشاره گر

    static_assert(!is_same<T,string>::value,"haaji az string estefade nakon!");// ارور استفاده از استرینگ

    return in *((T)2.0f);

}


int main()

{

    string a;

    cout<<Foo(a)<<endl;

    return 0;

}



میبینین که پیام ارور میشه: "حاجی از استرینگ استفاده نکن!"😄

اینطوری یه ذره سخت میشه
بیایین به جای اینکه بگیم کدوما ارور باشن
بگیم کدوم ها ارور نباشن

template <typename T>

T Foo(T in)

{

    static_assert(!is_pointer<T>::value,"haaji az pointer estefade nakon!");

    static_assert((is_same<T,int>::value||is_same<T,double>::value||is_same<T,float>::value),

                  "haaji faghat az int | double |float  estefade kon!");

    return in *((T)2.0f);

}


int main()

{

    int a=10;

    cout<<Foo(a)<<endl;

    return 0;

}


کار به همین راحتی بود🙂 حالا هی بگو اشی بده


کدهاتون بی باگ
#بدون_شرح

امروز یه ماکرو که برای پیدا کردن سریع تر محل ارور های ران تایم و دلیلش مینویسم و باهاتون به اشتراک میذارم

یه ماکرو که پیام ارور رو میگیره و به همراه پیام ارور
نام فایل و نام تابع و خط ارور رو هم به همراه ارور مشخص میکنه

برای تست هم یه متغیر قلابی داریم که اگه غلط بود ارور بده

#include <stdexcept>
using namespace std;

#define MYERROR( errormessage ) {char _errormessage[1000];sprintf(_errormessage,"\n\tError!!!\n\tFile=\"%s\"\n\tFunction=\"%s\"\n\tLine=\"%d\"\n\tProblem=\"%s\"",__FILE__,__FUNCTION__,__LINE__,errormessage); throw std::runtime_error(_errormessage);}

int main()
{
bool fakecheck = false;
if(!fakecheck)
MYERROR("fake check is false!");

return 0;
}


کدهاتون بی باگ
#بیشتر_بدانید

تاحالا به این فکر کردید که کلاس استرینگ مخصوص خودتون بسازین؟
بسازین خو

دوستی اومد و بهم گفت از شی گرایی بیشتر بگم
عجیبه اما من درباره شی گرایی زیاد حرف زدم قبلا

اما خب چون گفت منم این فکر به سرم زد

در ضمن من یه آماتور هستم و کلاس کامل تست شده نیست و احتمال ارور دادن یا نشت حافظه توش هست
اگه چیزی بود خبرم کنین درست کنم
کلاس کامنت گذاری شده هستش

#
include <iostream>
#include <cstring>
#include <stdexcept>
using namespace std;

//چون حال ندارم هر بار تایپش کنم یه اسم مستعار براش میسازم
typedef unsigned int uint;

class MyString
{
public:
//سازنده ی بدون آرگومان
MyString()
{
// دیتا هیچوقت نباید خالی باشه چون ما تمام حزف های کلاس رو بدون شرط انجام میشه
data = new char[1];
// همیشه آخرین حرف باید پوچ باشه
data[0]='\0';
}
//سازنده ی با آرگومان
MyString(const char* in )
{
//اشاره گر رو آماده میکنیم
data = new char[strlen(in)+1];
//کپی میکنیم
strcpy(data,in);
// حرف آخرش رو هم پوچ میکنیم
data[strlen(in)]='\0';
}
//سازنده ی کپی
MyString(const MyString& other)
{
data = new char[strlen(other.data)+1];
strcpy(data,other.data);
data[strlen(other.data)]='\0';
}
//مخرب
virtual ~MyString()
{
delete[] data;
}
//موقع سرچ معادل پیدا نشدن هست
static const uint NullPosition = -1;
//دیتا رو بر میگردونه
const char* getdata()const
{
return data;
}
//اندازه رشته رو برمیگردونه
uint getsize()const
{
return strlen(data);
}
// پاک میکنه
void clear()
{
delete[] data;
//حتما باید دیتا حداقل یه خونه داشته باشه تا ارور نده
data=new char[1];
data[0]='\0';
}
// یه تیکه از رشته رو بر میگردونه با استفاده از نقطه شروع و پایانش
MyString substring(uint _start,uint _end=-1)const
{
//اگه پایانش منفی یک بود یعنی پایان رشته البته مقدار منطقیش منفی یک نیست
uint __end = (_end!=-1)?_end:strlen(data)+1;
if(_start>__end)//اگه نقطه شروع از پایان بزرگتر بود ارور بده
throw std::runtime_error("MyString::substr : start point is greater than end point");
MyString out;
delete out.data;
out.data= new char[__end-_start];//مقدار جدید خروجی
for(int i=_start;i<__end;i++)// کپی کردن
out.data[i-_start]=data[i];
out.data[__end-_start]='\0';
return out;
}
// یه رشته رو از داخل خود رشته پیدا میکنه و میتونین مشخص کنین از کجا شروع بشه
uint findstring(const char* in,uint from=0)const
{
uint pos = (uint)strstr(data,in);
if(pos==0)
return -1;
while(pos - (uint )data<from)
{
pos=(uint)strstr(&data[pos - (uint )data+1],in);
if(pos==0)
break;
}
if(pos - (uint )data<from || pos - (uint )data>getsize())
return -1;
return pos - (uint )data;
}
// پیدا کردن با استفاده از نوع خود کلاس
uint findstring(const MyString& in,uint from=0)const
{
return findstring(in.data,from);
}
// پاک کردن یه قسمت از رشته با استفاده از نقطه شروع و پایان
void erase(uint start,uint end=-1)
{
//اگه منفی یک بود یعنی انتهاش
uint _end=(end!=-1)?end:strlen(data);
if(start>getsize()||_end>getsize())
throw std::runtime_error("MyString::erase : start point or end point is greater than data size");
//سایز رشته قبل از تغییر
uint strsize=getsize();
//کپی کردن کاراکتر های بعد از قسمتی که باید پاک بشه
for(uint i=start;i<strsize;i++)
{
//یعنی توانایی کپی رو داره
if(i+(_end-start)<strsize)
{
// رشته جلویی رو میاره میذاره جایی که باید پاک شه
data[i]=data[i+(_end-start)];
//پوچ میشه تا باقی نمونه
data[i+(_end-start)]='\0';
}
else
// قابل کپی نیست و باید پوچ شه
data[i]='\0';
}
#بیشتر_بدانید

تاحالا به این فکر کردید که کلاس استرینگ مخصوص خودتون بسازین؟
بسازین خو

دوستی اومد و بهم گفت از شی گرایی بیشتر بگم
عجیبه اما من درباره شی گرایی زیاد حرف زدم قبلا

اما خب چون گفت منم این فکر به سرم زد

در ضمن من یه آماتور هستم و کلاس کامل تست شده نیست و احتمال ارور دادن یا نشت حافظه توش هست
اگه چیزی بود خبرم کنین درست کنم
کلاس کامنت گذاری شده هستش

#
include <iostream>
#include <cstring>
#include <stdexcept>
using namespace std;

//چون حال ندارم هر بار تایپش کنم یه اسم مستعار براش میسازم
typedef unsigned int uint;

class MyString
{
public:
//سازنده ی بدون آرگومان
MyString()
{
// دیتا هیچوقت نباید خالی باشه چون ما تمام حزف های کلاس رو بدون شرط انجام میشه
data = new char[1];
// همیشه آخرین حرف باید پوچ باشه
data[0]='\0';
}
//سازنده ی با آرگومان
MyString(const char* in )
{
//اشاره گر رو آماده میکنیم
data = new char[strlen(in)+1];
//کپی میکنیم
strcpy(data,in);
// حرف آخرش رو هم پوچ میکنیم
data[strlen(in)]='\0';
}
//سازنده ی کپی
MyString(const MyString& other)
{
data = new char[strlen(other.data)+1];
strcpy(data,other.data);
data[strlen(other.data)]='\0';
}
//مخرب
virtual ~MyString()
{
delete[] data;
}
//موقع سرچ معادل پیدا نشدن هست
static const uint NullPosition = -1;
//دیتا رو بر میگردونه
const char* getdata()const
{
return data;
}
//اندازه رشته رو برمیگردونه
uint getsize()const
{
return strlen(data);
}
// پاک میکنه
void clear()
{
delete[] data;
//حتما باید دیتا حداقل یه خونه داشته باشه تا ارور نده
data=new char[1];
data[0]='\0';
}
// یه تیکه از رشته رو بر میگردونه با استفاده از نقطه شروع و پایانش
MyString substring(uint _start,uint _end=-1)const
{
//اگه پایانش منفی یک بود یعنی پایان رشته البته مقدار منطقیش منفی یک نیست
uint __end = (_end!=-1)?_end:strlen(data)+1;
if(_start>__end)//اگه نقطه شروع از پایان بزرگتر بود ارور بده
throw std::runtime_error("MyString::substr : start point is greater than end point");
MyString out;
delete out.data;
out.data= new char[__end-_start];//مقدار جدید خروجی
for(int i=_start;i<__end;i++)// کپی کردن
out.data[i-_start]=data[i];
out.data[__end-_start]='\0';
return out;
}
// یه رشته رو از داخل خود رشته پیدا میکنه و میتونین مشخص کنین از کجا شروع بشه
uint findstring(const char* in,uint from=0)const
{
uint pos = (uint)strstr(data,in);
if(pos==0)
return -1;
while(pos - (uint )data<from)
{
pos=(uint)strstr(&data[pos - (uint )data+1],in);
if(pos==0)
break;
}
if(pos - (uint )data<from || pos - (uint )data>getsize())
return -1;
return pos - (uint )data;
}
// پیدا کردن با استفاده از نوع خود کلاس
uint findstring(const MyString& in,uint from=0)const
{
return findstring(in.data,from);
}
// پاک کردن یه قسمت از رشته با استفاده از نقطه شروع و پایان
void erase(uint start,uint end=-1)
{
//اگه منفی یک بود یعنی انتهاش
uint _end=(end!=-1)?end:strlen(data);
if(start>getsize()||_end>getsize())
throw std::runtime_error("MyString::erase : start point or end point is greater than data size");
//سایز رشته قبل از تغییر
uint strsize=getsize();
//کپی کردن کاراکتر های بعد از قسمتی که باید پاک بشه
for(uint i=start;i<strsize;i++)
{
//یعنی توانایی کپی رو داره
if(i+(_end-start)<strsize)
{
// رشته جلویی رو میاره میذاره جایی که باید پاک شه
data[i]=data[i+(_end-start)];
//پوچ میشه تا باقی نمونه
data[i+(_end-start)]='\0';
}
else
// قابل کپی نیست و باید پوچ شه
data[i]='\0';
}
نوشتن اولین برنامه 😄

#سی_کد
📁📄📁📄📁

حال میخواهیم اولین برنامه خود را به زبان سی و در محیط کدبلاکس بنویسیم. برای این کار از منوی File و سپس زیر منوی New گزینهٔ Empty file را انتخاب میکنیم.

در برگهٔ بازشده عبارات زیر (که در واقع یک برنامه است که عبارت «Hello World» را چاپ میکند) را عینا تایپ میکنیم.

اصلا عجیب نیست اگر هیچ یک از عبارات زیر را متوجه نشوید! به زودی به طور کامل به معرفی تک‌تک این‌ها می‌پردازیم.

// written by CProg channel
#include "stdio.h"
int main ()
{
printf("Hello World\n");
return 0;
}

توجه کنید که در تمام طول برنامه نویسی می‌توانیم با فشردن کلید های Ctrl و S به طور همزمان، برنامه را ذخیره کنیم. چنانچه برنامه ای را برای اولین بار بخواهیم ذخیره کنیم نام برنامه (یا به عبارت درست تر، نام‌ فایل) و مسیر ذخیره‌ٔ آن پرسیده می شود.

در این مثال، برنامه را با نام first.c ذخیره کردیم.
پس از ذخیره برنامه با فشردن دکمه F9 برنامه کامپایل می‌شود و خروجی اولین برنامه‌مان که نمایش پیغام «Hello World» است را میتوانیم ببینیم.
💻 توضیحات (comment) در برنامه نویسی

#سی_کد
📁📄📁📄📁

در زبان برنامه نویسی C برای نوشتن توضیحات یک خطی در خصوص کل برنامه یا یک بخش خاص از آن از علامت // استفاده می‌کنیم. چنانچه توضیحات ما بیشتر از یک خط باشد آن‌را با علامت */ شروع و با علامت /* پایان می‌دهیم.

کامپایلر از توضیحات صرف نظر می‌کند و در واقع نوشتن توضیحات در روند اجرای برنامه تاثیری ندارد اما نوشتن توضیحات کامل و مناسب باعث می‌شود که برنامه شما خواناتر باشد و دیگران بتوانند بهتر آن‌را درک کنند.

در مثال زیر از توضیحات چندخطی و تک خطی استفاده شده است.

#include "stdio.h"

/*
Code by:
CPROG Channel in telegram.
*/

int main ()
{

printf("Hello World\n");
return 0;
} // end of code
⌨⌨⌨⌨⌨
💻 دستورات پیش پردازنده (PreProcessor)

#سی_کد
📁📄📁📄📁

دستوراتی که با علامت # شروع شود، یک دستور C نیستند و جزو دستورات پیش پردازنده (PreProcessor) محسوب می‌شوند.

دستورات پیش پردازنده دستورهایی هستند که توسط کامپایلر، قبل از شروع به کامپایل انجام می‌شوند. به‌عنوان مثال دستور #include باعث می‌شود که تعاریف اولیهٔ مربوط به توابعی (زیربرنامه‌هایی) که قصد استفاده از آنها را داریم به برنامه اضافه شود.

در برنامه Hello World که پیشتر با هم نوشتیم، برای استفاده از تابع printf، فایل سرآمد (هدر) stdio.h که این تابع در آن تعریف شده‌است را شامل برنامه کرده‌ایم.
⌨⌨⌨⌨⌨
💻 تابع اصلی (main)

#سی_کد
📁📄📁📄📁

هر برنامه C باید دارای تابعی به نام main باشد که اجرای برنامه از آن شروع می‌شود و در حقیقت آن تابع برنامهٔ اصلی است.

همچنین می‌توان هر تعداد دیگری تابع (زیربرنامه) نیز تعریف کرد، اما وجود تابع main الزامی است. دقت کنید که به دلیل این که این تابع پارامتر ورودی ندارد، از یک جفت پرانتز (پرانتز باز و بسته) استفاده کرده ایم.

برای تعریف تابع باید جنس مقدار برگشتی تابع را مشخص کنیم. در مثال فوق تابع main را با مقدار برگشتی عدد صحیح (int) تعریف کرده‌ایم. به این معنی که خروجی این تابع می‌تواند یک عدد صحیح باشد و می‌تواند به بقیه توابع برنامه (فعلا تابع دیگری نداریم) ارسال شود.

و در آخر، قبل از اتمام تابع main ( { ) مقدار بازگشتی تابع با عبارت «;return 0» تعریف شده است.

از مقدار بازگشتی توابع استفاده هایی می‌شود. به عنوان مثال در برنامهٔ فوق بازگرداندن عدد صفر به معنی صحیح اجرا شدن برنامه (چاپ عبارت Hello World) است.
⌨⌨⌨⌨
💻 دو نکته مهم

#سی_کد
📁📄📁📄📁

در زبان C هر بلوک برنامه با علامت } آغاز شده و با { پایان می‌یابد. در مثال فوق تابع main یک بلوک می باشد و به همین شکل پیاده سازی شده است.

دقت کنید که در پایان هر دستورالعمل از علامت ; (SemiColon) استفاده شده است.

✍ Mr.ashkan
#ادامه_دارد...
💻 توضیحات (comment) در برنامه نویسی

#سی_کد
📁📄📁📄📁

در زبان برنامه نویسی C برای نوشتن توضیحات یک خطی در خصوص کل برنامه یا یک بخش خاص از آن از علامت // استفاده می‌کنیم. چنانچه توضیحات ما بیشتر از یک خط باشد آن‌را با علامت */ شروع و با علامت /* پایان می‌دهیم.

کامپایلر از توضیحات صرف نظر می‌کند و در واقع نوشتن توضیحات در روند اجرای برنامه تاثیری ندارد اما نوشتن توضیحات کامل و مناسب باعث می‌شود که برنامه شما خواناتر باشد و دیگران بتوانند بهتر آن‌را درک کنند.

در مثال زیر از توضیحات چندخطی و تک خطی استفاده شده است.

#include "stdio.h"

/*
Code by:
CPROG Channel in telegram.
*/

int main ()
{

printf("Hello World\n");
return 0;
} // end of code
#بیشتر_بدانید

اینبار یه چیزی مینویسم تا کار سریالیز کردن کلاس ها و استراکت ها رو راحت تر کنه

#include <iostream>
#include <sstream>
#include <fstream>
#include <streambuf>
#include <string>
using namespace std;

//برای اینکه چک کنیم شی تابع سریالیز داره
// مبحث پست بالا همین بود
template<typename T,typename Sign>
struct hasserialize
{
template <typename U,U> struct AAA;\
template <typename A> static char Check(AAA<Sign,&A::serialize >* in);
template <typename A> static int Check(...);\
const static bool value = sizeof(Check<T>(0))==sizeof(char);
};


// برای نوع ورودی
struct BinaryIn
{
//لود کردن از فایل
void load(const char* filename)
{
fstream inf;
inf.open(filename,ios::in|ios::binary);
str<<inf.rdbuf();
inf.close();
}

//این برای موارد کلی هست
template<typename T>
void operator() (T& in)
{
// ارور برای وقتی که تابعه وجود نداره
static
assert(hasserialize<T,void (T::*)(BinaryIn&)>::value,"there are no member func Typename::serialize(BinaryIn&) to serialize object" );
in.serialize(*this);
}

stringstream str;
};

// از اینجا به بعد برای استثنا ها یا آن هایی که باید بیرون از کلاس سریالیزرشون تعریف شده باشه
// من یه چندتایی برای تست آماده کردم
template<>
void BinaryIn::operator() <int>(int& in)
{
str.read(reinterpretcast<char>(&in),sizeof(in));
}

template<>
void BinaryIn::operator() <double>(double& in)
{
str.read(reinterpret_cast<char>(&in),sizeof(in));
}

template<>
void BinaryIn::operator() <float>(float& in)
{
str.read(reinterpretcast<char*>(&in),sizeof(in));
}

// استرینگ خودش نوعی کلاسه اما چون استثنا هست این شکلی امکان لود بهش میدیم
template<>
void BinaryIn::operator() <string>(string& in)
{
size
t ss;
str.read(reinterpretcast<char>(&_ss),sizeof(__ss));
in.reserve(__ss);
char
ins = new char[ss];
str.read(ins,ss);
in = ins;
delete
ins;
}

// برای خروجی
struct BinaryOut
{
// ذخیره کردن در یه فایل
void save(const char filename)
{
fstream onf;
onf.open(filename,ios::out|ios::binary);
onf<<str.rdbuf();
onf.close();
}

template<typename T>
void operator() (T& in)
{
static_assert(has_serialize<T,void (T::
)(BinaryOut&)>::value,"there are no member func Typename::serialize(BinaryOut&) to serialize object" );
in.serialize(this);
}

stringstream str;
};
//استثنا ها
template<>
void BinaryOut::operator() <int>(int& in)
{
str.write(reinterpret_cast<char
>(&in),sizeof(in));
}

template<>
void BinaryOut::operator() <double>(double& in)
{
str.write(reinterpretcast<char*>(&in),sizeof(in));
}

template<>
void BinaryOut::operator() <float>(float& in)
{
str.write(reinterpret
cast<char>(&in),sizeof(in));
}

template<>
void BinaryOut::operator() <string>(string& in)
{
size_t _s = in.size();
str.write(reinterpret_cast<const char
>(&s),sizeof(in.size()));
str.write(in.c
str(),s);
}


// از اینجا به بعد یه مثال برای استفاده ازش هست
struct struct2
{
template <typename T>
void serialize(T& ar)
{
ar(a);
}

int a;
};

struct struct1
{
template <typename T>
void serialize(T& ar)
{
ar(a);
ar(b);
ar(c);
ar(d);
ar(e);
}

int a;
float b;
double c;
struct2 d;
string e;
};

int main()
{
struct1 a,b;
a.a=50;
a.b=80.0f;
a.c=410.0;
a.d.a=880;
a.e="Hello World!";
b.a=0;
b.b=0.0f;
b.c=0.0;
b.d.a=0;
b.e.clear();

BinaryOut tout;
tout(a);
tout.save("out.txt");
BinaryIn tin;
tin.load("out.txt");
tin(b);

cout<<b.a<<endl<<b.b<<endl<<b.c<<endl<<b.d.a<<endl<<b.e<<endl;
return 0;
}

تو مثال بالا محتویات
a
رو توی فایلی ذخیره میکنیم و
b
رو از روی همون فایل لود میکنیم
آخر کار هم تک تک محتویات
b
رو چاپ میکنیم تا خیالمون راحت شه که همه چیز ردیفه

محتویات و سایز
out.txt
رو بررسی کنید تا ببینید چه خبره
امیدوارم براتون جالب بوده باشه
@Simpleprogramming
#بیشتر_بدانید

من قبلا
variadic function
و
variadic macro
رو معرفی کرده بودن.

گفتم این
variadic template
گناه داره😭
اونم معرفی کنم😊

variadic template
بچه ها
—------------------------------
بچه ها
variadic template

تو برنامه پایین چند تا آرگومان به یه تابع میفرستم و سومیش رو چاپ میکنم😥😥😥😥
پس با این حساب بهتره بگیم
# خودتان_میدانید !!!

#include <iostream>
#include <tuple>
using namespace std;

template<typename... T>
void foo(T... in)
{
cout<<get<2>(make_tuple(in...))<<endl;
}

int main()
{
foo("hello",47.5,77,55,44);
return 0;
}


همه باگهاتون دیباگ♥️

@Simpleprogramming