زبان برنامه نویسی سی/تبدیل و جایگزینی داده‌ها: تفاوت میان نسخه‌ها

محتوای حذف‌شده محتوای افزوده‌شده
جزبدون خلاصۀ ویرایش
بدون خلاصۀ ویرایش
خط ۶:
تبدیل داده (type conversion) در دانش برنامه‌نویسی که در زبان سی با مفهومی به نام ایفای نقش داده (type casting) وجود دارد ، به رفتار کردن کامپایلر با نوعی از داده به صورت نوع دیگری از داده می‌گویند که به صورت علنی و غیر علنی می‌باشد که در ادامه به آنها می‌پردازیم و در انتهای مبحث به رویدادی که برای داده‌ها طی تبدیل آنها رخ می‌دهد مطابق با استاندارد که ناقص است و بیشتر دل‌به‌خواه عرضه‌کننده کامپایلر می‌باشد می‌پردازیم ( استاندارد C تمام تبدیل‌ها را تعریف نکرده است ؛ اما بیشتر نویسنده‌های کامپایلرهای مطرح از روش یکسانی استفاده می‌کنند )
 
'''۱ - غیر علنی :''' در حالت غیر علنی ( implicit ) شما برنامه‌ای را می‌نویسید که عملوندهای عملگرها ( عملگری مثل عملگر جمع که عملیات جمع بر روی عملوندها را انجام می‌دهد و عملوند به داده‌ای می‌گویند که عملگر بر روی آنها عمل محاسباتی ، منطقی یا ... را انجام می‌دهد ) از نوع داده‌های متفاوتی هستند یا داده‌ای را از نوع داده‌ای دیگر جز پارامتر تابع ، به عنوان آرگومان به آن تابع ارجاع می‌دهید ( پاس می‌دهید ) . مثلاً یک نوع داده از نوع صحیح را با یک نوع داده از نوع اعشاری ، جمع می‌بندید و سپس آن را در یک نوع داده کاراکتری ذخیره می‌کنید یا یک نوع داده صحیح را به عنوان آرگومان به یک تابعی ارجاع می‌دهید که پارامتر متناظر با آن آرگومان ، در تابع به صورت کاراکتر تعریف شده است ( مثلاً اولین پارامتر تابع به صورت پارامتر کاراکتر تعریف شده است ؛ اما شما موقع احضار تابع ، به تابع یک نوع داده صحیح را نسبتارجاع می‌دهید )
 
در اینجا کامپایلر به صورت خودکار داده‌ها را به یک دیگر تبدیل می‌کند (implicit) که تا جای ممکن با یکدیگر سازگار باشند یا مطابق با دستور شما باشد ( اگر امکان پذیر باشد )
خط ۳۱:
خروجی این برنامه ، عدد 5 خواهد بود . در انتهای همین مبحث ، پیش از بحث typedef به صورت کامل خواهیم نوشت که کامپایلر چگونه انواع داده‌ها را به یکدیگر تبدیل می‌کند . اما به صورت ساده در مثال بالا داده اعشاری را که عدد پی ( پای ) را در خود ذخیره کرده بود ، با داده اعشاری با دقت دوبرابر که عدد نپر ( همان اویلر ) را در خود ذخیره کرده بود جمع کردیم و نتیجه را داخل یک نوع داده صحیح گذاشتیم که داده صحیح خروجی تابع است و در خروجی خط‌دستوری نمایش داده می‌شود . به زبان ساده ؛ نوع داده صحیح نمی‌تواند قسمت‌های اعشاری در خود ذخیره کند ؛ پس آن را نادیده می‌گیرد . این یکی از کاربردهای تبدیل داده‌ها در قسمت انواع صحیح و اعشاری است که قسمت اعشاری را حذف کنیم
 
'''۱ - علنی :''' در نقش دادن علنی ( explicit ) که از این پس با اصطلاح انگلیسی آن یعنی '''کست کردن''' از آن یاد می‌کنیم به صورت صریح (explicit) به کامپایلر دستور می‌دهیم تا با داده ما به عنوان نوع داده دیگری رفتار کند . در این نوع کست کردن که به دستور ما انجام می‌شود ؛ باید شناسه داده‌ای که می‌خواهیم با آن به عنوان نوع داده‌ای دیگر رفتار شود در مقابل یک جفت پرانتز باز و بسته می‌نویسمبنویسیم و در داخل جفت پرانتز خود نوع داده‌ای که قرار است شناسه ما نقش آن را ایفا کند می‌نویسیمقرار می‌دهیم . مثلاً char c را داریم و میخواهیم از آن به عنوان یک عدد استفاده کنیم ؛ پس می‌نویسیم :
 
