الفريق العربي للبرمجةأرشيف المنتديات · 2000 – 2023
نسخة أرشيفية للقراءة فقط — التسجيل والمشاركة مغلقان، والمحتوى محفوظ كما كان.

سؤالين عن C++ ،، كيف اكتب كم كلمة كبيرة في متغير + كيف انفذ امر واجمع بين فاريبل و متغير

رائج
بدأه CPP-Programmer في 18 سبتمبر 2008 · 65 رد · 6,626 مشاهدة · في الأسئلة المجابة
مشاركة: واتساب X فيسبوك تيليجرام
#51

عفوا على المقاطعة .

نحن هنا لاننكر بأن استخدام strcpy بشكل خاطئ ينتج عنه مشكلة , ولكن لايعني هذا ان نقول بأن الدالة اصبحت عديمة الفائدة وخطرة و مذمومة و و و .

ببساطة اي خطأ ينتج في البرنامج نتيجة وضع المبرمج سلسلة تفوق الـ dest في الـ src يحسب على المبرمج وليس على الدالة , يمكن تفادي ذلك بكثير من الطرق strncpy احد هذه الطرق , يمكن اختابر العدد المراد نسخه مع طول الـ dest الحالي والكلي ,يمكن استخدام الحجز الديناميكي , عملية التحسينات وغيرها تتم من اجل المبرمج الذي اخر همه هو استخدام Exception Handling

llback.jpg

اشهد ان لا إله إلا الله وان محمدا ً رسول الله

#52
سمسوما كتب:
انت مش فاهم سى، وما ذكرته خطأ... strncpy تنسخ بحد أقصى الحجم المحدد (وهو حجم الواجهة بغض النظر عن حجم النص المصدر) أو حتى تجد 0\ ، فمبرمج السى اليقظ المتمرس يحدد حجم الواجهة وليس المصدر !

دعني أخبرك أن طريقتك في الكلام ليست لطيفة ، وعندما يتحول الحوار إلى مبارزة فأنا أول المنسحبين.

بغض النظر عن تجاهلك لحقيقة أن strcat و strncpy لهما وظيفتين مختلفتين ولا يجوز المقارنة بينهما ، سأخبرك التالي :

الدالة strncpy تنسخ فوق الوجهة بعدد ال bytes المحدد لها ، بغض النظر عن حجم الوجهة ، وفي جميع الأحوال الدالة سوف تكتب فوق الوجهة عدد البايتات المحدد لها ، وإذا كان المصدر منتهي بحرف خامد (في مدى البايتات المحدد) فسوف يكتب عدد الحروف ما قبل الحرف الخامد ويملأ الباقي من العدد المحدد من الحروف بحروف خامدة \0 ، أي أنه في جميع الأحوال سوف يكتب العدد المحدد من المصدر فوق الوجهة وليس بحد أقصى العدد المحدد ، ومرة أخرى أؤكد لك لا اعتبار في ذلك لحجم الوجهة.

اقرأ في تعريف الدالة strncpy من الرابط الأول أدناه :

اقتباس
[h3]Parameters[/h3]

destination

Pointer to the destination array where the content is to be copied.

source

C string to be copied.

num

Maximum number of characters to be copied from source.

هل ترى كيف أن كلمة source محددة بالقلم الإيطالي ( مائلة )

لست أنا من حددها بهذه الطريقة ، هي كذلك في تعريف اللغة واضغط الرابط أدناه لتراه بنفسك.

ويمكنك أن تستزيد عن ذلك من هذه الروابط :

http://www.cplusplus.com/reference/clibrar...ng/strncpy.html

http://man.he.net/man3/strncpy

http://www.opengroup.org/onlinepubs/007908...sh/strncpy.html

سمسوما كتب:
والحل العملى للخروج من هذه المشكله استخدام ++C مثل string او فى MFC مثل CString او فى COM مثل _bstr_t أو _variant_t

رائع ، أحييك على هذه الملاحظة ، نعم بالضبط هذا هو ما أقوله لو أنك قرأت الصفحات السابقة.

سمسوما كتب:
ولكن أسئلك سؤال واحد فقط: هل الدالة strcat_s قياسية؟ (هل موجوده فى ANSI C ولجميع المترجمات الاخرى غير MSVS؟)

لا أدري إن كانت قياسية أم لا ، ربما ليست قياسية ، لكنها في msvc منبوذة نظرًا لكونها غير آمنة ، إذا كنت تريد أن تمحى الدالة من المقياس كدليل على أنها غير آمنة فعفوًا لا أستطيع أن أفعل ذلك!

معروف أن السي هي أقل اللغات أمانًا وأكثرها خطورة بعد لغة التجميع ، ولكن هذا لا يعني أن أحدًا لا يكتب برامجًا بالسي أو بالتجميع.

ولن أزيد على كلام أخي Xacker ، نحن هنا بصدد إعطاء نصيحة لمبتدئ أملاً في تجنيبه الحيرة لاحقًا ، ولسنا بصدد استعراض عضلات ولا مبارزة في المعلومات.

تم تعديل هذه المشاركة بواسطة عمـــرو في 21 سبتمبر 2008 في 05:09

