سؤال

أود أن أعلم البنى التي تنتهك الافتراضات لقد أدرجت أدناه. أيضًا ، أود أن أعرف ما إذا كانت أي من الافتراضات خاطئة لجميع البنى (أي إذا كان أي منها مخطئًا تمامًا).

  1. sizeof (int *) == sizeof (char *) == sizeof (void *) == sizeof (func_ptr *)

  2. التمثيل في الذاكرة لجميع المؤشرات للهندسة المعمارية هو نفسه بغض النظر عن نوع البيانات المشار إليها.

  3. إن التمثيل في الذاكرة للمؤشر هو نفس عدد صحيح من نفس البت مثل البنية.

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

  5. يمكن إلقاء جميع قيم المؤشر إلى عدد صحيح واحد. بمعنى آخر ، ما هي البنى التي لا تزال تستفيد من الأجزاء والتعويضات؟

  6. زيادة المؤشر مكافئًا للإضافة sizeof(the pointed data type) إلى عنوان الذاكرة المخزنة بواسطة المؤشر. إذا p هو int32* ومن بعد p+1 يساوي عنوان الذاكرة 4 بايت بعد p.

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

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

المحلول

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

sizeof(int *) == sizeof(char *) == sizeof(void *) == sizeof(func_ptr *)

لا أعرف أي أنظمة حيث أعرف هذا ليكون خطأ ، ولكن النظر في:

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

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

التمثيل في الذاكرة لجميع المؤشرات للهندسة المعمارية هو نفسه بغض النظر عن نوع البيانات المشار إليها.

فكر في المؤشرات الأعضاء مقابل المؤشرات العادية. ليس لديهم نفس التمثيل (أو الحجم). يتكون مؤشر عضو من this مؤشر وإزاحة.

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

إن التمثيل في الذاكرة للمؤشر هو نفس عدد صحيح من نفس البت مثل البنية.

يعتمد على كيفية تعريف طول البت. :) int على العديد من المنصات 64 بت لا تزال 32 بت. لكن المؤشر هو 64 بت. كما قيل سابقًا ، فإن وحدة المعالجة المركزية مع نموذج ذاكرة مجزأة سيكون لها مؤشرات تتكون من زوج من الأرقام. وبالمثل ، تتكون مؤشرات الأعضاء من زوج من الأرقام.

لا يحظر على المترجم الضرب وتقسيم أنواع بيانات المؤشر إلا.

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

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

يمكن إلقاء جميع قيم المؤشر إلى عدد صحيح واحد.

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

زيادة المؤشر مكافئ لإضافة حجم (نوع البيانات المدببة) إلى عنوان الذاكرة المخزنة بواسطة المؤشر.

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

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

أخيرًا ، كما ذكرت في تعليق على سؤالك ، ضع في اعتبارك أن C ++ مجرد لغة. لا يهتم بالهندسة المعمارية التي تم تجميعها. قد تبدو العديد من هذه القيود غامضة على وحدة المعالجة المركزية الحديثة. ولكن ماذا لو كنت تستهدف وحدة المعالجة المركزية الخاصة بـ Yesteryear؟ ماذا لو كنت تستهدف وحدة المعالجة المركزية في العقد القادم؟ أنت لا تعرف حتى كيف ستعمل ، لذلك لا يمكنك تحمل الكثير عنهم. ماذا لو كنت تستهدف جهازًا افتراضيًا؟ توجد بالفعل المترجمين التي تولد رمز Bytecode لـ Flash ، جاهزًا للتشغيل من موقع ويب. ماذا لو كنت تريد تجميع C ++ الخاص بك إلى رمز مصدر Python؟

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