{{چپ‌چین}}
خط ۳۹:
در زبان C بسیاری از داده‌ها توسط کامپایلر قابل تبدیل به یکدیگر نیستند اما برخی را می‌توان به یکدیگر تبدیل کرد ؛ مثل مغیرهای پایه ؛ که ما با روش کست کردن علنی به صورت صریح ( explicit ) به کامپایلر دستور می‌دهیم تا نوع داده مورد نظر ما را به داده‌ای که طی کست کردن تعیین نموده‌ایم تبدیل کند . البته در کامپایلرهای جدید که با استاندارد C11 یا C18 سازگار هستند تمام تبدیل‌ها به صورت خودکار قابل انجام است ؛ اما اگر از کامپایلر قدیمی استفاده می‌کنید یا کامپایلر شما نمی‌تواند تبدیل‌های مجاز را خودکار انجام دهد و به شما خطایی مشابه با casting expected می‌دهد ؛ داده خود را کست کنید . عواقب کست کردن و تبدیل داده به عهده ماست . بنابراین باید دقت کنید که وقتی می‌خواهید تبدیلی را انجام بدهید ؛ پس از تبدیل ، داده‌ای از دست نرود یا زائده‌ای به وجود نیاید و هر خطایی که در برنامه رخ بدهد از دید بسیاری از کامپایلرها پنهان خواهد ماند ( به شما اخطاری داده نمی‌شود ) و این خود شما هستید که پس از اجرای برنامه و دیدن نقص در اجرای برنامه خود باید پیدا کنید که کجا کد خطایی نوشته‌اید ؛ این مسئله در بخش‌های بسیاری از برنامه امکان پذیر است که یکی از آن بخش‌ها همین مبحث کست کردن می‌باشد . مثلاً یک اشاره‌گر کاراکتری را با نوع اشاره‌گر صحیح عوض می‌کنید و این باعث می‌شود تا زمان اشاره کردن به فضای بزرگ‌تری از حافظه اشاره شود و داده‌های زائد در نتیجه شما لحاظ شوند . در انتهای همین مبحث می‌پردازیم به اینکه طی تبدیل داده‌ها چه رخدادی به وقوع می‌پیوندد تا بهتر تصمیم بگیرید که در کجای برنامه می‌توانید داده خود را کست کنید
 
نتیجه کست کردن علنی و غیر علنی در تبدیل داده‌ها به یکدیگر در برخی کامپایلرها متفاوت است ؛ برای کسب اطمینان به راهنمای کامپایلر خود مراجعه کنید . برای همین ، زمانی که می‌خواهید داده شما به دلخواه شما ( و متفاوت با آنچه که کامپایلر می‌تواند خودکار تبدیل کند و می‌کند ) تبدیل شود ؛ می‌توانید داده‌های خود را کست کنید . برای این کار نوع داده خود را داخل یک جفت پرانتز باز و بسته می‌نویسیدو بنویسید
 
مثال :
خط ۷۹:
{{پایان چپ‌چین}}
 
با اعلام هشدار کامپایلر نظیر : warning: format ‘%d’ expects argument of type ‘int’, but argument 3 has type ‘double’ رو به رو خواهید شد ( در کامپایلر جی‌سی‌سی و کلنگ ) در کامپایلرهای دیگر نیز اخطاری نظیر همین اعلام هشدار مواجه خواهید شد . که در واقع به شما می‌گوید آرگومان‌های تابع printf منتظر یک مقدار اعشاری با دقت دو برابر و یک صحیح است ( مطابق با کد ) اما در متغیرهای نسبت داده شده ، متغیر دوم از نوع اعشاری است که با کست کردن آن مشکل برطرف می‌شود ؛ دقت کنید که در صورت دادن هشدار نیز برنامه ، کامپایل شده اما مقداری که برای آرگومان دوم برمی‌گرداند چیزی شبیه : 988039304 است که در واقع طی تبدیل ناصحیح متغیر اعشاری در حافظه موقت به مقدار صحیح حاصل شده است
 