#53
عمـــرو كتب:
وإذا لم تكن مقتنع أن strcat دالة خطيرة ، ربما تريد أن تسأل مصمموا لغة السي لماذا strcat الآن منبوذة - deprecated - ولماذا هناك strcat_s بدلاً منها ؟

والملحق _s بالمناسبة هو اختصار ل safe.

عمـــرو كتب:
اقتباس
ولكن أسئلك سؤال واحد فقط: هل الدالة strcat_s قياسية؟ (هل موجوده فى ANSI C ولجميع المترجمات الاخرى غير MSVS؟)
لا أدري إن كانت قياسية أم لا ، ربما ليست قياسية ، لكنها في msvc منبوذة نظرًا لكونها غير آمنة

:blink: :blink: :blink: :blink: :blink:
اقتباس
الدالة strncpy تنسخ فوق الوجهة بعدد ال bytes المحدد لها ، بغض النظر عن حجم الوجهة ، وفي جميع الأحوال الدالة سوف تكتب فوق الوجهة عدد البايتات المحدد لها ، وإذا كان المصدر منتهي بحرف خامد (في مدى البايتات المحدد) فسوف يكتب عدد الحروف ما قبل الحرف الخامد ويملأ الباقي من العدد المحدد من الحروف بحروف خامدة \0 ، أي أنه في جميع الأحوال سوف يكتب العدد المحدد من المصدر فوق الوجهة وليس بحد أقصى العدد المحدد ، ومرة أخرى أؤكد لك لا اعتبار في ذلك لحجم الوجهة.

اقرأ في تعريف الدالة strncpy من الرابط الأول أدناه :

اقتباس
Parameters

destination

Pointer to the destination array where the content is to be copied.

source

C string to be copied.

num

Maximum number of characters to be copied from source

هل ترى كيف أن كلمة source محددة بالقلم الإيطالي ( مائلة )

لست أنا من حددها بهذه الطريقة ، هي كذلك في تعريف اللغة واضغط الرابط أدناه لتراه بنفسك.

الطبيعى أن يتم النسخ من المصدر source، قإذا لم يكن النسخ من المصدر ... فمن أين يكون؟

والنسخ يكون للواجهة destination، بالطول num، وهو الطول الذى لا يتعدى حجم الواجهة.

أى يتم النسخ من source إلى destination بحد أقصى num (أقصى طول للـ destination)

هذه من المساعدة من MSDN

post-169176-1221989712_thumb.jpg

ولن اشرح طويلاً حول الواجهة والمصدر (فمثال يعبر عن ابلغ شرح)، ولكن منتظر قرائتك وتعليقك على المثال التالى

#include <string.h>
#include <iostream.h>

#define		MAX_NAME_BUFFER		20

int main(int argc, char* argv[])
{
	char	des_str[MAX_NAME_BUFFER+1];
	char	*src_long = "This is long string more than 20 bytes";
	char	*src_short = "This is short";

	strncpy(des_str, src_long, MAX_NAME_BUFFER);
	des_str[MAX_NAME_BUFFER] = '\0';
	cout << des_str << endl;

	strncpy(des_str, src_short, MAX_NAME_BUFFER);
	des_str[MAX_NAME_BUFFER] = '\0';
	cout << des_str << endl;

	return 0;
}

تم تعديل هذه المشاركة بواسطة سمسوما في 21 سبتمبر 2008 في 13:18

#54

فى شئ مش قادر افهمه فى كلامكم

انتم تقارنون بين strncpy و strcat

Definition fo strncpy

Copy characters from string

Copies the first num characters of source to destination. If the end of the source C string (which is signaled by a null-character) is found before num characters have been copied, destination is padded with zeros until a total of num characters have been written to it.

Definition fo strcat

Concatenate strings

Appends a copy of the source string to the destination string. The terminating null character in destination is overwritten by the first character of source, and a new null-character is appended at the end of the new string formed by the concatenation of both in destination.

التعريفات من هذا الموقع

اريد ان اعرف كي تقارنون دالتين مختلفتين تماما فى العمل ؟

-----

انا جلابت البرنامج التالى , و كان يعمل 100% مع اختلاف حجم المصفوفة ؟

كيف حدث هذا ؟

  1.  
  2. #include <iostream>
  3. using namespace std;
  4.  
  5. int main(void)
  6. {
  7. char src[11] = "AAABBBCCCD";
  8. char des[3];
  9. strcpy(des,src);
  10. cout << des << endl;
  11. system("pause");
  12. }
  13.  
  14.  

من الملاحظ ان النص des حجمه 3 و النص src حجمه 11 فكيف تم نسخ محتويات src الى des مع انهم مختلفين فى الحجم ؟

#55
Omar Eladel كتب:
فى شئ مش قادر افهمه فى كلامكم

انتم تقارنون بين strncpy و strcat

المقارنه بين

strcpy مقارنة بـ strncpy

أو

strcat مقارنة بـ strncat

#56

عمرو , ممكن تعطيني مثال بسيط على Overflow

llback.jpg

اشهد ان لا إله إلا الله وان محمدا ً رسول الله

#57
سمسوما كتب:
:blink: :blink: :blink: :blink: :blink:

