اذهب إلى المحتوى

السؤال

نشر

السلام عليكم. لدي بعض الأسئله بخصوص ال pointers:

1. بما أن ال pointer هو فقط رمز أو عنوان يميز المتغيرات في الرام, فما فائده أسماء المتغيرات. فهي أيضا تقوم بنفس الوظيفة. و اللغه لا تسمح بتكرار تلك الاسماء. فما فائده ال pointers هنا.

2. ال pointer يحمل رقم hexa يشير لمكان ما أو رقم خانة (بايت) في الذاكرة. فمثلا لو 0x63dfaa380004 فهو يشير للبايت رقم 109812284653572 أي يشير لبايت واحد فقط. لكن المتغيرات من النوع int او short او غيره تخزن في أكثر من واحد بايت. فكيف يصل للقيمة رغم تأشيره علي بايت واحد من الأربعه أو الثمانيه.

3. في الكود التالي:

#include <stdio.h>
#include <cs50.h>
int main(void)
{
    string s = "HI!, hello"; // s[0]='H' s[1]='I' s[2]='!' s[3]='\0'

    printf("size of s[0]: %lu\n", sizeof(s[0]));
    // char is stored 1 byte
    printf("size of HI!: %lu\n", sizeof(s));
    // size of s should be 4 bytes. but why and how 8 bytes
}

حجم ال s[0] واحد بايت. اليس من المفترض ان يكون حجم ال string اربعه بايت. الكود يخرج 8 دائما. جربته علي compiler اخر و اخرج 32 ايضا دائما.

أرجو التوضيح لأنني لا أفهم. و شكرا علي مجهودكم.

Recommended Posts

  • 0
نشر

اسم المتغير ثابت  بينما المؤشر متغير وديناميكي، بمعنى هو مجرد label يضعه المترجم على مكان محدد في الذاكرة، والربط بين الاسم والمكان يتم في وقت الترجمة، وليس بإمكانك تغيير المكان الذي يشير إليه الاسم x، فسيظل يشير دائمًا إلى نفس العنوان في الذاكرة طوال فترة حياته.

بينما المؤشر هو متغير بحد ذاته، وظيفته هي تخزين عنوان ذاكرة لمتغير آخر، وبما أنّ المؤشر متغير، فباستطاعتك تغيير القيمة التي يحملها أي العنوان الذي يشير إليه في أي وقت أثناء تشغيل البرنامج.

للتوضيح، المؤشر مثل ورقة ملاحظات في يدك، تستطيع أن تكتب عليها شارع القاهرة، منزل رقم 10، ثم تمسحها وتكتب عنواناً آخر، مثل شارع النهضة، منزل رقم 55، فالورقة نفسها وهي المؤشر لم تتغير، لكن العنوان الذي تشير إليه تغير.

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

بتاريخ 2 ساعة قال Abdelrehman Elsied:

2. ال pointer يحمل رقم hexa يشير لمكان ما أو رقم خانة (بايت) في الذاكرة. فمثلا لو 0x63dfaa380004 فهو يشير للبايت رقم 109812284653572 أي يشير لبايت واحد فقط. لكن المتغيرات من النوع int او short او غيره تخزن في أكثر من واحد بايت. فكيف يصل للقيمة رغم تأشيره علي بايت واحد من الأربعه أو الثمانيه.

 

المؤشر بالفعل يخزن عنوان أول بايت فقط من المتغير، الفكرة في نوع المؤشر، أي عند تعريفه تحدد نوع البيانات التي سيشير إليها:

int* p_int;

بالتالي المترجم يعلم حجم البيانات،  حيث تلك العملية تسمى Dereferencing، بمعنى يعلم أن p_int هو مؤشر من نوع int، وبناءًا على ذلك، يذهب إلى العنوان المخزن في p_int ويقرأ 4 بايتات تالية أو حسب حجم الـ int في نظامك، ولو كان *p_double، لقرأ 8 بايتات.

