سؤال

في أي جزء (.BSS، .DATA، وغيرها) من الملف القابل للتنفيذ يتم تخزين المتغيرات الثابتة بحيث لا يكون هناك تضارب في الأسماء؟على سبيل المثال:


foo.c:                         bar.c:
static int foo = 1;            static int foo = 10;
void fooTest() {               void barTest() {
  static int bar = 2;            static int bar = 20;
  foo++;                         foo++;
  bar++;                         bar++;
  printf("%d,%d", foo, bar);     printf("%d, %d", foo, bar);
}                              }

إذا قمت بتجميع كلا الملفين وربطهما بملف رئيسي يستدعي fooTest() وbarTest بشكل متكرر، فستزداد عبارات printf بشكل مستقل.يبدو الأمر منطقيًا نظرًا لأن متغيرات foo وbar محلية لوحدة الترجمة.

ولكن أين يتم تخصيص التخزين؟

للتوضيح، الافتراض هو أن لديك سلسلة أدوات يمكنها إخراج ملف بتنسيق ELF.وهكذا، أنا يعتقد أن هناك لديه ليكون هناك بعض المساحة المحجوزة في الملف القابل للتنفيذ لتلك المتغيرات الثابتة.
لأغراض المناقشة، لنفترض أننا نستخدم سلسلة أدوات مجلس التعاون الخليجي.

هل كانت مفيدة؟

المحلول

يعتمد المكان الذي تذهب إليه الإحصائيات الخاصة بك على ما إذا كانت كذلك صفر التهيئة أم لا. صفر التهيئة تدخل البيانات الثابتة .BSS (الحظر الذي بدأ بواسطة الرمز), ، عدم صفر التهيئة تدخل البيانات .بيانات

نصائح أخرى

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

شريحة البيانات التي تمت تهيئتها: يتم تخزين جميع البيانات العالمية والثابتة والثابتة هنا.
شريحة البيانات غير المهيأة (BSS): يتم تخزين جميع البيانات غير المهيأة في هذا الجزء.

وفيما يلي رسم تخطيطي لشرح هذا المفهوم:

enter image description here


إليك رابط جيد جدًا يشرح هذه المفاهيم:

http://www.inf.udec.cl/~leo/teoX.pdf

في الواقع، المتغير عبارة عن صف (التخزين، النطاق، النوع، العنوان، القيمة):

storage     :   where is it stored, for example data, stack, heap...
scope       :   who can see us, for example global, local...
type        :   what is our type, for example int, int*...
address     :   where are we located
value       :   what is our value

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

سيعتمد موقع تخزين البيانات على التنفيذ.

ومع ذلك فإن معنى ثابتة هو "الارتباط الداخلي".وهكذا يكون الرمز داخلي إلى وحدة الترجمة (foo.c, bar.c) ولا يمكن الرجوع إليها خارج وحدة الترجمة تلك.لذلك، لا يمكن أن يكون هناك تضارب في الأسماء.

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

يختلف استخدام الثبات داخل الدالة - فالمتغير يكون مرئيًا فقط للدالة، ولكن يتم الاحتفاظ بقيمته عبر استدعاءات تلك الدالة.

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

بعد قولي هذا، أعتقد أنه سيتم تخزينه في DATA الذي يميل إلى تهيئة المتغير.كان BSS في الأصل يرمز إلى byte-set-<something> الذي يحتوي على متغيرات لم تتم تهيئتها.

كيف تجدها بنفسك مع objdump -Sr

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

دعنا نحلل مثال Linux x86-64 ELF لرؤيته بأنفسنا:

#include <stdio.h>

int f() {
    static int i = 1;
    i++;
    return i;
}

int main() {
    printf("%d\n", f());
    printf("%d\n", f());
    return 0;
}

جمع مع:

gcc -ggdb -c main.c

قم بفك الكود باستخدام:

objdump -Sr main.o
  • -S يفك شفرة الكود مع اختلاط المصدر الأصلي
  • -r يظهر معلومات النقل

داخل فك f نحن نرى:

 static int i = 1;
 i++;
4:  8b 05 00 00 00 00       mov    0x0(%rip),%eax        # a <f+0xa>
        6: R_X86_64_PC32    .data-0x4

و ال .data-0x4 يقول أنه سوف يذهب إلى البايت الأول من .data شريحة.

ال -0x4 موجود لأننا نستخدم RIP عنونة نسبية، وبالتالي فإن %rip في التعليمات و R_X86_64_PC32.

إنه مطلوب لأن RIP يشير إلى التالي التعليمات، والتي تبدأ بعد 4 بايت 00 00 00 00 وهو ما سيتم نقله.ولقد شرحت ذلك بمزيد من التفصيل في: https://stackoverflow.com/a/30515926/895245

ثم إذا قمنا بتعديل المصدر إلى i = 1 ونقوم بنفس التحليل نستنتج أن:

  • static int i = 0 يستمر .bss
  • static int i = 1 يستمر .data

في المنطقة "العالمية والثابتة" :)

هناك العديد من مناطق الذاكرة في C++

  • كومة
  • متجر مجاني
  • كومة
  • عالمية وثابتة
  • مقدار ثابت

يرى هنا للحصول على إجابة مفصلة لسؤالك

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

ستنتقل البيانات المعلنة في وحدة التحويل البرمجي إلى ملف .BSS أو بيانات إخراج تلك الملفات.البيانات التي تمت تهيئتها في BSS، لم تتم تهيئتها في DATA.