أخطئ في قراءتها على أنها strcpy_s هل تريد أن تأخذها عليه واحدة أيضا؟

أما بخصوص الشفرة التي وضعتها، فلاحظ معي ما قد لا يقوم به مبرمج سي لا ينتبه إلى هذه النقطة.

	char	des_str[MAX_NAME_BUFFER+1];

قمت بزيادة حجم المصفوفة بمقدار 1. السبب هو أ، strncpy حتى لو كانت تقوم بالنسخ بالضبط حسب عدد البايتات المحدد لها لكنها لا تقوم بإضافة حرف خامد \0 إلى نهاية السلسلة المنسوخة. بالتالي الكود السابق الذي قمت بوضعه يمكن أن يكتبه شخص بهذا الشكل

#include <iostream.h>

#define		MAX_NAME_BUFFER		20

int main(int argc, char* argv[])
{
	char	des_str[MAX_NAME_BUFFER];
	char	*src_long = "This is long string more than 20 bytes";

	strncpy(des_str, src_long, MAX_NAME_BUFFER);
	cout << des_str << endl;

	return 0;
}

وبهذه الحالة يتم نسخ 20 بايت بالضبط إلى الـ dest لكن حجم الـ dest بالأساس هو 20 حرف فما يحدث هنا هو أن النسخ صحيح لكن ليس هناك null terminator فتظهر نتائج النسخ بالشكل

post-7008-1222002846_thumb.jpg

وهذا سينتج overflow أو يتم طباعة نتائج غير المرغوب بها.

حالة أخرى

#include <iostream.h>

#define		MAX_NAME_BUFFER		20

int main(int argc, char* argv[])
{
	char	des_str[MAX_NAME_BUFFER];
	char	*src_long = "This is long string more than 20 bytes";

	strncpy(des_str, src_long, MAX_NAME_BUFFER);
		des_str[MAX_NAME_BUFFER] = '\0';
	cout << des_str << endl;

	return 0;
}

في هذه الحالة لن يعود هناك overflow في عملية النسخ.

لكنه سيقوم بكتابة \0 فوق بايت تالي للسلسلة بمعنى لو كانت التصريحات بالشكل القديم

	char	des_str[MAX_NAME_BUFFER];
	char	*src_long = "This is long string more than 20 bytes";
	char	*src_short = "This is short";

فالـ \0 سيأتي ليكتب فوق "T" من src_long.

عند محاولة النسخ في المرة الثانية فإن أول حرف في src_long هو \0 بالتالي يعتبر نهاية السلسلة ولن يتم نسخ شئ حتى الصفر نفسه.

تخيل الكارثة التي قد تظهر من هذا.

الحالة الثالثة هي الحالة التي قمت بوضعها بنفسك

#include <string.h>
#include <iostream.h>

#define		MAX_NAME_BUFFER		20

int main(int argc, char* argv[])
{
	char	des_str[MAX_NAME_BUFFER+1];
	char	*src_long = "This is long string more than 20 bytes";
	char	*src_short = "This is short";

	strncpy(des_str, src_long, MAX_NAME_BUFFER);
	des_str[MAX_NAME_BUFFER] = '\0';
	cout << des_str << endl;

	return 0;
}

متمرس السي يعلم أنه لا يعود هناك حاجة إلى استخدام

	des_str[MAX_NAME_BUFFER] = '\0';

لأن النسخ يتم بطول 20 بايت بالضبط والـ buff محجوزة بطول 21 بايت فالبايت الأخير دوماً خامد ولا داع لإعادة كتابة صفر فوق صفر.

اقتباس
المقارنه بين

strcpy مقارنة بـ strncpy

أو

strcat مقارنة بـ strncat

غير صحيح، المقارنة كانت بالأساس بين strcpy_s و strcpy

بين strcat و strcat_s

فلماذا تخرج عن الموضوع؟

@عمر:

المثال الذي ذكرته صحيح، لكنه يسبب overflow لأن ما تبقى من محتويات src "BBBCCCD" هذه سيتم نسخها في عنوان الذاكرة التالي لـ dst والذي قد يكون عنوان ذاكرة في المكدس (وهذا ما يحدث في حالة لغة البرمجة C أليس كذلك؟) بالتالي لو كان هناك عنوان عودة return address موجود لنفرض في عنوان الذاكرة [ESP+100] وكانت dst ذات عنوان ذاكرة [ESP] وقمت بجعل طول src بطول 104 بايت فإن آخر 4 بايت سيتم كتابتهم بدلاً من عنوان العودة وعندما ينتهي البرنامج من تنفيذ وظيفته سيأتي ليقوم بالخروج من هذه الوظيفة بالاعتماد على هذا العنوان وسيجد نفسه في العنوان AAAAAAAA مثلاً بدل العنوان السابق الصحيح.

وهذه ثغرة كبيرة.

تم تعديل هذه المشاركة بواسطة Xacker في 21 سبتمبر 2008 في 22:11

Do as I say, not as I do

We are Anonymous. We are Legion. We don't forgive. We don't forget

#58

نسيت أن أذكر تعليق صغير حول حجز مصفوفة، فإن لم تكن قد قمت بعمل init لها وتصفيرها بالكامل فالبرنامج غالباً ما سيحجز مكان في الذاكرة غير مستخدم هذا المكان ليس بالضرورة أن يكون فارغ تماماً 100% وفي المثال التالي سأوضح ما أريد قوله