در مثال بعدی می‌بینید که کست کردن نا‌به‌جا نیز عواقبی در پی دارد :
خط ۱۱۷:
{{پایان چپ‌چین}}
 
در متغیر صحیح i مقدار ۵۶۸۴5684 را ذخیره کرده‌ایم ؛ در احضار تابع printf آرگومان را عددی و از نوع صحیح تعیین کرده‌ایم که البته متغیر i را به آن نسبتارجاع داده‌ایم ؛ اما به کامپایلر دستور داده‌ایم تا در برنامه خروجی با متغیر i مثل کاراکتر رفتار کند و یک متغیر کاراکتری نهایتاً می‌تواند تا عدد ۲۵۵ را در خود ذخیره کند که در صورت کامپایل و اجرای این قطعه کد خواهید که بخش بزرگی از عدد ما از دست می‌رود و خروجی ما 52 خواهد بود ؛ در ادامه ، چگونگی تبدیل داده‌ها را خواهیم نوشت تا بهتر بتوانید تصمیم بگیرید که کجا مجاز هستید تا کست کنید و کجا مناسب است تا کست کنید و اگر اختلالی پیش آمد بدانید که به لحاظ تکنیکی در این تبدیل چه رخ داده است ( که به شما کمک می‌کند تا در صورت وجود مشکل در برنامه‌تان ، کدهای نوشته شما توسط شما که باعث ایجاد خطا شده را راحت‌تر پیدا کیند )
 
اشاره‌گرها را تنها می‌توان به اشاره‌گر کست کرد ، طبق استاندارد کست کردن یک غیر اشاره‌گر به اشاره‌گر غیر مجاز نیست ، اما این کار علاوه بر بیهوده بودن می‌تواند باعث ایجاد خطا در برنامه شما شود ( بدین شکل که آدرس حافظه را به جای محتوای آن بازگرداند ) ، کست کردن اشاره‌گر به عنوان غیر اشاره‌گر نیز غیر مجاز نیست اما باعث ایجاد خطا می‌شود و داده کست شده آدرس حافظه را به جای مقدار مورد اشاره باز می‌گرداند . کست کردن به عنوان اشاره‌گر با قرار دادن داده‌ای که قرار است کست شود در داخل جفت پرانتز باز و بسته و یک علامت استریسک ( ستاره ) پس از نوشتن نوع داده انجام می‌شود و اگر بخواهید آن را غیر مستقیم کنید تا مقدار داده‌ای را که اشاره‌گر به آن اشاره نموده ، بازگرداند یک استریسک دیگر ، پیش از جفت پرانتز قرار می‌دهید
خط ۱۳۷:
{{پایان چپ‌چین}}
 
در قطعه کد بالا یک متغیر از نوع صحیح بلند با نام ln مقدار ۵۱۴۲۲۸۹۳۵514228935 را در خود ذخیره می‌کند ، سپس یک متغیر اشاره‌گر از نوع صحیح بلند با نام lnptr به آن اشاره می‌کند . در انتها در تابع کتابخانه‌ای printf آرگومان خود را از نوع صحیح معمولی تعیین می‌کنیم و به آن اشاره‌گر lnptr را که به عنوان اشاره‌گر صحیح معمولی کست کرده‌ایم که آن را غیر مستقیم نیز نموده‌ایم تا مقدار اشاره شده را باز گرداند ، نسبتارجاع داده‌ایم . در سیستم‌های ۶۴ بیتی ، قطعه کد بالا بدون هیچ ایرادی کامپایل شده ، اجرا می‌شود و خروجی درست را نمایش می‌دهد ؛ اما از آنجایی که در سیستم‌های ۳۲ بیتی هر int فقط ۲ بایت است ، مقدار ذخیره شده در ln قابل ذخیره در صحیح معمولی نیست ؛ برای همین با اعداد دیگری مواجه خواهید شد ! به همین ترتیب اگر مقدار خود را در همین قطعه کد بالا و در سیستم ۶۴ بیتی خودمان به ۵۱۴۲۲۸۹۳۵۶۲۱۵5142289356215 تغییر دهیم ، با اعداد و مقادیر دیگری رو به رو خواهیم شد . بنابراین در کست کردن اشاره‌گرها نیز باید دقت کنید که اگر نوع داده کست شده ، کوچک‌تر از نوع داده اصلی باشد و مقداری که در آن ذخیره شده در نوع داده‌ای که می‌خواهیم کست کنیم جا نشود یا مقادیری از دست می‌روند یا با آدرس‌ها و مقادیر اشتباهی و زباله رو به رو خواهیم شد . از سویی دیگر ، اگر نوع داده اصلی از نوع داده کست شده ، کوچک‌تر باشد ( یعنی نوع داده کست شده بزرگ‌تر باشد ) اشاره‌گر به خانه‌هایی دیگری اشاره خواهد کرد و باز با مقادیر زباله رو به رو خواهیم شد
 