نصائح أخرى

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

  1. غير مطلوب من قبل المعيار (انظر هذا السؤال). على سبيل المثال، sizeof(int*) يمكن أن يكون غير متكافئ size(double*). void* مضمون لتكون قادرا على تخزين أي قيمة المؤشر.
  2. غير مطلوب من قبل المعيار. بحكم التعريف ، الحجم هو جزء من التمثيل. إذا كان الحجم يمكن أن يكون مختلفًا ، فقد يكون التمثيل مختلفًا أيضًا.
  3. ليس بالضرورة. في الواقع ، "طول البتة للهندسة المعمارية" هو بيان غامض. ما هو معالج 64 بت ، حقا؟ هل هي حافلة العنوان؟ حجم السجلات؟ مركبة البيانات؟ ماذا؟
  4. ليس من المنطقي "الضرب" أو "تقسيم" مؤشر. يحظر المترجم ، ولكن يمكنك بالطبع مضاعفة أو تقسيم التمثيل الأساسي (الذي لا معنى لي حقًا) وهذا يؤدي إلى سلوك غير محدد.
  5. ربما لا أفهم وجهة نظرك ولكن كل شىء في الكمبيوتر الرقمي هو مجرد نوع من العدد الثنائي.
  6. نعم؛ نوعا من. من المضمون الإشارة إلى موقع sizeof(pointer_type) أبعد. لا يعادل بالضرورة إضافة رقم الحساب (أي أبعد هو مفهوم منطقي هنا. التمثيل الفعلي هو الهندسة المعمارية محددة)

ل 6: المؤشر ليس بالضرورة عنوان ذاكرة. انظر على سبيل المثال "مؤامرة المؤشر العظيم"بواسطة stack overflow user جالف:

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

و:

المؤشر ليس عنوان ذاكرة! ذكرت هذا أعلاه ، ولكن دعنا نقول ذلك مرة أخرى. عادة ما يتم تنفيذ المؤشرات من قبل المترجم ببساطة كعناوين الذاكرة ، نعم ، ولكن لا يجب أن تكون ".

بعض المعلومات الإضافية حول مؤشرات من معيار C99:

  • 6.2.5 § 27 تضمن ذلك void* و char* لديهم تمثيلات متطابقة ، أي يمكن استخدامها بشكل متداول دون تحويل ، أي نفس العنوان يشير إليه نفس نمط البت (الذي لا يجب أن يكون صحيحًا لأنواع المؤشرات الأخرى)
  • 6.3.2.3 §1 ينص على أنه يمكن إلقاء أي مؤشر على نوع غير مكتمل أو كائن (ومن) void* والعودة مرة أخرى وما زالت صالحة ؛ هذا لا يشمل مؤشرات الوظيفة!
  • 6.3.2.3 §6 تنص على ذلك void* يمكن إلقاؤها إلى (ومن) الأعداد الصحيحة و 7.18.1.4 §1 توفر أنواعًا مناسبة intptr_t و uintptr_t; ؛ المشكلة: هذه الأنواع اختيارية - يذكر المعيار صراحة أنه لا يجب أن يكون هناك نوع عدد صحيح كبير بما يكفي ليحمل قيمة المؤشر!