#include <iostream>

using namespace std;

int main(void)

{

char src[11] = "AAABBBCCCD";
char des[3];
strcat(des,src);
cout << des << endl;
system("pause");

return 0;
}

المصفوفة des في الذاكرة ستحجز بهذا الشكل:

post-7008-1222004680_thumb.jpg

لاحظ أن البايتات ليست خامدة (\0) عند المعالجة.

فعندما تأتي لتقوم بتنفيذ strcat(des,src) انظر ما ستراه هذه الدالة

post-7008-1222004929_thumb.jpg

ألا يكفي هذا لتكون strcat مشكلة بحد ذاتها إن لم يتم تصفير الـ buff بعد حجزها؟

طيب لنفرض الآن أنك قمت بتصفيرها أول مرة (الحمد لله، فطنة)

وكان برنامجنا بالشكل التالي:

#include <string.h>
#include <iostream.h>

#define		MAX_NAME_BUFFER		20

int main(int argc, char* argv[])
{
	char	des_str[MAX_NAME_BUFFER+1];
	char	*src_long = "This is long string more than 20 bytes";
	char	*src_short = "This is short";

	strcat(des_str, src_long);
	cout << des_str << endl;

	strcat(des_str, src_short);
	cout << des_str << endl;

	return 0;
}

هذا ما سيكون الحال عليه، نفس الورطة:

post-7008-1222005306_thumb.jpg

بعد تنفيذ strcat:

post-7008-1222005439_thumb.jpg

هل ترى ما حدث؟ طالما أن أول بايت ليس صفر فالبرنامج سيبحث عن أول صفر وسيقوم بنسخ السلسلة خلفه مباشرة، انظر أين تمت الكتابة، خارج نطاق السلسلتين تماماً!

هذا في حالة استخدام strcat لنقل سلسلة str2 إلى مكان ذاكرة لم يتم النسخ إليه بعد ولا يحتوي على str1 المراد النسخ خلفها، طيب، افرض أنه تم استخدام strcpy و strcat في البرنامج للصق السلسلتين في مكان ذاكرة باسم des.

لنرى الكود ولننظر التنفيذ وكفى هراء:

#include <string.h>
#include <iostream.h>

#define		MAX_NAME_BUFFER		20

int main(int argc, char* argv[])
{
	char	des_str[MAX_NAME_BUFFER+1];
	char	*src_long = "This is long string more than 20 bytes";
	char	*src_short = "This is short";

	strcpy(des_str, src_long);
	cout << des_str << endl;

	strcat(des_str, src_short);
	cout << des_str << endl;

	return 0;
}

والنسخ في الذاكرة:

post-7008-1222005819_thumb.jpg

ألم يتجاوز هذا حجم الـ buff الذي قمت بتحديده؟

إذاً، خلاصة ما كنا نحاول إيصاله لصاحب الموضوع:

strcpy لا تراعي حجم الـ buffer المراد النسخ إليها، لو كانت بحجم 20 بايت وأردت نسخ 1000 بايت سيتم نسخها بدون اعتراض لكنه ستقوم بالكتابة فوق كل ما يلي تلك الـ buffer في الذاكرة، ولو كانت هذه الـ buffer مكدس فهناك احتمال كبير أن تخلص ثغرة في برنامجك قد تستغل من قبل أمور كثيرة.

strcat لا تراعي حجم الـ buffer المراد النسخ إليها، تقوم بالبحث عن صفر في الوجهة المراد النسخ إليها، فإن كنت تستخدمها للنسخ إلى buffer بدلاً من strcpy فهذا خطأ - وأيضا ستقوم بالكتابة في مكان ما الله يعلم ما سيكون في الذاكرة.

strcat لو تم استخدامها مع buffer نظامية بطول 100 بايت مثلاً وكان النص str1 الذي تم نسخه بأمان إلى هذه الـ buffer بطول 80 بايت وكان النص str2 بطول 20 بايت (اوه انظر، تماماً على حجم الـ buffer.. يا سلام على التوفير في الذاكرة!) فإن strcat وللعلم تقوم بإضافة null terminator بشكل اوتوماتيكي في نهاية السلسلة المنسوخة بالتالي سيتم نسخ هذا الصفر في هذه الحالة على أول بايت يلي الـ buffer التي تم حجزها.

هل تبقى strcpy و strcat آمنة الآن؟

ماذا لو قمت باستخدام strcpy_s و strcat_s؟

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>

int main( void )
{
   char str1[] = "Hello world from ";
   char str2[] = "strcpy_s ";
   char str3[] = "and ";
   char str4[] = "strcat_s!";
   char des[20];
   strcpy_s( des, str1 );
   strcat_s( des, str2 );
   strcat_s( des, str3 );
   strcat_s( des, sizeof(des), str4 );

   printf( "String = %s\n", des );
   return 0;
}

هذه الدوال أساساً مصممة كي تقوم بإحداث exception مربوط بـ SEH دون أن ينهار البرنامج بشكل غير مقبول، فإن كنت تريد النسخ إلى buffer بحجم أقل من طول السلسلة المطلوب نسخها فسيظهر لديك