اگر مبحث نوع داده پوچ را به یاد داشته باشید ، گفتیم که اشاره‌گر پوچ نمی‌تواند غیر مستقیم شود مگر آنکه کست شود ( نقش نوع داده دیگری که جز نوع پوچ است را ایفا کند ) . اشاره‌گرهای پوچ درست مثل اشاره‌گرهای معمولی کست می‌شوند و قوانینی که در مورد کست کردن اشاره‌گرها گفته شد در مورد اشاره‌گرهای پوچ (void pointers) نیز صادق است . سعی کنید همواره اندازه نوع داده‌ای که اشاره‌گر پوچ به آن اشاره‌کرده با اندازه داده‌ای که قرار است کست شود ، برابر باشد تا نه فضای اضافی اشغال شود و یا خطایی ایجاد شود و نه مقداری از دست برود
خط ۱۵۷:
{{پایان چپ‌چین}}
 
در مثال بالا متغیری از نوع داده صحیح بلند با نام l با مقدار ۵۸۹۴۴۷۶۵۲۳5894476523 تعریف شده است . اشاره‌گری از نوع داده پوچ با نام vptr به آن اشاره کرده است و در نهایت در تابع printf آرگومانی از نوع صحیح بلند تعیین نموده و اشاره‌گر پوچ vptr را ضمن کست کردن به نوع داده اشاره‌گر صحیح بلند که غیر مستقیم نیز شده است به آن نسبت داده‌ایم
 
'''تابع‌ها'''
 
هنوز به مبحث تایع نرسیده‌ایم اما با این حال چند بار در طول همین سه فصل به صورت مختصر نوشته‌ایم که تابع ، در زبان C داده‌ای است که داده‌های دیگری را می‌تواند به عنوان پارامتر داخل جفت پرانتزی که در مقابل نام تابع قرار می‌گیرند تعریف کند تا سپس بعد از احضار شدن و نسبت داده شدن داده‌هایی که به عنوان آرگومان تعریف می‌شوند و به ترتیب پارامترهای تابع هستند روی آن آرگومان‌ها ، درست هماطور که در تابع برای پارامترهایش تعریف شده ، پردازش انجام دهد و برای این پردازش از ماکروها ، تابع‌های کتابخانه‌ای ، دستورها و عملگرها استفاده می‌کنیم ( ضمن اینکه تابع می‌تواند هیچ پارامتری نداشته باشد که در نتیجه آرگومانی هم نمی‌تواند بپذیرد )
 
با این مقدمه ، می‌خواهیم به این مطلب نیز اشاره کنیم که تابع‌ها خود و پارامتر‌هایشان می‌توانند به عنوان اشاره‌گرِ تابع تعریف شوند تا تابع اشاره‌گر به تابع دیگری اشاره کند و یا پارامتر اشاره‌گر ، به تابع اشاره‌گر دیگری اشاره کند و موقع احضار تابعمان ، که پارامتر اشاره‌گری دارد ، یک اشاره‌گر را با کمک عملگر آدرس به تابع خود نسبت دهیم . اما این کار از طریق عملی مشابه با کست کردن امکان‌پذیر است ؛ این کار کست کردن تابع نیست ، اما درست مشابه کست کردن انجام می‌شود
خط ۲۲۰:
یک نکته را پیش از بررسی چگونگی تبدیل داده‌ها بگوئیم ؛ آن هم اینکه در صورتی که مشخص نکرده باشید که متغیر شما علامت‌دار است یا بدون علامت ، معمولاً توسط کامپایلر به صورت پیش فرض علامت‌دار است ( که در بحث متغیرها هم ذکر شده است ) اما اگر مقداری بزرگ‌تر از آنچه که در متغیر علامت‌دار جا می‌شود ، ذخیره کنید ، بیشتر کامپایلرهای مطرح به صورت خودکار آن را بدون علامت در نظر می‌گیرند . گرچه طبق استاندارد ، شما موظف به تعیین کردن علامت‌دار بودن یا بدون علامت بودن تمام متغیرهای خود هستید ( که در مورد بدون علامت‌ها ، در صورتی است که مقداری بزرگ‌تر از آنچه که در علامت‌دار جا می‌شود را می‌خواهید ذخیره کنید )
 