أيضًا حسابات المؤشرات، فبزيادة المؤشر p_int++ أو p_int + 1، فالمترجم لا يضيف 1 إلى العنوان بل يضيف حجم النوع الذي يشير إليه، حيث p_int + 1 سينتقل إلى العنوان التالي بمقدار sizeof(int) أي 4 بايتات.

وp_double + 1 سينتقل إلى العنوان التالي بمقدار sizeof(double) أي 8 بايتات.

بتاريخ 2 ساعة قال Abdelrehman Elsied:

حجم ال s[0] واحد بايت. اليس من المفترض ان يكون حجم ال string اربعه بايت. الكود يخرج 8 دائما. جربته علي compiler اخر و اخرج 32 ايضا دائما.

 

في مكتبة CS50 وكذلك في لغة C، النوع string ليس كائن معقد مثل std::string في لغة C++، بل مجرد اسم بديل typedef لنوع char*

typedef char* string;

إذن، بكتابة التالي:
 

string s = "HI!";

فالنص HI! والذي هو في الحقيقة {'H', 'I', '!', '\0'} يتم تخزينه في مكان ما في الذاكرة في قسم للقراءة فقط، والمتغير s يتم إنشاؤه بما أن string هو char*، فالمتغير s هو مؤشر.

وقيمة المؤشر s تصبح هي عنوان أول حرف في النص، أي عنوان الحرف H، لذا عندما تسأل عن sizeof(s)، أنت لا تسأل عن حجم النص HI! بل تسأل عن حجم متغير المؤشر s نفسه.

ويظهر 8 لأنك على الأغلب تستخدم نظام تشغيل بمعمارية 64-bit، والتي بها أي عنوان ذاكرة وبالتالي أي مؤشر يحتاج إلى 64 بت لتخزينه.

64 bits / 8 bits per byte = 8 bytes.

بالنسبة لـ 32 فالرقم غريب لحجم مؤشر، لأنّ أنظمة 32-bit حجم المؤشر بها  4 بايت أي 32 بت، عامًة لا تستخدم sizeof واعتمد على دالة strlen() من مكتبة <string.h>:

#include <stdio.h>
#include <string.h>
#include <cs50.h>

int main(void)
{
    string s = "HI!"; 

    printf("Size of the pointer 's' itself: %lu\n", sizeof(s));

    printf("Length of the string content: %lu\n", strlen(s)); 
}

 

  • 0
نشر
بتاريخ 2 ساعة قال Mustafa Suleiman:

اسم المتغير ثابت  بينما المؤشر متغير وديناميكي، بمعنى هو مجرد label يضعه المترجم على مكان محدد في الذاكرة، والربط بين الاسم والمكان يتم في وقت الترجمة، وليس بإمكانك تغيير المكان الذي يشير إليه الاسم x، فسيظل يشير دائمًا إلى نفس العنوان في الذاكرة طوال فترة حياته.

بينما المؤشر هو متغير بحد ذاته، وظيفته هي تخزين عنوان ذاكرة لمتغير آخر، وبما أنّ المؤشر متغير، فباستطاعتك تغيير القيمة التي يحملها أي العنوان الذي يشير إليه في أي وقت أثناء تشغيل البرنامج.

للتوضيح، المؤشر مثل ورقة ملاحظات في يدك، تستطيع أن تكتب عليها شارع القاهرة، منزل رقم 10، ثم تمسحها وتكتب عنواناً آخر، مثل شارع النهضة، منزل رقم 55، فالورقة نفسها وهي المؤشر لم تتغير، لكن العنوان الذي تشير إليه تغير.

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

المؤشر بالفعل يخزن عنوان أول بايت فقط من المتغير، الفكرة في نوع المؤشر، أي عند تعريفه تحدد نوع البيانات التي سيشير إليها:

int* p_int;