post-7008-1222007950_thumb.jpg

هل يكفي هذا كي ننهي هذا الجدل البيزنطي؟

Do as I say, not as I do

We are Anonymous. We are Legion. We don't forgive. We don't forget

#59

وأزيدك من الشعر بيت بما أني في مزاج عالي الآن، حتى strncat و strncpy لها نسخ أكثر أمناً strncat_s و strncpy_s

https://buildsecurityin.us-cert.gov/daisy/b...ng/317-BSI.html

حسناً هذا يكفي، سأذهب لتحضير الإفطار الآن.

Do as I say, not as I do

We are Anonymous. We are Legion. We don't forgive. We don't forget

#60
Omar Eladel كتب:
انا جلابت البرنامج التالى , و كان يعمل 100% مع اختلاف حجم المصفوفة ؟

كيف حدث هذا ؟

  1.  
  2. #include <iostream>
  3. using namespace std;
  4.  
  5. int main(void)
  6. {
  7. char src[11] = "AAABBBCCCD";
  8. char des[3];
  9. strcpy(des,src);
  10. cout << des << endl;
  11. system("pause");
  12. }
  13.  
  14.  
  15.  

من الملاحظ ان النص des حجمه 3 و النص src حجمه 11 فكيف تم نسخ محتويات src الى des مع انهم مختلفين فى الحجم ؟

جرب كذا انقل البرنامج إلى دالة اخرى وشوف النتائج

void x(char *src)
{
	char des[3];
	strcpy(des,src);
	cout << des << endl;
}
int main(void)
{
	char src[11] = "AAABBBCCCD";
	x(src);
	return 0;
}

ايضا انت قلت

	char des[3];

حاول ان تقوم بطباعة طول السلسلة

وجرب طباعة طول السلسلة الان بعد كتابة السطر بهذا الشكل

	char des[3]="";

هل هنالك اختلاف ؟ :wink:

***********************************************************************

عند تخزين سلسلة حروف ضمن مصفوفة للحروف يجب التأكد من ان المصفوفة ذات حجم كاف ٍ لتخزين اطول سلسلة حروف تتعامل معها , اذا كانت سلسلة الحروف المراد التعامل معها اطول من المصفوفة المستخدمة للتخزين فأنه سيتم تجاوز نهاية المصفوفة وتكتب بقية الحروف في المواضع التالية لعناصر المصفوفة فوق المعطيات الموجودة في الذاكرة مسبقا .

من الخطأ عدم اضافة الحرف الصفري إلى الوسيط الاول للدالة strncpy عندما تكون قيمة الوسيط الثالث اقل من او يساوي طول السلسلة للوسيط الثاني

llback.jpg

اشهد ان لا إله إلا الله وان محمدا ً رسول الله

#61

أخ time1 ، المثال أعطاء الأخ عمر فوق مشاركتك بمشاركتين ، هنا /index.ph...st&p=870068 كما شرحه الأخ Xacker ،

رائع أخي Xacker ، لا يوجد ما يمكن أن أقوله بعدك !

#62
عمـــرو كتب:
أخ time1 ، المثال أعطاء الأخ عمر فوق مشاركتك بمشاركتين ،

حسنا اخي , ارجع واقول لك هذا استخدام خاطئ من المبرمج والمفروض ان يحسب على المبرمج وليس على الدالة , وقد وضحت ذلك قبل هذه المرة وقلت :

عند تخزين سلسلة حروف ضمن مصفوفة للحروف يجب التأكد من ان المصفوفة ذات حجم كاف ٍ لتخزين اطول سلسلة حروف تتعامل معها , اذا كانت سلسلة الحروف المراد التعامل معها اطول من المصفوفة المستخدمة للتخزين فأنه سيتم تجاوز نهاية المصفوفة وتكتب بقية الحروف في المواضع التالية لعناصر المصفوفة فوق المعطيات الموجودة في الذاكرة مسبقا .

llback.jpg

اشهد ان لا إله إلا الله وان محمدا ً رسول الله

#63

بالطبع هو خطأ من المبرمج ، لأن المبرمج هو من كتب الشفرة ، لكن ما نقوله هنا هو أن تصميم اللغة يسمح للمستخدم بأن يخطئ مثل هذه الأخطاء بدون تنبيه ، هل وضحت الفكرة الآن ؟

بعد إذنكم هذه هي آخر مشاركة لي في هذا الموضوع.

#64
Xacker كتب:
أخطئ في قراءتها على أنها strcpy_s هل تريد أن تأخذها عليه واحدة أيضا؟
أنا لا اصطاد أخطاء، ولا اريد ان ااخذها واحده، ولو ان الموضوع فلسفى لما كان هناك فارقاً

ولكنى كمبرمج تعلمت انى لا استطيع ان استخدم داله بدل الأخرى، واقول للمترجم أخطئت في قراءتها على أنها strcpy_s هل تريد أن تأخذها عليه واحدة أيضا يا ايها المترجم Compiler؟ :S

Xacker كتب:
أما بخصوص الشفرة التي وضعتها، فلاحظ معي ما قد لا يقوم به مبرمج سي لا ينتبه إلى هذه النقطة.

	char	des_str[MAX_NAME_BUFFER+1];

