জিসিসির __অনেকটি __ ((প্যাকড)) / # প্রগমা প্যাকটি কি নিরাপদ?


164

সি-তে, কম্পাইলার কোনও কাঠামোর সদস্যদের তাদের ক্রম অনুযায়ী সাজিয়ে রাখবে যাতে সদস্যদের মধ্যে সম্ভাব্য প্যাডিং বাইটগুলি lastোকানো হয় বা সর্বশেষ সদস্যের পরে, যাতে প্রতিটি সদস্যের সঠিকভাবে বিন্যস্ত হয় তা নিশ্চিত করা যায়।

জিসিসি একটি ভাষা এক্সটেনশন সরবরাহ করে, __attribute__((packed))যা সংকলককে প্যাডিং inোকানো না বলে স্ট্রাক্ট সদস্যদের বিভ্রান্ত করার অনুমতি দেয়। উদাহরণস্বরূপ, যদি সিস্টেমটিতে সাধারণত int4-বাইট প্রান্তিককরণের জন্য সমস্ত বস্তুর প্রয়োজন হয়,__attribute__((packed)) হতে পারে intstruct হয় সদস্যদের বিজোড় অফসেট এ বরাদ্দ করা হয়।

সিসি ডকুমেন্টেশন উদ্ধৃত:

'প্যাকড' বৈশিষ্ট্যটি উল্লেখ করে যে কোনও ভেরিয়েবল বা কাঠামো ক্ষেত্রে সর্বনিম্ন সম্ভব প্রান্তিককরণ হওয়া উচিত - ভেরিয়েবলের জন্য একটি বাইট এবং একটি ক্ষেত্রের জন্য একটি বিট, যদি না আপনি `সারিবদ্ধ 'বৈশিষ্ট্যের সাথে আরও বড় মান নির্দিষ্ট করেন।

স্পষ্টতই এই এক্সটেনশনের ব্যবহারের ফলে অল্প ডেটা প্রয়োজনীয়তা হতে পারে তবে ধীর কোড হতে পারে, কারণ সংকলককে অবশ্যই (কিছু প্ল্যাটফর্মগুলিতে) একবারে ভুল বিযুক্ত সদস্যকে অ্যাক্সেস করতে কোড তৈরি করতে হবে।

তবে এমন কোনও মামলা আছে যেখানে এটি অনিরাপদ? সংকলক সর্বদা প্যাকড স্ট্রাক্টের ভুলভ্রষ্ট সদস্যদের অ্যাক্সেসের জন্য সঠিক (ধীর হলেও) কোড উত্পন্ন করে? এমনকি এটি কি সব ক্ষেত্রেই করা সম্ভব?


1
পিসিটার অ্যাসাইনমেন্টে সতর্কতা সংযোজন (এবং সতর্কতাটি অক্ষম করার একটি বিকল্প) দিয়ে এখন গিসি বাগ রিপোর্টটি ফিক্সড হিসাবে চিহ্নিত করা হয়েছে। আমার উত্তরে বিশদ ।
কিথ থমসন

উত্তর:


148

হ্যাঁ, __attribute__((packed))কিছু সিস্টেমে সম্ভাব্য অনিরাপদ। লক্ষণটি সম্ভবত কোনও x86-এ প্রদর্শিত হবে না, যা সমস্যাটিকে আরও কুখ্যাত করে তোলে; x86 সিস্টেমে পরীক্ষা করা সমস্যা প্রকাশ করবে না। (X86-তে, ভুলভাবে স্বাক্ষরিত অ্যাক্সেসগুলি হার্ডওয়ারে পরিচালনা করা হয়;int* পয়েন্টারটিকে কোনও নির্দিষ্ট ঠিকানা নির্দেশ করে তবে এটি সঠিকভাবে সংযুক্ত থাকলে কিছুটা ধীর হবে তবে আপনি সঠিক ফলাফল পাবেন))

কিছু অন্যান্য সিস্টেমে যেমন স্পার্ক, একটি বিভ্রান্তিকৃত intবস্তু অ্যাক্সেস করার চেষ্টা করার ফলে প্রোগ্রামটি ক্র্যাশ হয়ে একটি বাস ত্রুটির সৃষ্টি করে।

এমন সিস্টেমগুলিও রয়েছে যেখানে একটি বিভ্রান্তিকৃত অ্যাক্সেস চুপচাপভাবে ঠিকানার নীচের অর্ডারের বিটগুলিকে উপেক্ষা করে, যার ফলে এটি মেমরির ভুল অংশে অ্যাক্সেস করে।

নিম্নলিখিত প্রোগ্রাম বিবেচনা করুন:

#include <stdio.h>
#include <stddef.h>
int main(void)
{
    struct foo {
        char c;
        int x;
    } __attribute__((packed));
    struct foo arr[2] = { { 'a', 10 }, {'b', 20 } };
    int *p0 = &arr[0].x;
    int *p1 = &arr[1].x;
    printf("sizeof(struct foo)      = %d\n", (int)sizeof(struct foo));
    printf("offsetof(struct foo, c) = %d\n", (int)offsetof(struct foo, c));
    printf("offsetof(struct foo, x) = %d\n", (int)offsetof(struct foo, x));
    printf("arr[0].x = %d\n", arr[0].x);
    printf("arr[1].x = %d\n", arr[1].x);
    printf("p0 = %p\n", (void*)p0);
    printf("p1 = %p\n", (void*)p1);
    printf("*p0 = %d\n", *p0);
    printf("*p1 = %d\n", *p1);
    return 0;
}

জিসিসি 4.5.2 সহ এক্স 86 উবুন্টুতে, এটি নিম্নলিখিত আউটপুট উত্পাদন করে:

sizeof(struct foo)      = 5
offsetof(struct foo, c) = 0
offsetof(struct foo, x) = 1
arr[0].x = 10
arr[1].x = 20
p0 = 0xbffc104f
p1 = 0xbffc1054
*p0 = 10
*p1 = 20

জিপিসি 4.5.1 সহ স্পার্ক সোলারিস 9 এ এটি নিম্নলিখিত উত্পাদন করে:

sizeof(struct foo)      = 5
offsetof(struct foo, c) = 0
offsetof(struct foo, x) = 1
arr[0].x = 10
arr[1].x = 20
p0 = ffbff317
p1 = ffbff31c
Bus error

উভয় ক্ষেত্রেই, প্রোগ্রামটি কোনও অতিরিক্ত বিকল্প ছাড়াই সংকলিত হয়েছে, ঠিক নেই gcc packed.c -o packed

(একটি প্রোগ্রাম যা অ্যারের পরিবর্তে একটি একক কাঠামো ব্যবহার করে নির্ভরযোগ্যভাবে সমস্যাটি প্রদর্শন করে না, যেহেতু সংকলকটি কাঠামোটটিকে একটি বিজোড় ঠিকানায় বরাদ্দ করতে পারে যাতে xসদস্যটি সঠিকভাবে সংযুক্ত থাকে। দুটি struct fooঅবজেক্টের অ্যারে সহ , কমপক্ষে একটি বা অন্যটি একটি বিভ্রান্ত xসদস্য থাকবে।)

(এই ক্ষেত্রে, p0একটি শ্রেণীবদ্ধ ঠিকানায় পয়েন্ট, কারণ এটি একটি বস্তাবন্দী স্থানটিকে intএকটি নিম্নলিখিত সদস্য charসদস্য। p1সঠিকভাবে প্রান্তিককৃত করতে হবে, যেহেতু এটি অ্যারের দ্বিতীয় উপাদান একই সদস্যের কাছে পয়েন্ট ঘটবে, তাই দুটি charএটা পূর্ববর্তী বস্তু - এবং SPARC সোলারিস এ অ্যারেarr এমন একটি ঠিকানায় বরাদ্দ করা হয়েছে যা 4 এর একাধিক নয়))

নামের দ্বারা xকোনও সদস্যকে উল্লেখ করার সময় struct foo, সংকলকটি জানে যে xএটি সম্ভাব্যভাবে বিভ্রান্ত হয়েছে এবং এটি সঠিকভাবে অ্যাক্সেস করার জন্য অতিরিক্ত কোড তৈরি করবে।

একবার কোনও পয়েন্টার অবজেক্টে ঠিকানা arr[0].xবা arr[1].xসংরক্ষণ করা হয়ে গেলে , সংকলক বা চলমান প্রোগ্রামটি কেউই জানে না যে এটি একটি বিভ্রান্তিকর intবস্তুটিকে নির্দেশ করে। এটি কেবল ধরে নিয়েছে যে এটি যথাযথভাবে সংযুক্ত রয়েছে, যার ফলে (কিছু সিস্টেমে) একটি বাস ত্রুটি বা অন্যান্য অনুরূপ ব্যর্থতা রয়েছে।

জিসিসিতে এটি স্থির করা, আমার বিশ্বাস, অযৌক্তিক হবে। একটি সাধারণ সমাধানের প্রয়োজন হবে, প্রতিটি ত্রুটিযুক্ত অ্যালাইনমেন্ট প্রয়োজনীয়তার সাথে যে কোনও ধরণের পয়েন্টারকে ডিফারেন্স করার চেষ্টা করা হয় (ক) সংকলনের সময় প্রমাণ করে যে পয়েন্টারটি কোনও প্যাকড স্ট্রাক্টের বিভ্রান্ত সদস্যকে নির্দেশ না করে, বা (খ) বাল্কিয়ার এবং স্লো কোড উত্পন্ন করে যা প্রান্তিক বা মিস্যালাইনযুক্ত বস্তুগুলি পরিচালনা করতে পারে।

আমি একটি জিসিসি বাগ রিপোর্ট জমা দিয়েছি । যেমনটি আমি বলেছি, আমি বিশ্বাস করি না এটি ঠিক করা এটি ব্যবহারিক, তবে ডকুমেন্টেশনের মধ্যে এটি উল্লেখ করা উচিত (এটি বর্তমানে তা নয়)।

আপডেট : 2018-12-20 হিসাবে, এই বাগটি ফিক্সড হিসাবে চিহ্নিত করা হয়েছে। প্যাচটি জিসিসি 9 তে একটি নতুন -Waddress-of-packed-memberবিকল্প সংযোজন সহ ডিফল্টরূপে সক্ষম হবে।

যখন স্ট্রাক্ট বা ইউনিয়নের প্যাকড সদস্যের ঠিকানা নেওয়া হয়, এটি একটি স্বাক্ষরবিহীন পয়েন্টার মান হতে পারে। এই প্যাচটি পয়েন্টার অ্যাসাইনমেন্টে সারিবদ্ধতা পরীক্ষা করতে এবং স্বাক্ষরবিহীন ঠিকানার পাশাপাশি অ-স্বাক্ষরিত পয়েন্টারটি সতর্ক করতে -ড্রেস-অফ-প্যাক-সদস্য যোগ করে

আমি উত্স থেকে সিসিটি সংস্করণটি তৈরি করেছি। উপরের প্রোগ্রামটির জন্য, এটি এই ডায়াগোনস্টিকগুলি উত্পাদন করে:

c.c: In function main’:
c.c:10:15: warning: taking address of packed member of struct foo may result in an unaligned pointer value [-Waddress-of-packed-member]
   10 |     int *p0 = &arr[0].x;
      |               ^~~~~~~~~
c.c:11:15: warning: taking address of packed member of struct foo may result in an unaligned pointer value [-Waddress-of-packed-member]
   11 |     int *p1 = &arr[1].x;
      |               ^~~~~~~~~

1
সম্ভাব্য ভুল পথে চালিত, এবং উত্পন্ন হবে ... কি?
Almo

5
এআরএম-এ বিভক্ত কাঠামোগত উপাদানগুলি অদ্ভুত স্টাফ করে: কিছু অ্যাক্সেসের ফলে ত্রুটি হয়, অন্যরা পুনরুদ্ধার করা ডেটাটিকে স্ব-স্বজ্ঞাতভাবে পুনরায় সাজানো বা সংলগ্ন অপ্রত্যাশিত ডেটা যুক্ত করে তোলে।
wallyk

8
দেখে মনে হচ্ছে প্যাকিং নিজেই নিরাপদ তবে প্যাকড সদস্যরা কীভাবে ব্যবহার করবেন তা অনিরাপদ হতে পারে। পুরানো এআরএম-ভিত্তিক সিপিইউগুলি স্বাক্ষরবিহীন মেমরি অ্যাক্সেসগুলি সমর্থন করে না, নতুন সংস্করণগুলি করে তবে আমি জানি সিম্বিয়ান ওএস এখনও এই নতুন সংস্করণগুলিতে চলার সময় স্বাক্ষরবিহীন অ্যাক্সেসগুলি অস্বীকার করে (সমর্থনটি বন্ধ রয়েছে)।
জেমস

14
জিসিসি-র মধ্যে এটির সমাধানের আরেকটি উপায় হ'ল টাইপ সিস্টেমটি ব্যবহার করা দরকার: প্যাকড স্ট্রকের সদস্যদের পয়েন্টারগুলি কেবলমাত্র পয়েন্টারগুলিতে নির্ধারিত হতে পারে যা নিজেরাই প্যাকড হিসাবে চিহ্নিত (যেমন সম্ভাব্য স্বাক্ষরবিহীন)। তবে সত্যই: প্যাকড স্ট্রোকস, কেবল না বলুন।
ক্যাফে

9
@ ফ্লাভিয়াস: আমার মূল উদ্দেশ্য ছিল সেখানকার তথ্য পাওয়া। আরও দেখুন মেটা.স্ট্যাকেক্সেঞ্জারওয়েজড.কমিশনস
কেথ থম্পসন

62

এমস যেমন উপরে বলেছে, কোনও স্ট্রাক্টের সদস্যের কাছে কোনও পয়েন্টার নিবেন না pack এটি কেবল আগুন নিয়ে খেলছে। আপনি যখন বলেন __attribute__((__packed__))বা #pragma pack(1), আপনি যা বলছেন তা হ'ল "আরে জিসিসি, আমি সত্যিই জানি আমি কী করছি।" যখন এটি প্রমাণিত হয় যে আপনি না, আপনি ঠিকভাবে সংকলককে দোষ দিতে পারবেন না।

যদিও আমরা এটির আত্মতুষ্টির জন্য সংকলককে দোষ দিতে পারি। জিসিসি একটি আছে যদিও -Wcast-alignবিকল্প, এটি ডিফল্ট দ্বারা কিংবা সঙ্গে সক্রিয় করা হয় না -Wallবা -Wextra। এটি দৃশ্যত জিসিসি বিকাশকারীদের এই ধরণের কোডটিকে মস্তিষ্ক-মৃত " ঘৃণা " বলে সম্বোধন করার অযোগ্য হিসাবে বিবেচনা করার কারণে ঘটেছিল - বোধগম্য অপছন্দ, তবে যখন কোনও অনভিজ্ঞ প্রোগ্রামার এতে ঝাঁপিয়ে পড়ে তখন কোনও লাভ হয় না।

নিম্নোক্ত বিবেচনা কর:

struct  __attribute__((__packed__)) my_struct {
    char c;
    int i;
};

struct my_struct a = {'a', 123};
struct my_struct *b = &a;
int c = a.i;
int d = b->i;
int *e __attribute__((aligned(1))) = &a.i;
int *f = &a.i;

এখানে, প্রকারটি aএকটি প্যাকড স্ট্রাক্ট (উপরে সংজ্ঞায়িত হিসাবে)। একইভাবে, bপ্যাকড স্ট্রাক্টের পয়েন্টার। এক্সপ্রেশনটির a.iধরণটি (মূলত) 1 বাইট প্রান্তিককরণ সহ একটি আন্ত l-মান হয়। cএবং dউভয়ই সাধারণ int। পড়ার সময় a.i, সংকলকটি স্বাক্ষরবিহীন অ্যাক্সেসের জন্য কোড উত্পন্ন করে। আপনি যখন পড়েন b->i, bএর ধরণ এখনও জানে যে এটি প্যাক করা হয়েছে, সুতরাং এগুলির কোনও সমস্যা নেই। eএক-বাইট-প্রান্তিককরণের জন্য একটি পয়েন্টার, সুতরাং সংকলক সঠিকভাবে পাশাপাশি যে dereferences করতে জানেন। কিন্তু আপনি যখন অ্যাসাইনমেন্টটি করেন f = &a.i, আপনি একটি অলাইনাইড ইন্ট পয়েন্টারটির মান একটি প্রান্তিক বিন্দুতে পয়েন্টার ভেরিয়েবলের মধ্যে সংরক্ষণ করছেন - সেখানেই আপনি ভুল করেছেন। এবং আমি সম্মত হই, gcc এর দ্বারা এই সতর্কতাটি সক্ষম করা উচিতডিফল্ট (এমনকি নেই -Wallবা -Wextra)।


6
আন-সাইনড স্ট্রাক্ট সহ পয়েন্টার কীভাবে ব্যবহার করবেন তা ব্যাখ্যা করার জন্য +1!
সৌম্য

@ সৌম্য পয়েন্টের জন্য ধন্যবাদ! :) মনে রাখবেন তবে এটি __attribute__((aligned(1)))একটি সিসি এক্সটেনশন এবং পোর্টেবল নয়। আমার জ্ঞানের মতে, সিতে স্বাক্ষরবিহীন অ্যাক্সেসের একমাত্র সত্যই পোর্টেবল উপায় (কোনও সংকলক / হার্ডওয়্যার সংমিশ্রণ সহ) বাইট-ওয়াইজ মেমরি অনুলিপি (মেমকি বা অনুরূপ) সহ। কিছু হার্ডওয়্যার এমনকি স্বাক্ষরবিহীন অ্যাক্সেস জন্য নির্দেশাবলী নেই। আমার দক্ষতা বাহু এবং x86 এর সাথে রয়েছে যা উভয়ই করতে পারে, যদিও স্বাক্ষরবিহীন অ্যাক্সেসটি কম is সুতরাং যদি আপনার উচ্চতর পারফরম্যান্সের সাথে কখনও এটি করার প্রয়োজন হয় তবে আপনাকে হার্ডওয়্যারটিকে স্নিগ্ধ করতে হবে এবং খিলান-নির্দিষ্ট কৌশলগুলি ব্যবহার করতে হবে।
ড্যানিয়েল স্যান্টোস

4
@ সৌম্য দুঃখের বিষয়, __attribute__((aligned(x)))পয়েন্টারগুলির জন্য ব্যবহার করার সময় এখন তা এড়ানো হবে বলে মনে হচ্ছে। :( আমি এখনো এই পূর্ণ বিবরণ আছে না, কিন্তু ব্যবহার __builtin_assume_aligned(ptr, align)জিসিসি পেতে সঠিক কোড জেনারেট বলে মনে হয় যখন আমি আরও সংক্ষিপ্ত উত্তর (এবং আশা একটি বাগ রিপোর্ট) আমি আমার উত্তর আপডেট
ড্যানিয়েল সান্তোসের

@ ড্যানিয়েলস্যান্টোস: আমি ব্যবহার করি এমন একটি মানের সংকলক (কেইল) পয়েন্টারগুলির জন্য "প্যাকড" কোয়ালিফায়ারকে স্বীকৃতি দেয়; যদি কোনও কাঠামোটিকে "প্যাকড" হিসাবে ঘোষণা করা হয়, তবে কোনও uint32_tসদস্যের ঠিকানা নেওয়ার ফলে ফল পাওয়া যাবে uint32_t packed*; যেমন একটি পয়েন্টার থেকে যেমন পড়ার চেষ্টা কর্টেক্স-এম0 আইআইআরসি একটি সাব্রোটিন কল করবে যা পয়েন্টারটি স্বাক্ষরবিহীন থাকলে সাধারণ পড়ার ক্ষেত্রে ~ 7x ডলার লাগবে বা এটি সারিবদ্ধ থাকলে ~ 3x দীর্ঘ হবে তবে উভয় ক্ষেত্রেই ভবিষ্যদ্বাণীপূর্ণ আচরণ করবে [ইন-লাইন কোডটি প্রান্তিককরণ বা স্বাক্ষরবিহীন হোক না কেন 5x সময় লাগবে]
সুপারক্যাট


49

যতক্ষণ না আপনি সর্বদা কাঠামোর মাধ্যমে .(বিন্দু) বা স্বরলিখনের মাধ্যমে মানগুলি অ্যাক্সেস করেন ততক্ষণ এটি পুরোপুরি নিরাপদ ->

যা নিরাপদ নয় তা হ'ল স্বাক্ষরযুক্ত ডেটারের পয়েন্টার গ্রহণ করা এবং তারপরে এটিকে অ্যাকাউন্টে না নিয়েই অ্যাক্সেস করা।

এছাড়াও, স্ট্রাক্টের প্রতিটি আইটেম আনইলাইনড হিসাবে পরিচিত বলে জানা গেছে, এটি নির্দিষ্ট উপায়ে আনইলাইনাইনড হিসাবে পরিচিত , সুতরাং সামগ্রিকভাবে স্ট্রাক্টটি সংকলকটি প্রত্যাশার সাথে সংযুক্ত করতে হবে বা সমস্যা হবে (কিছু প্ল্যাটফর্মে, বা ভবিষ্যতে যদি স্বাক্ষরযুক্ত অ্যাক্সেসগুলি অনুকূল করতে কোনও নতুন উপায় উদ্ভাবিত হয়)।


হুম, আমি ভাবছি আপনি যদি একটি প্যাক স্ট্রাক্ট অন্য প্যাক স্ট্রাক্টের ভিতরে রাখেন তবে যেখানে অ্যালাইনমেন্টটি আলাদা হবে? আকর্ষণীয় প্রশ্ন, তবে এর উত্তরটি পরিবর্তন করা উচিত নয়।
এমএস

জিসিসি সর্বদা কাঠামো নিজেই বিন্যস্ত করবে না। উদাহরণস্বরূপ: স্ট্রাক্ট foo {int x; চর গ; __ ribribute __ ((প্যাকড)); কাঠামো বার {চর সি; কাঠামো foo চ; }; আমি খুঁজে পেয়েছি যে বার: চ :: এক্স কমপক্ষে এমআইপিএসের স্বাদে প্রয়োজনীয়ভাবে প্রান্তিক করা হবে না।
আন্তন

3
@ অ্যান্টম: হ্যাঁ, একটি প্যাকড স্ট্রাক্টের মধ্যে একটি কাঠামোটি আনইলাইনড থাকতে পারে, তবে, সংকলকটি প্রতিটি ক্ষেত্রের প্রান্তিককরণ কী তা জানে এবং যতক্ষণ আপনি কাঠামোর মধ্যে পয়েন্টার ব্যবহার করার চেষ্টা না করেন ততক্ষণ এটি পুরোপুরি নিরাপদ। আপনার কেবল কাঠামোর জন্য কাঠামোর মধ্যে একটি সমতল ক্ষেত্রের ক্ষেত্র হিসাবে একটি অতিরিক্ত কাঠামোর অতিরিক্ত পাঠ্যযোগ্যতার জন্য কল্পনা করা উচিত।
এএমএস

6

এই বৈশিষ্ট্যটি ব্যবহার করা অবশ্যই অনিরাপদ।

একটি বিশেষ জিনিস যা এটি ভেঙে যায় তার একটি unionএমন ক্ষমতা যার মধ্যে দুটি বা ততোধিক স্ট্রাক্ট থাকে যাতে একটি সদস্য লিখতে পারে এবং অন্যটি পড়তে হয় যদি স্ট্র্টগুলির সদস্যদের একটি সাধারণ প্রাথমিক ক্রম থাকে। সি 11 স্ট্যান্ডার্ডের 6.5.2.3 বিভাগে বলা হয়েছে:

6 এক বিশেষ গ্যারান্টি ইউনিয়ন ব্যবহারের প্রক্রিয়া সহজ করার জন্য তৈরি করা হয়: যদি কোন ইউনিয়ন বিভিন্ন কাঠামো ভাগ একটি সাধারণ প্রারম্ভিক ক্রম ধারণ করে (নীচে দেখুন), এবং যদি ইউনিয়ন বস্তুর বর্তমানে এই স্ট্রাকচার এক ধারণ করে, এটা পরিদর্শন করা অনুমতি দেওয়া হয় এগুলির যে কোনও একটিরও সাধারণ প্রাথমিক অংশ যা ইউনিয়নের সম্পূর্ণ ধরণের ঘোষণাটি দৃশ্যমান। দুটি বা কাঠামোগুলি একটি সাধারণ প্রাথমিক ক্রম ভাগ করে যদি সংশ্লিষ্ট সদস্যদের এক বা একাধিক প্রাথমিক সদস্যের অনুক্রমের জন্য সামঞ্জস্যপূর্ণ প্রকারগুলি (এবং বিট-ফিল্ডগুলির জন্য একই প্রস্থের জন্য) থাকে।

...

9 উদাহরণ 3 নিম্নলিখিতটি একটি বৈধ খণ্ড:

union {
    struct {
        int    alltypes;
    }n;
    struct {
        int    type;
        int    intnode;
    } ni;
    struct {
        int    type;
        double doublenode;
    } nf;
}u;
u.nf.type = 1;
u.nf.doublenode = 3.14;
/*
...
*/
if (u.n.alltypes == 1)
if (sin(u.nf.doublenode) == 0.0)
/*
...
*/

কখন __attribute__((packed))পরিচয় হয় এটি এটিকে ভেঙে দেয়। নিম্নলিখিত উদাহরণটি উবুন্টু 16.04 x64 এ অপ্টিমাইজেশন অক্ষম করে জিসিসি 5.4.0 ব্যবহার করে চালানো হয়েছিল:

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

struct s1
{
    short a;
    int b;
} __attribute__((packed));

struct s2
{
    short a;
    int b;
};

union su {
    struct s1 x;
    struct s2 y;
};

int main()
{
    union su s;
    s.x.a = 0x1234;
    s.x.b = 0x56789abc;

    printf("sizeof s1 = %zu, sizeof s2 = %zu\n", sizeof(struct s1), sizeof(struct s2));
    printf("s.y.a=%hx, s.y.b=%x\n", s.y.a, s.y.b);
    return 0;
}

আউটপুট:

sizeof s1 = 6, sizeof s2 = 8
s.y.a=1234, s.y.b=5678

যদিও struct s1এবং struct s2একটি "সাধারণ প্রাথমিক ক্রম" রয়েছে, প্যাকিংটির প্রাক্তনটির জন্য প্রয়োগ করা মানে সংশ্লিষ্ট সদস্যরা একই বাইট অফসেটে বাস করেন না। ফলাফলটি মেম্বারে লিখিত মান সদস্যের x.bকাছ থেকে পঠিত মানের মতো হয় না y.b, যদিও মান বলে যে তাদের একই হওয়া উচিত।


একটি যুক্তিযুক্ত হতে পারে যে আপনি যদি একটি স্ট্রাকটি প্যাক করেন এবং অন্যটি না, তবে আপনি তাদের সামঞ্জস্যপূর্ণ বিন্যাসের আশা করতে যাবেন না। তবে হ্যাঁ, এটি অন্য স্ট্যান্ডার্ড প্রয়োজনীয়তা যা এটি লঙ্ঘন করতে পারে।
কিথ থম্পসন

1

(নীচে চিত্রিত করার জন্য রান্না করা খুব কৃত্রিম উদাহরণ।) প্যাকযুক্ত স্ট্রাক্টগুলির একটি প্রধান ব্যবহার হ'ল যেখানে আপনার কাছে ডেটা স্ট্রিম রয়েছে (256 বাইট বলুন) যার কাছে আপনি অর্থ সরবরাহ করতে চান। আমি যদি আরও ছোট উদাহরণ নিই, ধরুন আমার আমারডুডিনোতে একটি প্রোগ্রাম চলছে যা সিরিয়ালের মাধ্যমে 16 বাইটের একটি প্যাকেট পাঠায় যার নীচের অর্থ রয়েছে:

0: message type (1 byte)
1: target address, MSB
2: target address, LSB
3: data (chars)
...
F: checksum (1 byte)

তাহলে আমি এরকম কিছু ঘোষণা করতে পারি

typedef struct {
  uint8_t msgType;
  uint16_t targetAddr; // may have to bswap
  uint8_t data[12];
  uint8_t checksum;
} __attribute__((packed)) myStruct;

এবং তারপরে আমি পয়েন্টার গাণিতিকের সাথে ঝাঁকুনির পরিবর্তে লক্ষ্যস্ট্রেড অ্যাড্রেট বাইটগুলি উল্লেখ করতে পারি aStruct.targetAddr এর মাধ্যমে।

প্রান্তিককরণের স্টাফগুলি ঘটার সাথে সাথে, প্রাপ্ত তথ্যগুলিতে স্মৃতিতে শূন্য * পয়েন্টার গ্রহণ এবং এটি মাইস্ট্রাক্ট * এ castালাই কাজ করবে না যতক্ষণ না সংকলক স্ট্রাক্টকে প্যাকড হিসাবে ব্যবহার করে (এটি নির্দিষ্ট ক্রমে ডেটা সঞ্চয় করে এবং সঠিকভাবে 16 ব্যবহার করে) এই উদাহরণের জন্য বাইটস)। স্বাক্ষরবিহীন পাঠকদের জন্য পারফরম্যান্সের জরিমানা রয়েছে, সুতরাং আপনার প্রোগ্রামটি সক্রিয়ভাবে যে ডেটা দিয়ে সক্রিয়ভাবে কাজ করছে তার জন্য প্যাকযুক্ত স্ট্রাক্ট ব্যবহার করা ভাল ধারণা নয় not কিন্তু যখন আপনার প্রোগ্রামটি বাইটগুলির একটি তালিকা সরবরাহ করা হয়, তখন প্যাকযুক্ত স্ট্রাক্টগুলি প্রোগ্রামগুলি লিখিতভাবে সহজ করে তোলে যা সামগ্রীগুলি অ্যাক্সেস করে।

অন্যথায় আপনি সি ++ ব্যবহার করে এবং অ্যাকসেসর পদ্ধতি এবং স্টাফ সহ একটি ক্লাস লিখেছেন যা পর্দার আড়ালে পয়েন্টার গাণিতিক করে। সংক্ষেপে, প্যাকযুক্ত স্ট্রাক্টগুলি প্যাকড ডেটাগুলির সাথে দক্ষতার সাথে ডিল করার জন্য এবং প্যাকড ডেটা আপনার প্রোগ্রামের সাথে কাজ করার জন্য দেওয়া হতে পারে। বেশিরভাগ ক্ষেত্রে, আপনার কোডটি কাঠামোর বাইরে মানগুলি পড়া উচিত, তাদের সাথে কাজ করা উচিত এবং হয়ে গেলে এগুলি আবার লিখুন। প্যাকড স্ট্রাকচারের বাইরে অন্য সব করা উচিত। সমস্যার অংশটি হ'ল নিম্ন স্তরের স্টাফ যা সি প্রোগ্রামার থেকে আড়াল করার চেষ্টা করে এবং হুপ জাম্পিংয়ের প্রয়োজন হয় যদি এই জাতীয় জিনিসগুলি যদি প্রোগ্রামারটির সাথে সত্যিই গুরুত্ব দেয় তবে। (আপনার ভাষাতে প্রায় একটি ভিন্ন 'ডেটা লেআউট'র নির্মাণের প্রয়োজন হয় যাতে আপনি বলতে পারেন যে' এই জিনিসটি 48 বাইট লম্বা, ফু 13 ​​ডাব্লু ডেটা বোঝায় এবং এভাবে ব্যাখ্যা করা উচিত '; এবং একটি পৃথক কাঠামোগত ডেটা কনস্ট্রাক্ট,


আমি যদি কিছু মিস করি না তবে এটি প্রশ্নের উত্তর দেয় না। আপনি যুক্তি দেন যে স্ট্রাকচার প্যাকিং সুবিধাজনক (যা এটি) তবে এটি নিরাপদ কিনা এই প্রশ্নটির আপনি উত্তর দিচ্ছেন না। এছাড়াও, আপনি স্বাক্ষর করেন যে স্বাক্ষরবিহীন পাঠকদের জন্য পারফরম্যান্স জরিমানা; এটি x86 এর ক্ষেত্রে সত্য, তবে সমস্ত সিস্টেমের ক্ষেত্রে নয়, যেমনটি আমি আমার উত্তরে দেখিয়েছি।
কিথ থম্পসন
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.