اولین نحوه تبدیل که استاندارد است این است که اگر چند متغیر را با نوع داده‌های مختلف با یکدیگر در تعامل بگیرید و به وسیله عملگرها آنآنها را ها مورد پردازش ریاضی یا منطقی قرار دهید ؛ متغیرهای شما همگی به نوع داده‌ای تبدیل می‌شوند که در بین متغیرهای شما بزرگ‌ترین نوع داده را دارد که طبیعتاً هیچ یک از مقادیر متغیرهای شما از دست نمی‌رود اما فضای اضافی و زائدی ایجاد می‌شود . دوم اینکه طبق استاندارد کامپایلر باید تمام تلاش خود را بکند تا به نحوی تبدیل انجام شود که مقدار و موجودی در داده نهایی ( نتیجه ) از دست نرود ( اما زمانی که امکان‌پذیر نباشد یا دستور خود شما باشد ، مطمئناً امکان از دست رفتن مقادیر وجود دارد ) . استاندارد برای اینکه اگر یک علامت‌دار و بدون علامت را مثلاً جمع ببندیم و نوع متغیر نتیجه را معین نکنیم ، تعریفی ندارد که باید نتیجه علامت‌دار باشد یا بدون علامت . اما اکثر کامپایلرها در صورتی که امکان حفظ علامت در نتیجه باشد ، نتیجه را علامت‌دار می‌کنند ( اگر نوع داده خروجی و نتیجه را unsigned معین کنیم ، کامپایلر علامت را نادیده می‌گیرد اما اگر خروجی و نتیجه مثلاً int باشد و ما یک علامت‌دار و یک بدون علامت را از هم کسر کنیم ، کامپایلر در ایفای نقش غیر علنی ، علامت را در خروجی منظور می‌کند و int ما را به عنوان signed int در نظر می‌گیرد )
 
در تبدیل متغیرهای غیر اعشاری ( مثل int و long ) به متغیرهای اعشاری ( مثل float و double ) ، یک بخش اعشاری درست به همان اندازه‌ای که بزرگ‌ترین نوع اعشاری دارد ( در قسمت اعشار خود ) ، به همه آنها اضافه خواهد شد . در تبدیل انواع داده‌های اعشاری به داده‌های صحیح ، کامپایلرهای مدرن بخش اعشاری را نادیده می‌گیرند اما برخی کامپایلرها بر اساس گرد کردن اعداد اعشاری کار می‌کنند که معمولاً بر اساس گرد کردن در ریاضی است ( مثلاً کوچک‌تر از ۰٫۵ به عدد کوچک‌تر گرد می‌شود و بزرگ‌تر از ۰٫۵ به عدد بزرگ‌تر ) ، ضمن اینکه در ریاضی هم ۰٫۵ را هم می‌توان به عدد بزرگ‌تر گرد کرد و هم کوچک‌تر و برخی کامپایلرها نیز در تبدیل داده‌های اعشاری به غیر اعشاری ، بین کست کردن ایفای نقش غیرعلنی و علنی تفاوت قائل می‌شوند ؛ برای دانستن اینکه کامپایلر شما چگونه عمل می‌کند به راهنمای آن مراجعه کنید . در داده‌هایی که همگی اعشاری هستند ( تبدیل انواع اعشاری با دقت معمولی یا دوبرابر یا long double ) ، تبدیل‌ها به نحوی انجام می‌شود که قسمت اعشار از بین نرود ولی اگر مقدار ما ، در داده نتیجه جا نشود ، اول از قسمت اعشاری مقادیر حذف می‌شوند ( که با گرد کردن نیز همراه است ) . ضمن اینکه در احضار تابع ، در صورت همخوانی نداشتن نوع داده آرگومان با پارامتر ، قوانین پیشین ، اعمال می‌گردند
 