sizeof(char*) != sizeof(void(*)(void) ؟ - ليس على X86 في وضع معالجة 36 بت (معتمدة على كل وحدة المعالجة المركزية Intel إلى حد كبير منذ Pentium 1)

"التمثيل في الذاكرة للمؤشر هو نفس عدد صحيح من نفس طول البت"-لا يوجد تمثيل في الذاكرة على أي بنية حديثة ؛ لم يتم اكتشاف الذاكرة الموسومة مطلقًا وكانت قديمة بالفعل قبل توحيد C. في الواقع ، لا تحتوي الذاكرة على أعداد صحيحة ، بل مجرد أجزاء وكلمات يمكن القول (وليس البايتات ؛ معظم الذاكرة الفعلية لا تسمح لك بقراءة 8 بتات فقط.)

"ضرب المؤشرات أمر مستحيل" - 68000 عائلة ؛ لم تدعم سجلات العنوان (تلك الحاصلة على المؤشرات) IIRC.

"يمكن إلقاء جميع المؤشرات على الأعداد الصحيحة" - وليس على الصور.

"زيادة t* تعادل إضافة حجم (t) إلى عنوان الذاكرة" - صحيح بحكم التعريف. كما يعادل &pointer[1].

لا أعرف عن الآخرين ، ولكن بالنسبة إلى DOS ، فإن الافتراض في #3 غير صحيح. DOS هو 16 بت وتستخدم الحيل المختلفة لتعيين أكثر من 16 بت من الذاكرة.

إن التمثيل في الذاكرة للمؤشر هو نفس عدد صحيح من نفس البت مثل البنية.

أعتقد أن هذا الافتراض غير صحيح لأنه في 80186 ، على سبيل المثال ، يتم عقد مؤشر 32 بت في سجلين (سجل إزاحة في سجل قطاع) ، والتي ذهبت فيها نصف الكلمات التي تسجلها الأمور أثناء الوصول.

لا يحظر على المترجم الضرب وتقسيم أنواع بيانات المؤشر إلا.

لا يمكنك ضرب أنواع أو تقسيمها. ؛ ص

لست متأكدًا من سبب رغبتك في مضاعفة أو تقسيم مؤشر.

يمكن إلقاء جميع قيم المؤشر إلى عدد صحيح واحد. بمعنى آخر ، ما هي البنى التي لا تزال تستفيد من الأجزاء والتعويضات؟

يسمح معيار C99 بتخزين المؤشرات intptr_t, ، وهو نوع عدد صحيح. لذا ، نعم.

زيادة المؤشر مكافئ لإضافة حجم (نوع البيانات المدببة) إلى عنوان الذاكرة المخزنة بواسطة المؤشر. إذا كان P int32* ، فإن P+1 يساوي عنوان الذاكرة 4 بايت بعد ص.

x + y أين x هو T * و y هل عدد صحيح ملزم (T *)((intptr_t)x + y * sizeof(T)) بقدر ما أعرف. قد تكون المحاذاة مشكلة ، ولكن قد يتم توفير الحشو في sizeof. أنا غير متأكد.

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

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

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

خاصة:

  1. التمثيل في الذاكرة لجميع المؤشرات للهندسة المعمارية هو نفسه بغض النظر عن نوع البيانات المشار إليها. صحيح باستثناء التصميمات السابقة الغريبة للغاية التي حاولت تنفيذ الحماية ليس بلغات قوية ولكن في الأجهزة.
  2. إن التمثيل في الذاكرة للمؤشر هو نفس عدد صحيح من نفس البت مثل البنية. ربما ، بالتأكيد نوع من النوع المتكامل هو نفسه ، انظر LP64 مقابل LLP64.
  3. لا يحظر على المترجم الضرب وتقسيم أنواع بيانات المؤشر إلا. الصحيح.
  4. يمكن إلقاء جميع قيم المؤشر إلى عدد صحيح واحد. بمعنى آخر ، ما هي البنى التي لا تزال تستفيد من الأجزاء والتعويضات؟ لا شيء يستخدم الأجزاء والتعويضات اليوم ، ولكن ج int غالبًا ما لا تكون كبيرة بما يكفي ، فقد تحتاج إلى ملف long أو long long لعقد مؤشر.
  5. زيادة المؤشر مكافئ لإضافة حجم (نوع البيانات المدببة) إلى عنوان الذاكرة المخزنة بواسطة المؤشر. إذا كان P int32* ، فإن P+1 يساوي عنوان الذاكرة 4 بايت بعد ص. نعم.

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

كان هناك الكثير من البنى "التي تم تناولها" في الخمسينيات والستينيات والسبعينيات. لكنني لا أستطيع أن أتذكر أي أمثلة رئيسية تحتوي على برنامج التحويل البرمجي C. أتذكر ICL / Three Rivers Perq Machines في الثمانينيات من القرن الماضي ، تم تناول الكلمة وكان لديه متجر تحكم قابل للكتابة (رمز صغير). تحتوي إحدى مثيلاتها على مترجم C ونكهة UNIX تسمى PNX, ، لكن برنامج التحويل البرمجي C يتطلب رمزًا صغيرًا خاصًا.

المشكلة الأساسية هي أن أنواع char* على آلات المعالجة الكلمات محرجة ، ولكنك تنفذها. غالبا ما تكون مع sizeof(int *) != sizeof(char *) ...

ومن المثير للاهتمام ، قبل C كانت هناك لغة تسمى BCPL حيث كان نوع المؤشر الأساسي عنوان كلمة ؛ أي أن زيادة المؤشر أعطاك عنوان الكلمة التالية ، و ptr!1 أعطاك الكلمة في ptr + 1. كان هناك مشغل مختلف لمعالجة بايت: ptr%42 إذا كنت أتذكر.

تحرير: لا تجيب على الأسئلة عندما يكون السكر في الدم منخفضًا. عقلك (بالتأكيد ، لي) لا يعمل كما تتوقع. :-(

Nitpick الصغرى:

P هو int32* ثم p+1

خطأ ، يجب أن يكون غير موقّع int32 ، وإلا فإنه سوف يلف بسرعة 2 جيجابايت.

من المثير للاهتمام - لقد حصلت على هذا من مؤلف برنامج التحويل البرمجي C لشراقة Transputer - أخبرني أنه لهذا المترجم ، تم تعريف Null على أنه -2 جيجابايت. لماذا ا؟ لأن Transputer كان لديه نطاق عنوان موقّع: -2 جيجابايت إلى +2 جيجابايت. هل يمكنك أن تصدق ذلك؟ مذهل أليس كذلك؟

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

أعتقد أن معظمنا يمكن أن يكون سعيدًا لأننا لا نعمل على العروض!

I would like to know architectures which violate the assumptions I've listed below.

I see that Stephen C mentioned PERQ machines, and MSalters mentioned 68000s and PICs.

I'm disappointed that no one else actually answered the question by naming any of the weird and wonderful architectures that have standards-compliant C compilers that don't fit certain unwarranted assumptions.

sizeof(int *) == sizeof(char *) == sizeof(void *) == sizeof(func_ptr *) ?

Not necessarily. Some examples:

Most compilers for Harvard-architecture 8-bit processors -- PIC and 8051 and M8C -- make sizeof(int *) == sizeof(char *), but different from the sizeof(func_ptr *).

Some of the very small chips in those families have 256 bytes of RAM (or less) but several kilobytes of PROGMEM (Flash or ROM), so compilers often make sizeof(int *) == sizeof(char *) equal to 1 (a single 8-bit byte), but sizeof(func_ptr *) equal to 2 (two 8-bit bytes).

Compilers for many of the larger chips in those families with a few kilobytes of RAM and 128 or so kilobytes of PROGMEM make sizeof(int *) == sizeof(char *) equal to 2 (two 8-bit bytes), but sizeof(func_ptr *) equal to 3 (three 8-bit bytes).

A few Harvard-architecture chips can store exactly a full 2^16 ("64KByte") of PROGMEM (Flash or ROM), and another 2^16 ("64KByte") of RAM + memory-mapped I/O. The compilers for such a chip make sizeof(func_ptr *) always be 2 (two bytes); but often have a way to make the other kinds of pointers sizeof(int *) == sizeof(char *) == sizeof(void *) into a a "long ptr" 3-byte generic pointer that has the extra magic bit that indicates whether that pointer points into RAM or PROGMEM. (That's the kind of pointer you need to pass to a "print_text_to_the_LCD()" function when you call that function from many different subroutines, sometimes with the address of a variable string in buffer that could be anywhere in RAM, and other times with one of many constant strings that could be anywhere in PROGMEM). Such compilers often have special keywords ("short" or "near", "long" or "far") to let programmers specifically indicate three different kinds of char pointers in the same program -- constant strings that only need 2 bytes to indicate where in PROGMEM they are located, non-constant strings that only need 2 bytes to indicate where in RAM they are located, and the kind of 3-byte pointers that "print_text_to_the_LCD()" accepts.

Most computers built in the 1950s and 1960s use a 36-bit word length or an 18-bit word length, with an 18-bit (or less) address bus. I hear that C compilers for such computers often use 9-bit bytes, with sizeof(int *) == sizeof(func_ptr *) = 2 which gives 18 bits, since all integers and functions have to be word-aligned; but sizeof(char *) == sizeof(void *) == 4 to take advantage of special PDP-10 instructions that store such pointers in a full 36-bit word. That full 36-bit word includes a 18-bit word address, and a few more bits in the other 18-bits that (among other things) indicate the bit position of the pointed-to character within that word.

The in-memory representation of all pointers for a given architecture is the same regardless of the data type pointed to?

Not necessarily. Some examples:

On any one of the architectures I mentioned above, pointers come in different sizes. So how could they possibly have "the same" representation?

Some compilers on some systems use "descriptors" to implement character pointers and other kinds of pointers. Such a descriptor is different for a pointer pointing to the first "char" in a "char big_array[4000]" than for a pointer pointing to the first "char" in a "char small_array[10]", which are arguably different data types, even when the small array happens to start at exactly the same location in memory previously occupied by the big array. Descriptors allow such machines to catch and trap the buffer overflows that cause such problems on other machines.

The "Low-Fat Pointers" used in the SAFElite and similar "soft processors" have analogous "extra information" about the size of the buffer that the pointer points into. Low-Fat pointers have the same advantage of catching and trapping buffer overflows.

The in-memory representation of a pointer is the same as an integer of the same bit length as the architecture?

Not necessarily. Some examples:

In "tagged architecture" machines, each word of memory has some bits that indicate whether that word is an integer, or a pointer, or something else. With such machines, looking at the tag bits would tell you whether that word was an integer or a pointer.

I hear that Nova minicomputers have an "indirection bit" in each word which inspired "indirect threaded code". It sounds like storing an integer clears that bit, while storing a pointer sets that bit.

Multiplication and division of pointer data types are only forbidden by the compiler. NOTE: Yes, I know this is nonsensical. What I mean is - is there hardware support to forbid this incorrect usage?

Yes, some hardware doesn't directly support such operations.

As others have already mentioned, the "multiply" instruction in the 68000 and the 6809 only work with (some) "data registers"; they can't be directly applied to values in "address registers". (It would be pretty easy for a compiler to work around such restrictions -- to MOV those values from an address register to the appropriate data register, and then use MUL).

All pointer values can be casted to a single data type?

Yes.

In order for memcpy() to work right, the C standard mandates that every pointer value of every kind can be cast to a void pointer ("void *").

The compiler is required to make this work, even for architectures that still use segments and offsets.

All pointer values can be casted to a single integer? In other words, what architectures still make use of segments and offsets?

I'm not sure.

I suspect that all pointer values can be cast to the "size_t" and "ptrdiff_t" integral data types defined in "<stddef.h>".

Incrementing a pointer is equivalent to adding sizeof(the pointed data type) to the memory address stored by the pointer. If p is an int32* then p+1 is equal to the memory address 4 bytes after p.

It is unclear what you are asking here.

Q: If I have an array of some kind of structure or primitive data type (for example, a "#include <stdint.h> ... int32_t example_array[1000]; ..."), and I increment a pointer that points into that array (for example, "int32_t p = &example_array[99]; ... p++; ..."), does the pointer now point to the very next consecutive member of that array, which is sizeof(the pointed data type) bytes further along in memory?

A: Yes, the compiler must make the pointer, after incrementing it once, point at the next independent consecutive int32_t in the array, sizeof(the pointed data type) bytes further along in memory, in order to be standards compliant.

Q: So, if p is an int32* , then p+1 is equal to the memory address 4 bytes after p?

A: When sizeof( int32_t ) is actually equal to 4, yes. Otherwise, such as for certain word-addressable machines including some modern DSPs where sizeof( int32_t ) may equal 2 or even 1, then p+1 is equal to the memory address 2 or even 1 "C bytes" after p.

Q: So if I take the pointer, and cast it into an "int" ...

A: One type of "All the world's a VAX heresy".

Q: ... and then cast that "int" back into a pointer ...

A: Another type of "All the world's a VAX heresy".

Q: So if I take the pointer p which is a pointer to an int32_t, and cast it into some integral type that is plenty big enough to contain the pointer, and then add sizeof( int32_t ) to that integral type, and then later cast that integral type back into a pointer -- when I do all that, the resulting pointer is equal to p+1?

Not necessarily.

Lots of DSPs and a few other modern chips have word-oriented addressing, rather than the byte-oriented processing used by 8-bit chips.

Some of the C compilers for such chips cram 2 characters into each word, but it takes 2 such words to hold a int32_t -- so they report that sizeof( int32_t ) is 4. (I've heard rumors that there's a C compiler for the 24-bit Motorola 56000 that does this).

The compiler is required to arrange things such that doing "p++" with a pointer to an int32_t increments the pointer to the next int32_t value. There are several ways for the compiler to do that.

One standards-compliant way is to store each pointer to a int32_t as a "native word address". Because it takes 2 words to hold a single int32_t value, the C compiler compiles "int32_t * p; ... p++" into some assembly language that increments that pointer value by 2. On the other hand, if that one does "int32_t * p; ... int x = (int)p; x += sizeof( int32_t ); p = (int32_t *)x;", that C compiler for the 56000 will likely compile it to assembly language that increments the pointer value by 4.

I'm most used to pointers being used in a contiguous, virtual memory space.

Several PIC and 8086 and other systems have non-contiguous RAM -- a few blocks of RAM at addresses that "made the hardware simpler". With memory-mapped I/O or nothing at all attached to the gaps in address space between those blocks.

It's even more awkward than it sounds.

In some cases -- such as with the bit-banding hardware used to avoid problems caused by read-modify-write -- the exact same bit in RAM can be read or written using 2 or more different addresses.

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