قمت بزيادة حجم المصفوفة بمقدار 1. السبب هو أ، strncpy حتى لو كانت تقوم بالنسخ بالضبط حسب عدد البايتات المحدد لها لكنها لا تقوم بإضافة حرف خامد \0 إلى نهاية السلسلة المنسوخة. بالتالي الكود السابق الذي قمت بوضعه يمكن أن يكتبه شخص بهذا الشكل

#include <iostream.h>

#define		MAX_NAME_BUFFER		20

int main(int argc, char* argv[])
{
	char	des_str[MAX_NAME_BUFFER];
	char	*src_long = "This is long string more than 20 bytes";

	strncpy(des_str, src_long, MAX_NAME_BUFFER);
	cout << des_str << endl;

	return 0;
}

وبهذه الحالة يتم نسخ 20 بايت بالضبط إلى الـ dest لكن حجم الـ dest بالأساس هو 20 حرف فما يحدث هنا هو أن النسخ صحيح لكن ليس هناك null terminator فتظهر نتائج النسخ بالشكل

post-7008-1222002846_thumb.jpg

وهذا سينتج overflow أو يتم طباعة نتائج غير المرغوب بها.

يا اخى الكريم اذا لم ينتبه مبرمج السى كما ذكرت أنت سلفاً لواحده من اسس لغة السى التى يعرفها اى مبتدء فى لغة السى وهى (قواعد اساسيات السلاسل الحرفية ودوالها)، فهذا ليس عيب فى اللغة، وكما ذكر الأخ time1 هذا استخدام خاطئ من المبرمج والمفروض ان يحسب على المبرمج وليس على الدالة، فلا داعى للدفاع عن خطئ المبرمج والصاقه باللغة، والذى يعتبر ذلك خطئ فى لغه السى فليتجه للغات لا تحتوى مثل هذه الاخطاء من وجهة نظره مثل VB.

صانياً: معروف لذى اى طالب دارس سى أن strncpy لا تضيف 0\ فى نهاية السلسله من تلقاء نفسها، وهذا كان واضح فى مثالى وفى امثله مايكروسوفت التعليميه، ولكن أخطئت مرتين فى كودك: 1)لم تأخذ فى حساباتك مكان الرمز 0\ وملئت المخزن كاملاً ب 20 حرف، وهذا يتنافى مع قواعد السلاسل الحرفيه التى تستلزم 0\ فى نهاية السلسله.

2) تجاهلت هذا السطر فى مثالك

des_str[MAX_NAME_BUFFER-1] = '\0';

وتزعم ان النتائج ظهرت بشكل خاطئ، ببساطه لأن كودك الذى بنيت عليه شرحك وتحليلك اساساً خطئ، ولو كنت التزمت بقواعد اللغة فيما يخص هذه الداله والسلاسل الحرفيه، لما ظهرت المشاكل التى اختلقتها وتحدثت عنها مشكوراً.

Xacker كتب:
حالة أخرى

#include <iostream.h>

#define		MAX_NAME_BUFFER		20

int main(int argc, char* argv[])
{
	char	des_str[MAX_NAME_BUFFER];
	char	*src_long = "This is long string more than 20 bytes";

	strncpy(des_str, src_long, MAX_NAME_BUFFER);
		des_str[MAX_NAME_BUFFER] = '\0';
	cout << des_str << endl;

	return 0;
}

في هذه الحالة لن يعود هناك overflow في عملية النسخ.

لكنه سيقوم بكتابة \0 فوق بايت تالي للسلسلة بمعنى لو كانت التصريحات بالشكل القديم

	char	des_str[MAX_NAME_BUFFER];
	char	*src_long = "This is long string more than 20 bytes";
	char	*src_short = "This is short";

فالـ \0 سيأتي ليكتب فوق "T" من src_long.

عند محاولة النسخ في المرة الثانية فإن أول حرف في src_long هو \0 بالتالي يعتبر نهاية السلسلة ولن يتم نسخ شئ حتى الصفر نفسه.

تخيل الكارثة التي قد تظهر من هذا.

نفس الخطئ ، وهو انه من قواعد استخدام السلاسل الحرفيه الاخذ فى الحسبان 0\ وهذا لم تأخذه فى حسبانك، وبالتالى هو خطئك اولاً واخيراً وليس خطئ اللغه او الداله

كذلك اخطئت مره أخرى بأستخدامك الكود

des_str[MAX_NAME_BUFFER] = '\0';

فى هذا المثال لأنك عرفت طول السلسله بالطول MAX_NAME_BUFFER أى من (0) إلى (MAX_NAME_BUFFER - 1) واستخدام الموضع MAX_NAME_BUFFER خطئ يقع فيه صغار المبرمجين، وجملتك في هذه الحالة لن يعود هناك overflow في عملية النسخ هى جمله خاطئة لان فعلاً حدث overflow لأنك تعديت الحد الاقصى للمخزن.

Xacker كتب:
الحالة الثالثة هي الحالة التي قمت بوضعها بنفسك

#include <string.h>
#include <iostream.h>

#define		MAX_NAME_BUFFER		20