يأتي الفرق بين البيانات الثابتة والعالمية في تضمين معلومات الرمز في الملف.يميل المترجمون إلى تضمين معلومات الرمز ولكنهم يقومون فقط بوضع علامة على المعلومات العامة على هذا النحو.

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

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

حسنًا، هذا السؤال قديم بعض الشيء، ولكن بما أنه لا أحد يشير إلى أي معلومات مفيدة:تحقق من المنشور بواسطة 'mohit12379' الذي يشرح مخزن المتغيرات الثابتة بنفس الاسم في جدول الرموز:http://www.geekinterview.com/question_details/24745

لقد قمت بتجربتها باستخدام objdump و gdb، وهذه هي النتيجة التي حصلت عليها:

(gdb) disas fooTest
Dump of assembler code for function fooTest:
   0x000000000040052d <+0>: push   %rbp
   0x000000000040052e <+1>: mov    %rsp,%rbp
   0x0000000000400531 <+4>: mov    0x200b09(%rip),%eax        # 0x601040 <foo>
   0x0000000000400537 <+10>:    add    $0x1,%eax
   0x000000000040053a <+13>:    mov    %eax,0x200b00(%rip)        # 0x601040 <foo>
   0x0000000000400540 <+19>:    mov    0x200afe(%rip),%eax        # 0x601044 <bar.2180>
   0x0000000000400546 <+25>:    add    $0x1,%eax
   0x0000000000400549 <+28>:    mov    %eax,0x200af5(%rip)        # 0x601044 <bar.2180>
   0x000000000040054f <+34>:    mov    0x200aef(%rip),%edx        # 0x601044 <bar.2180>
   0x0000000000400555 <+40>:    mov    0x200ae5(%rip),%eax        # 0x601040 <foo>
   0x000000000040055b <+46>:    mov    %eax,%esi
   0x000000000040055d <+48>:    mov    $0x400654,%edi
   0x0000000000400562 <+53>:    mov    $0x0,%eax
   0x0000000000400567 <+58>:    callq  0x400410 <printf@plt>
   0x000000000040056c <+63>:    pop    %rbp
   0x000000000040056d <+64>:    retq   
End of assembler dump.

(gdb) disas barTest
Dump of assembler code for function barTest:
   0x000000000040056e <+0>: push   %rbp
   0x000000000040056f <+1>: mov    %rsp,%rbp
   0x0000000000400572 <+4>: mov    0x200ad0(%rip),%eax        # 0x601048 <foo>
   0x0000000000400578 <+10>:    add    $0x1,%eax
   0x000000000040057b <+13>:    mov    %eax,0x200ac7(%rip)        # 0x601048 <foo>
   0x0000000000400581 <+19>:    mov    0x200ac5(%rip),%eax        # 0x60104c <bar.2180>
   0x0000000000400587 <+25>:    add    $0x1,%eax
   0x000000000040058a <+28>:    mov    %eax,0x200abc(%rip)        # 0x60104c <bar.2180>
   0x0000000000400590 <+34>:    mov    0x200ab6(%rip),%edx        # 0x60104c <bar.2180>
   0x0000000000400596 <+40>:    mov    0x200aac(%rip),%eax        # 0x601048 <foo>
   0x000000000040059c <+46>:    mov    %eax,%esi
   0x000000000040059e <+48>:    mov    $0x40065c,%edi
   0x00000000004005a3 <+53>:    mov    $0x0,%eax
   0x00000000004005a8 <+58>:    callq  0x400410 <printf@plt>
   0x00000000004005ad <+63>:    pop    %rbp
   0x00000000004005ae <+64>:    retq   
End of assembler dump.

هنا نتيجة objdump

Disassembly of section .data:

0000000000601030 <__data_start>:
    ...

0000000000601038 <__dso_handle>:
    ...

0000000000601040 <foo>:
  601040:   01 00                   add    %eax,(%rax)
    ...

0000000000601044 <bar.2180>:
  601044:   02 00                   add    (%rax),%al
    ...

0000000000601048 <foo>:
  601048:   0a 00                   or     (%rax),%al
    ...

000000000060104c <bar.2180>:
  60104c:   14 00                   adc    $0x0,%al

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

هذه هي الطريقة (سهلة الفهم):

stack, heap and static data

قد تعتمد الإجابة بشكل جيد على المترجم، لذلك ربما تريد تعديل سؤالك (أعني، حتى فكرة المقاطع ليست مفوضة من قبل ISO C أو ISO C++).على سبيل المثال، في نظام التشغيل Windows، لا يحمل الملف القابل للتنفيذ أسماء الرموز.سيتم إزاحة أحد "foo" بـ 0x100، وربما الآخر 0x2B0، ويتم تجميع التعليمات البرمجية من كلتا وحدتي الترجمة مع معرفة إزاحات foo "الخاصة بهم".

سيتم تخزينهما بشكل مستقل، ولكن إذا كنت تريد توضيح الأمر للمطورين الآخرين، فقد ترغب في تغليفهما بمساحات الأسماء.

أنت تعرف بالفعل أنه يتم تخزينه في bss (بدء الكتلة بالرمز) ويُشار إليه أيضًا باسم مقطع البيانات غير المهيأ أو في مقطع البيانات الذي تمت تهيئته.

لنأخذ مثالا بسيطا

void main(void)
{
static int i;
}

لم تتم تهيئة المتغير الثابت أعلاه، لذلك ينتقل إلى شريحة البيانات غير المهيأة (bss).

void main(void)
{
static int i=10;
}

وبالطبع تمت تهيئته بحلول 10 بحيث ينتقل إلى شريحة البيانات التي تمت تهيئتها.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top