در پایان باید بدانید که در تبدیل داده‌ها ، اگر قرار بر از دست رفتن داده‌ها باشد ، در سیستم‌های [https://fa.wikipedia.org/wiki/%D8%A7%D9%86%D8%AF%DB%8C%D8%A7%D9%86 بیگ‌اندیَن] که داده‌ها از خانه‌های ابتدایی به سمت خانه‌های انتهایی حافظه شروع به پر شدن می‌کنند ، داده‌ها از قسمت ابتدایی پاک شده و حذف می‌شوند ؛ اما در سیستم‌های لیتل‌اندیَن داده‌ها از سمت انتهایی خافظه شروع به پر شدن می‌کنند و از سمت انتها ، داده‌ها و مقادیر نادیده گرفته شده و یا حذف می‌شوند
خط ۲۴۰:
https://en.wikipedia.org/wiki/Single-precision_floating-point_format
 
'''توضیح :''' این مبحث مربوط به نوشتن کامپایلر می‌شود که مبتنی بر علم ریاضیات بوده و در علوم برق و الکترونیک نیز مورد استفاده قرار می‌گیرد ؛ اما به صورت مختصر در اینجا به نحوه ذخیره شدن و پردازش اعداد اعشاری در زبان‌های سطح پائینی مثل C و به صورت سخت‌افزاری ، اشاره می‌کنیم . عدد اعشاری با دقت معمولی ، ۳۲ بیت است . بیت آخر که ذخیره شده ( بیت اول در خانه‌های حافظه موقت ) علامتِ عدد را مشخص می‌کند . عدد ۱- به توان عددی که در بیت اول قرار دارد می‌رسد که این عدد یا ۱ است که ۱- به توان ۱ می‌شود ۱- و علامت ، منفی است یا می‌شود ۰ که در نتیجه ۱- به توان ۰ می‌شود ، ۱ و علامت ، مثبت است ؛ پس ۱ نمایانگر منفی بودن عدد و ۰ نمایانگر مثبت بودن عدد می‌باشد . هشت بیت بعدی به صورت ۲ به توان آن عدد منهای ۱۲۷ می‌باشد و در مبنای دهدهی محاسبه می‌شود ؛ که از ۰۰۰۰۰۰۰۰ شروع می‌شود که به صورت قرار دادی نمایانگر عدد ۰ می‌باشد و اگر در قسمت اعشاری همه اعداد ۰ باشند ، عدد ، ۰ منظور می‌شود . اما اگر در قسمت اعشاری ( مابقی بیت‌ها ) نیز مقداری ذخیره شده باشد ، ۰۰۰۰۰۰۰۰ به عنوان ۱ منظور می‌شود که منهای ۱۲۷ می‌شود ۱۲۶- که در نهایت می‌شود <sup>۱۲۶-</sup>۲ و مقداری که در ۸ بیتِ توان ، ذخیره می‌شود تا ۲۵۴ ادامه می‌یابد ( مقدار ۲۵۵ به صورت قرار دادی برای بی‌نهایت ذخیره شده است که با توجه به علامت ، می‌تواند منفی بی‌نهایت یا مثبت بی‌نهایت باشد ) که ۲۵۴ منهای ۱۲۷ می‌شود ۱۲۷ و عدد ۲ به توان ۱۲۷ می‌رسد . مقداری که از ۸ بیت توان با ۲ به توان آن عدد به دست آمد در ۲۳ بیت بعدی که قسمت اعشاری را ذخیره می‌کنند ضرب می‌شود . منتها قسمت اعشاری به صورت اعشار دودویی ( باینری ) ذخیره می‌شوند که در کامپیوتر با یک ۱ ، نیز جمع می‌شود . در دستگاه دودویی و در کامپیوتر که معمولاً بیگ‌اندین می‌باشد بیت بیست و سوم که شماره ۲۲ ـم می‌شود ؛ ۲ به توان ۱- می‌رسد و ضربدر عدد بیت بیست و سوم می‌شود و جمع می‌شود با ۲ به توان ۲- ضربدر عدد بیت بیست و دوم که می‌شود شماره ۲۱ و همین طور تا بیت اول که می‌شود شماره ۰ ادامه پیدا می‌کند . مثلاً ۰٫۰۱۱۱ که در مبنای دودویی می‌باشد ، در مبنای دهدهی خودمان می‌شود : ۰٫۴۳۷۵ ؛ ۲ به توان ۱- ضربدر ۰ می‌شود ۰ که جمع می‌شود با ۱ ضربدر ۲ به توان ۲- که می‌شود ۰٫۲۵ و جمع می‌شود با ۱ ضربدر ۲ به توان ۳- که می‌شود ۰٫۱۲۵ و جمع می‌شود با ۱ ضربدر ۲ به توان ۴- که می‌شود ۰٫۰۶۲۵ که در نهایت عدد ۰٫۴۳۷۵ به دست می‌آید . ضمن اینکه در استاندارد ۷۵۴ انستیتوی جهانی مهندسین برق و الکترونیک ، استثناهایی هم در نظر گرفته شده است که با مطالعه لینک بالا و یا پیوند فایل پی‌دی‌اف استاندارد IEEE 754 می‌توانید از تمام مطالب استاندارد آگاه شوید
 
در تبدیل‌های اعشاری بزرگ‌تر به کوجک‌تر ( مثلاً double به float ) قسمت غیراعشاری از دست نمی‌رود و ابتدا از قسمت اعشاری و آن هم از قسمت انتهای آن ، مقادیر نادیده گرفته شده و پاک می‌شوند که البته با گرد شدن نیز همراه است و طی تبدیل ، اگر قرار بر از دست رفتن مقادیری باشد ، هم رقم‌های اعشاری کوچک‌تر حذف می‌شوند و هم عدد اعشاری گرد می‌شود
خط ۲۴۶:
==typedef==
 
کلیدواژه '''typedef''' برای تعیین یک یا چند شناسه برایبه جایگزینیعنوان جایگزین ، جهت اعلان و تعریف یک نوع داده که خود می‌تواند از کلیدواژه‌هایی تشکیل شده باشد استفاده می‌شود . ضمن اینکه شما می‌توانید هر چند بار که خواستید برای یک یا چند نوع داده ، جایگزین یا جایگزین‌هایی تعریف کنید . مثلاً شما می‌توانید به جای اعلان و یا تعریف یک نوع داده صحیح ، به جای کلیدواژه int ، یک اسم مثل integer از روی int بسازید و از آن جهت اعلان و تعریف داده‌های صحیح استفاده کنید . دقت کنید که با این کار کلیدواژه int منسوخ نمی‌شود ( یعنی همچنان می‌توانید در برنامه از کلیدواژه int برای اعلان و تعریف داده‌های عددی صحیح خود استفاده کنید ) . شکل کلی ایجاد جایگزین‌ها برای انوع داده توسط typedef به شکل زیر است :
 
<code>'''typedef''' ''data-type'' ''new-name''</code>
خط ۳۲۱:
ما در زبان C مجاز هستیم تا اشاره‌گرها را نیز با کمک typedef تعریف و جایگزین نمائیم مثلاً typedef int* iptr شناسه iptr را به عنوان اعلان کننده اشاره‌گری از نوع صحیح تعریف می‌کند . بنابراین با الگوی نوشته شده اگر در داخل برنامه خود بنویسیم : iptr *a شناسه a اشاره‌گر به اشاره‌گری از نوع صحیح خواهد بود و iptr b[10] شناسه b را به عنوان آرایه ۱۰ عنصری از اشاره‌گر از نوع صحیح تعریف می‌کند
 
از کلیدواژه typedef می‌توان برای تعریف ساختمان‌ها و اجتماع‌ها هم استفاده کرد . برای این کار هم می‌توانید با استفاده از typedef و نوشتن کلیدواژه struct یا union و با استفاده از نام برچسب ساختمان یا اجتماع ( به ترتیب ) و سپس با نام جایگزینِ شناسه ، نمونه ایجاد کنید و هم می‌توانید هنگام تعریف ساختمان یا اجتماع ، پیش از کلیدواژه struct یا union کلیدواژه typedef را بنویسید سپس برچسب ساختمان یا اجتماع را نوشته و سپس بعد از بلوک و پیش از سمی‌کالن ( نقطه ویرگول ) ِ ساختمان یا اجتماع نام یا نام‌های جایگزین را بنویسید و سپس با نام جایگزین ، نمونه یا نمونه‌هایی ایجاد کنید
 
مثال :
خط ۳۷۲:
مثلاً اگر یک ساختمان با نام node داشته باشید و بخواهید از آن نمونه‌های اشاره‌گر زیادی بسازید یک بار می‌نویسید typedef node* nodeptr و سپس با کمک نام جایگزین nodeptr چندین نمونه اشاره‌گر با نام‌های معمولی startptr و endptr و curptr و prevptr و errptr و refptr با نوشتن در مقابل nodeptr ایجاد می‌کنید . از این ترفند در مورد ساخت لیست‌های پیوندی نیز استفاده می‌شود
 
در مورد تابع‌های اشاره‌گر نیز typedef به وفور به کار می‌رود ، مخصوصاً که بخواهیم تابع‌های اشاره‌گری تعریف کنیم که پارامترهای آنها تابع‌های اشاره‌گر دیگری باشند . مثال :
 
{{چپ‌چین}}
خط ۴۱۰:
'''دقت کنید :''' در جایگزینی تابع به واسطه typedef نامی که برای تابع انتخاب می‌کنید ، همان نام جایگزین برای تابع است
 
'''توضیح :''' در مثال بالا یک تابع با نام add تعریف نمودیم که دو پارامتر a و b را می‌پذیرد و آنها را با هم جمع می‌کند و حاصل‌جمع را به عنوان خروجی عرضه می‌کند . تابع sub دو پارامتر a و b را می‌گیرد و b را از a کم می‌کند و نتیجه را عرضه می‌کند . تابعی از نوع صحیح اشاره‌گر با نام function که با همین نام جایگزین نیز شده است ( به خاطر وجود کلیدواژه typedef ) با دو پارامتر a و b اعلان شده و سپس تابع call_function با پارامتر اول که یک function است که خود یک صحیح اشاره‌گر می‌باشد تعریف شده و همچنین دو پارامتر دیگر یعنی a و b را می‌پذیرد و به عنوان خروجی تابعی را از نوع صحیح اشاره‌گر که روی دو پارامتر a و b پردازش می‌کند به عنوان خروجی عرضه می‌کند . در نهایت در تابع main یک متغیر صحیح با نام sum به عنوان حاصل اعلان شده که بار اول call_function را احضار می‌کند و مقدار آن را ذخیره می‌کند که به عنوان آرگومان ، تابع add و اعداد ۱۰10 و ۵5 را به ترتیب به آن پاس داده‌ایم و سپس عدد حاصل را در خروجی رابط خط‌دستوری پرینت می‌کند . در بار دوم متغیر sum برابر با خروجی احضار تابع call_function با آرگومان‌های تابع sub و اعداد ۱۰10 و ۵5 ( به ترتیب ) است و این مقدار در خروجی رابط خط‌دستوری با کمک تابع printf چاپ می‌شود
 
می‌توان تابع‌های اشاره‌گری که تابع‌های اشاره‌گری نیز دارند را هم با کمک typedef خواناتر نمود . مثلاً در کرنل FreeBSD نوشته شده بود :
خط ۴۶۵:
'''تفاوت typedef و دستور مستقیم define'''
 
typedef ، تنها شناسه‌ای را که برای آن معین نموده‌ایم با نوع داده تعریف شده جایگزین می‌کند در حالی که دستور مستقیم define هر چیزی را حتی مقدار و موجودی‌ای را که با define تعریف کرده باشیم جایگزین می‌کند . define می‌تواند حتی کلیدواژه‌ها و یا تابع‌های کتابخانه‌ای را هم جایگزین کند ( که البته کاری ریسک‌پذیر است و باید از آن اجتناب شود ) typedef مشمول قانون حوزه دید می‌شود اما دستور مستقیم define# حوزه‌ای ندارد و هر جا که تعریفی برای آن مقرر شده باشد پس از دستور ، هر تعریف شده را ، با مقدارش جایگزین می‌کند که البته یکی از تفاوت‌های عمده typedef و define این است که define نمی‌تواند نوع داده‌ای را تعریف کند و فقط هر جا که کاراکتر یا رشته تعریف شده برای خود & در سر راهش قرار بگیرد با مقداری که برای آن تعریف شده جایگزین می‌کند مثلاً typedef با کد typedef float * fp هر fp را به اشاره‌گر اعشاری تبدیل می‌کند و اگر بنویسیم : fp a, b, c معادل خواهد بود با : float *a, *b, *c در حالی که اگر از دستور مستقیم define استفاده کنیم و بنویسیم :
 
{{چپ‌چین}}