int main(int argc, char* argv[])
{
	char	des_str[MAX_NAME_BUFFER+1];
	char	*src_long = "This is long string more than 20 bytes";
	char	*src_short = "This is short";

	strncpy(des_str, src_short, MAX_NAME_BUFFER);
	des_str[MAX_NAME_BUFFER] = '\0';
	cout << des_str << endl;

	return 0;
}

متمرس السي يعلم أنه لا يعود هناك حاجة إلى استخدام

	des_str[MAX_NAME_BUFFER] = '\0';

لأن النسخ يتم بطول 20 بايت بالضبط والـ buff محجوزة بطول 21 بايت فالبايت الأخير دوماً خامد ولا داع لإعادة كتابة صفر فوق صفر.

للأسف هذا الكلام غير صحيح اطلاقاً، فالبايت الاخير غير خامد كنا ذكرت، واى مبتدئ فى لغه السى كتب برنامجاً واحد على الاقل يعرف هذه المعلومه، ويعرف ان البايت يكون خامد فى حاله تعريف المتغير على انه static اما المتغير الـ dynamic مثل هذا المثال فيكون قيمته من مخلفات الذاكره ولابد من التأكد من وضعه 0\

وللتوضيح اقصد static لو قمت بتعريف المتغير بالشكل التالى

static char	des_str[MAX_NAME_BUFFER+1];

Xacker كتب:
طيب لنفرض الآن أنك قمت بتصفيرها أول مرة (الحمد لله، فطنة)

وكان برنامجنا بالشكل التالي:

#include <string.h>
#include <iostream.h>

#define		MAX_NAME_BUFFER		20

int main(int argc, char* argv[])
{
	char	des_str[MAX_NAME_BUFFER+1];
	char	*src_long = "This is long string more than 20 bytes";
	char	*src_short = "This is short";

	strcat(des_str, src_long);
	cout << des_str << endl;

	strcat(des_str, src_short);
	cout << des_str << endl;

	return 0;
}

يا اخى الكريم مرة اخرى هذا خطئ مبرمج وليس خطئ داله

ارجوك اقرى اى كتاب فى بدايات لغه السى او حتى المساعده قبل ان تدلوا بدلوك :wub:

معذرة أخى الكريم فأنا غير قادر على استكمال شرحك فكله مبنى على امثله خطأ

وكلها أخطاء مبرمج وليست مخاطر لغة

أخى الكريم اتمنى ان تزيد من قرائتك وتمرسك فى لغة السى ، ونرى تحليلات اكثر منطقيه واقرب للواقع خاليه من الاخطاء والجدل البيزنطى بدون علم :).

---------------------------

أخى الكريم هل من الممكن ان تتفضل بشرح كل هذه المشاكل التى قمت بتحليليها على المثال الاصلى الصحيح

#include <string.h>
#include <iostream.h>

#define		MAX_NAME_BUFFER		20

int main(int argc, char* argv[])
{
	char	des_str[MAX_NAME_BUFFER+1];
	char	*src_long = "This is long string more than 20 bytes";
	char	*src_short = "This is short";

	strncpy(des_str, src_long, MAX_NAME_BUFFER);
	des_str[MAX_NAME_BUFFER] = '\0';
	cout << des_str << endl;

	strncpy(des_str, src_short, MAX_NAME_BUFFER);
	des_str[MAX_NAME_BUFFER] = '\0';
	cout << des_str << endl;

	return 0;
}

بدون أى تعديل على المثال !!!!!!!!!!!

لن تجد اى من هذه الأخطاء ولا أى من الحالات سابقة الذكر

لأن ببساطه انت فى كل مره تغير فى جزء من المثال او تحذف منه جزء ثم تحلل ذلك على انه خطأ او قصور فى الدالة، وفى الحقيقه هو خطأ نتيجة لتعديلك فى المثال الصحيح

ما أقصده أن لديك مثال صحيح اذا اردت أن توضح شرحك وتحليلك فأبنى عليه كما هو دون تعديل خطئ او نقص خطئ او زياده خاطئة

أو وضح ذلك من خلال مثال اساساً صحيح وليس مثال اساساً خطأ

تم تعديل هذه المشاركة بواسطة سمسوما في 21 سبتمبر 2008 في 23:16

#65

عمرو :

اعلم بان ردك السابق كان اخر رد لك , لكن اريد ان اقول لك بأن ماتتحدث عنه واضح لنا جميعا وواضح حتى لكل مبتدء بالسي , ولكن هذه المشكلة لاتسمح لاي احد كان ان يصف سهولة التعامل مع الذاكرة التي توفرها السي بانها لغة خطرة الاستخدام او بأنها تسمح للمبرمجين بالخطأ ,فكل دالة ان لم يتم استخدامها بشكل صحيح في اي لغة تعرض المبرمج والبرنامج لاخطاء قاتلة .

#include <stdio.h>

void manipulate(char *buffer) {
  char newbuffer[80];
  strcpy(newbuffer,buffer);
}

int main() {
  char ch,buffer[4096];
  int i=0;

  while ((buffer[i++] = getchar()) != '\n') {};

  i=1;
  manipulate(buffer);
  i=2;
  printf("The value of i is : %d\n",i);
  return 0;
}

