سؤال

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

void myAccessFunc(bool string, void* p, size_t* len)
{
  static const char* s = "aha!";
  static const int i = 123;

  if (string) {
     *(char**)p = &s;
     *len = strlen(s);
  }
  else {
     *(int**)p = &i;
     *len = sizeof(i);
  }
}

char* s;
size_t bytes;
myAccessFunc(true,&s, &bytes);
printf("Got '%s'\n", s);

نعم، أنا أعلم أن تبدو قشرية.

ما أريد منعه هو:

char* s;
size_t bytes;
myAccessFunc(true,&s,&bytes);
s[4] = '?';

أعلم أنني لا أستطيع منع ذلك تماما ولكنني على الأقل مثل تحذير المحول البرمجي لإتهامه إلى مستخدم لا ينبغي أن يفعلوا ذلك. إذا قاموا بإلقاء مؤشراتي، فهذه هي مشكلتهم. هل هناك مزيج من CONST و VOIB و * هذا سيفعل هذا؟ حاولت شيئا مثل:

void myAccessFunc(bool string, const void** p, size_t* len);

لكنه بدا الأمر بعيدا عن الفراغ من المؤشر حتى يتعين على المتصل القيام به:

const void* p;
size_t bytes;
myAccessFunc(true, &p, &bytes);

أو

const char* s;
size_t bytes;
myAccessFunc(true, (const void**)&s, &bytes);

ولا يمكن أن تفعل:

const int * ip;
const char* s;
size_t bytes;
myAccessFunc(true, &s, &bytes);
myAccessFunc(false, &i, &bytes);

جاءت أخيرا إلى:

const void* myAccessFunc(bool string, size_t* len);

وإذا كان المستخدم يفعل:

char* p = myAcccessFunc(true,&bytes);

لا يشكو المترجم (دول مجلس التعاون الخليجي، على الأقل) من إلقاء التصفيات.

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

المحلول

سيكون من الأفضل القيام بشيء ما:

const void * myAccessFunc();

حيث تقوم بإرجاع المؤشر إلى Internals، فهذا طبيعي أكثر قليلا من تمريره كمعلمة خارجية.

إذا كنت قد تجاوزتها كمعلمة خارجية، فأنت تريد:

void myAccessFunc(const void **p);

الذي يمنعهم من القيام بذلك عن طريق الخطأ:

void *p;  /* error: must be 'const void *p;' */
myAccessFunc(&p);

نصائح أخرى

لديك شيئان يمكنك القيام به:

  • إرجاع const الفراغ *
  • اكتب بعض الوثائق ل API الخاص بك يخبر المستخدمين بعدم تعديل القيمة المرتجعة

مع ذلك، قال أي مستخدم API الذي يقرر المحاولة والتهوية بمؤشر كأنه يستحق ما يحصلون عليه: ص.

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

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

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

typedef struct _ptr_holder {
   const void* ptr;
} ptr_holder;

void myAccessFunc( bool string, ptr_holder* ptr ) {
...
}

ptr_holder s;
myAccessFunc(true,&s);
printf("Got '%s'\n", s.ptr);

انها هوكي، ولكن يجب أن تفعل الخدعة

لذلك تريد منع تعديل عبر المؤشر الذي تعيده؟ جرب هذا:

const void* myAccessFunc(bool string);

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

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

مثال:

في my_interface.h

 #ifndef __MYINTERFACE_H__
 #define __MYINTERFACE_H__

 /* The unspecified struct statement */
 typedef struct s_my_data t_my_data;

 t_my_data  *new_my_data(int someInitializer);
 void        free_my_data(t_my_data *data);
 int         enter_my_data(t_my_data *data, int someDataToEnter);

 const char *return_str_my_data(t_my_data *data);

 #endif /* __MYINTERFACE_H__ */



في my_interface.c

 #include <my_interface.h>

 /* Keep struct in .c file */
 struct s_my_data {
     int    length;
     char  *string;
 };

 t_my_data *new_my_data(int someInitializer)
 {
     /* the exercise */
 }

 void free_my_data(t_my_data *data)
 {
     /* the exercise */
 }

 int enter_my_data(t_my_data *data, int someDataToEnter)
 {
     /* the exercise */
 }

 const char *return_str_my_data(t_my_data *data)
 {
     /* the exercise */
 }

هل هذا C أو C ++؟ لماذا في حالة int هي تمرير المؤشر إلى ثابت ثابت؟ الواجهة مشبوهة.

الزائد والتخلص من المنطقي في الواجهة:

void myAccessFunc( const char* * const p, size_t* len){
   *p = "blahblahblah";
   *len = 12;
}

void myAccessFunc( const int* * const ppI, size_t* len){
   static const int i = 123;
   *ppI = &i;
   *len = 4;
}

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

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

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