بالتالي المترجم يعلم حجم البيانات،  حيث تلك العملية تسمى Dereferencing، بمعنى يعلم أن p_int هو مؤشر من نوع int، وبناءًا على ذلك، يذهب إلى العنوان المخزن في p_int ويقرأ 4 بايتات تالية أو حسب حجم الـ int في نظامك، ولو كان *p_double، لقرأ 8 بايتات.

أيضًا حسابات المؤشرات، فبزيادة المؤشر p_int++ أو p_int + 1، فالمترجم لا يضيف 1 إلى العنوان بل يضيف حجم النوع الذي يشير إليه، حيث p_int + 1 سينتقل إلى العنوان التالي بمقدار sizeof(int) أي 4 بايتات.

وp_double + 1 سينتقل إلى العنوان التالي بمقدار sizeof(double) أي 8 بايتات.

في مكتبة CS50 وكذلك في لغة C، النوع string ليس كائن معقد مثل std::string في لغة C++، بل مجرد اسم بديل typedef لنوع char*

typedef char* string;

إذن، بكتابة التالي:
 

string s = "HI!";

فالنص HI! والذي هو في الحقيقة {'H', 'I', '!', '\0'} يتم تخزينه في مكان ما في الذاكرة في قسم للقراءة فقط، والمتغير s يتم إنشاؤه بما أن string هو char*، فالمتغير s هو مؤشر.

وقيمة المؤشر s تصبح هي عنوان أول حرف في النص، أي عنوان الحرف H، لذا عندما تسأل عن sizeof(s)، أنت لا تسأل عن حجم النص HI! بل تسأل عن حجم متغير المؤشر s نفسه.

ويظهر 8 لأنك على الأغلب تستخدم نظام تشغيل بمعمارية 64-bit، والتي بها أي عنوان ذاكرة وبالتالي أي مؤشر يحتاج إلى 64 بت لتخزينه.

64 bits / 8 bits per byte = 8 bytes.

بالنسبة لـ 32 فالرقم غريب لحجم مؤشر، لأنّ أنظمة 32-bit حجم المؤشر بها  4 بايت أي 32 بت، عامًة لا تستخدم sizeof واعتمد على دالة strlen() من مكتبة <string.h>:

#include <stdio.h>
#include <string.h>
#include <cs50.h>

int main(void)
{
    string s = "HI!"; 

    printf("Size of the pointer 's' itself: %lu\n", sizeof(s));

    printf("Length of the string content: %lu\n", strlen(s)); 
}

 

شكرا لك.

لدي فقط سؤال أخير. لا يمكن مقارنه string ب string اخر لأن كلاهما مؤشران يحملان قيم مميزه. لكن أنظر لهاتين الصورتين. كود طبع same و اخر different.

 

q1-pic1.png

q1-pic2.png

  • 0
نشر
بتاريخ On 18‏/8‏/2025 at 09:52 قال Abdelrehman Elsied:

شكرا لك.

لدي فقط سؤال أخير. لا يمكن مقارنه string ب string اخر لأن كلاهما مؤشران يحملان قيم مميزه. لكن أنظر لهاتين الصورتين. كود طبع same و اخر different.في

في الصورة الأولى يتم طباعة "same" لأن المترجم يقوم بما يسمى String Interning لتوفير الذاكرة.

حيث أن السلسلتين "hi" متطابقتين حرفيا فيخزنها المترجم في نفس العنوان و i و j يشيران إلى نفس العنوان في الذاكرة.

لذلك ستجد أن i == j يعيد true .

أما في الصورة الثانية يتم طباعة "different" لأن argv[1] و argv[2] يأتيان من مدخلات المستخدم في سطر الأوامر وحتى لو أدخل المستخدم نفس النص فإن النظام يخصص مساحة ذاكرة منفصلة لكل وسيط و بذلك فإن argv[1] و argv[2] يشيران إلى عناوين ذاكرة مختلفة.

لهذا فإن argv[1] == argv[2] يعيد false

انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أجب على هذا السؤال...

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.

  • إعلانات

  • تابعنا على



×
×
  • أضف...