كان يستطيع المستخدم في المثال السابق ان يستخدم الحجز الديناميكي ويريح نفسه والامة من غبائه , فالخدمات التي تقدمها لغة سي في سهولة التعامل مع الذاكرة يحسب لها وليس عليها .

سمسوما:

اقتباس
الطبيعى أن يتم النسخ من المصدر source، قإذا لم يكن النسخ من المصدر ... فمن أين يكون؟

والنسخ يكون للواجهة destination، بالطول num، وهو الطول الذى لا يتعدى حجم الواجهة.

أى يتم النسخ من source إلى destination بحد أقصى num (أقصى طول للـ destination)

يمكن للـnum ان يتخطى الطول المخصص للـ Dest ولاشئ يمنعه وتحدث عندها المشكلة وهي مايتحدث عنها الاخوان , بالنسبة للمثال الذي ذكرته فليس هو المثال المقصود , الاخ عمرو يقصد بانه ستحدث مشكلة اذا تمت عملية مشابهه لهذه :

strncpy(des_str, src_long, MAX_NAME_BUFFER+10);

مرة اخرى .

عند تخزين سلسلة حروف ضمن مصفوفة للحروف يجب التأكد من ان المصفوفة ذات حجم كاف ٍ لتخزين اطول سلسلة حروف تتعامل معها , اذا كانت سلسلة الحروف المراد التعامل معها اطول من المصفوفة المستخدمة للتخزين فأنه سيتم تجاوز نهاية المصفوفة وتكتب بقية الحروف في المواضع التالية لعناصر المصفوفة فوق المعطيات الموجودة في الذاكرة مسبقا

يكفي استخدام الاساليب الاستفزازية واتمنى من الكل اعتبار المعرفة والفائدة الغاية المرجوه من هذا الحوار وليست التقليل من شأن الخصم .

تحياتي . . .

llback.jpg

اشهد ان لا إله إلا الله وان محمدا ً رسول الله

#66

بناء على طلب احد الاعضاء عبر الرسائل الخاصة كتبت هذا الرد .

قلت سابقا :

عند تخزين سلسلة حروف ضمن مصفوفة للحروف يجب التأكد من ان المصفوفة ذات حجم كاف ٍ لتخزين اطول سلسلة حروف تتعامل معها , اذا كانت سلسلة الحروف المراد التعامل معها اطول من المصفوفة المستخدمة للتخزين فأنه سيتم تجاوز نهاية المصفوفة وتكتب بقية الحروف في المواضع التالية لعناصر المصفوفة فوق المعطيات الموجودة في الذاكرة مسبقا

والان سأوضح ماقلت بمثال بسيط:

	char src[17] = "This is my array";
	char des[3]="";
	strcpy(des,src);
	return 0;

هذا هو المثال , سنحاول الان ان ننسخ سلسلة طولها 17 (src) في سلسلة طولها 3 (des) , عند تنفيذ البرنامج ربما لن يكون هناك اخطاء , ولكن سنحلل مالذي حدث .

على فرض ان عناوين المصفوفات كالتالي :

des = 0012ff68

src = 0012ff6c

عند تنفيذ التعليمة

	strcpy(des,src);

سيكون التالي:

سيتم تخزين الحروف T,h,i داخل العناصر الثلاثة وهي مانستطيع الوصول إليه ,

post-82612-1222062584_thumb.jpg

وسيخزن الحرفs في العنوان المجاور 0012ff6b والحرف (مسافة ) سيخزن في العنوان 0012ff6c والذي هو عنوان المصفوفة src وهكذا ستستمر عملية التخزين لتطغى على عناصر المصفوفة src.

post-82612-1222062592_thumb.jpg

لذلك السلسلة التالية (is my array [] ) ستخزن من بداية عناصر السلسلة src لتطغى على بياناتها وهذا مايفسر النتائج الغريبة التي ممكن ان تحدث عند محاولة طباعة عناصر المصفوفة src

post-82612-1222062601_thumb.jpg

********************************************************************************

**********

بالنسبة لطرق العلاج او تخطي هذه المشكلة فيمكن ذلك من خلال استخدام Exception Handling مثل الحجز الديناميكي , strncpy كما في مثال الاخ سمسوما وهنا يجب ان ننوه بأنه عندها يجب مراعاة تعيين الحرف الصفري لانه من الخطأ عدم اضافة الحرف الصفري إلى الوسيط الاول للدالة strncpy عندما تكون قيمة الوسيط الثالث اقل من او يساوي طول السلسلة للوسيط الثاني .

واكرر بأن هذه الاخطاء تنتج من خلال المبرمج نفسه وتحسب عليه لان طريقة الاستخدام واضحة في جميع الكتب التعليمية وحتى في المعايير الدولية ISO وماتقدمه هذه اللغة انما هو سهولة في التعامل مع الذاكرة كميزة لها وليس عيبا ضمنيا في تصميمها .

هذا مالدي , والله اعلم .

اشكر كل من حرص على ايصال وتوضيح المعلومة , وجزيتم خيرا .

llback.jpg

اشهد ان لا إله إلا الله وان محمدا ً رسول الله

مواضيع مشابهة