মিশ্রিত ডেটা টাইপ (ইনট, ফ্লোট, চর ইত্যাদি) কীভাবে অ্যারেতে সংরক্ষণ করা যায়?


145

আমি একটি অ্যারে মিশ্রিত ডেটা ধরণের সঞ্চয় করতে চাই। কেউ কীভাবে তা করতে পারে?


8
এটি সম্ভব এবং ব্যবহারের কেসগুলি রয়েছে তবে এটি সম্ভবত একটি ত্রুটিযুক্ত নকশা। এটি অ্যারেগুলির জন্য নয়।
djechlin

উত্তর:


244

আপনি অ্যারে উপাদানগুলিকে বৈষম্যমূলক ইউনিয়ন, ওরফে ট্যাগ ইউনিয়ন করতে পারেন

struct {
    enum { is_int, is_float, is_char } type;
    union {
        int ival;
        float fval;
        char cval;
    } val;
} my_array[10];

typeসদস্য পছন্দ যার সদস্য রাখা ব্যবহার করা হয় unionপ্রতিটি অ্যারের উপাদান জন্য ব্যবহার করা উচিত হয়। সুতরাং আপনি যদি intপ্রথম উপাদানটিতে একটি সঞ্চয় করতে চান তবে আপনি এটি করবেন:

my_array[0].type = is_int;
my_array[0].val.ival = 3;

আপনি যখন অ্যারের কোনও উপাদান অ্যাক্সেস করতে চান, আপনাকে প্রথমে প্রকারটি পরীক্ষা করতে হবে, তারপরে ইউনিয়নের সংশ্লিষ্ট সদস্যটি ব্যবহার করতে হবে। একটি switchবিবৃতি দরকারী:

switch (my_array[n].type) {
case is_int:
    // Do stuff for integer, using my_array[n].ival
    break;
case is_float:
    // Do stuff for float, using my_array[n].fval
    break;
case is_char:
    // Do stuff for char, using my_array[n].cvar
    break;
default:
    // Report an error, this shouldn't happen
}

typeসদস্যটি সর্বদা সঞ্চিত সর্বশেষ মানের সাথে সাদৃশ্যপূর্ণ তা নিশ্চিত করার জন্য এটি প্রোগ্রামারটির কাছে রেখে দেওয়া হয়েছে union


23
+1 এটি সি
টেক্সাসব্রুস

8
@ টেক্সাসব্রুস একটি "ট্যাগড ইউনিয়ন" নামেও পরিচিত। আমি এই কৌশলটি নিজের ভাষায়ও ব্যবহার করছি। ;)

উইকিপিডিয়া " বৈষম্যমূলক ইউনিয়ন " - সেট থিওরিতে "ডিসজেইন্ট ইউনিয়ন" এবং @ এইচ 2 সি 3 হিসাবে উল্লিখিত কম্পিউটার বিজ্ঞানে "ট্যাগড ইউনিয়ন" হিসাবে একটি বিশৃঙ্খলা পৃষ্ঠা ব্যবহার করে ।
ইজকাটা

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

1
@ বারমার আমি এটিকে "ট্যাগ ইউনিয়ন" হিসাবে পুনরায় লিখেছি কিন্তু তারপরে আপনার মন্তব্যটি পড়ুন। সম্পাদনাটি ঘূর্ণায়মান, আমি আপনার উত্তরটি ভাঙচুর করার অর্থ চাইনি।

32

ইউনিয়ন ব্যবহার করুন:

union {
    int ival;
    float fval;
    void *pval;
} array[10];

যদিও আপনাকে প্রতিটি উপাদানের ধরণটি জানতে হবে।


21

অ্যারে উপাদানগুলির একই আকার হওয়া দরকার, এ কারণেই এটি সম্ভব নয়। আপনি একটি বৈকল্পিক টাইপ তৈরি করে এটি চারপাশে কাজ করতে পারেন :

#include <stdio.h>
#define SIZE 3

typedef enum __VarType {
  V_INT,
  V_CHAR,
  V_FLOAT,
} VarType;

typedef struct __Var {
  VarType type;
  union {
    int i;
    char c;
    float f;
  };
} Var;

void var_init_int(Var *v, int i) {
  v->type = V_INT;
  v->i = i;
}

void var_init_char(Var *v, char c) {
  v->type = V_CHAR;
  v->c = c;
}

void var_init_float(Var *v, float f) {
  v->type = V_FLOAT;
  v->f = f;
}

int main(int argc, char **argv) {

  Var v[SIZE];
  int i;

  var_init_int(&v[0], 10);
  var_init_char(&v[1], 'C');
  var_init_float(&v[2], 3.14);

  for( i = 0 ; i < SIZE ; i++ ) {
    switch( v[i].type ) {
      case V_INT  : printf("INT   %d\n", v[i].i); break;
      case V_CHAR : printf("CHAR  %c\n", v[i].c); break;
      case V_FLOAT: printf("FLOAT %f\n", v[i].f); break;
    }
  }

  return 0;
}

ইউনিয়নের উপাদানটির আকার বৃহত্তম উপাদানটির আকার, 4।


8

অভ্যন্তরীণ ইউনিয়ন অপসারণ করে আইএমও এটি ব্যবহার করতে আরও সুন্দর করে তোলে ট্যাগ-ইউনিয়ন (যে কোনও নামেই) সংজ্ঞায়নের আলাদা স্টাইল রয়েছে's এটি ইভেন্টগুলির মতো জিনিসের জন্য এক্স উইন্ডো সিস্টেমে ব্যবহৃত স্টাইল।

বার্মারের উত্তরের উদাহরণটি valঅভ্যন্তরীণ ইউনিয়নের নাম দেয় । স্প এর উত্তরের উদাহরণটি .val.প্রতিবার আপনি বৈকল্পিক রেকর্ড অ্যাক্সেস করার সময় নির্দিষ্ট করে না দেওয়ার জন্য বেনামে ইউনিয়ন ব্যবহার করে। দুর্ভাগ্যক্রমে "বেনামে" অভ্যন্তরীণ স্ট্রাইক এবং ইউনিয়নগুলি C89 বা C99 এ উপলব্ধ নয়। এটি একটি সংকলক এক্সটেনশান, এবং তাই সহজাতভাবে অ-বহনযোগ্য।

পুরো সংজ্ঞাটি উল্টে ফেলার জন্য আইএমওর আরও ভাল উপায়। প্রতিটি ডেটা তার নিজস্ব স্ট্রাক টাইপ করুন এবং প্রতিটি স্ট্রাক্টে ট্যাগ (টাইপ স্পেসিফায়ার) রাখুন।

typedef struct {
    int tag;
    int val;
} integer;

typedef struct {
    int tag;
    float val;
} real;

তারপরে আপনি এগুলিকে একটি শীর্ষ স্তরের ইউনিয়নে মুড়িয়ে দিন।

typedef union {
    int tag;
    integer int_;
    real real_;
} record;

enum types { INVALID, INT, REAL };

এখন দেখা যাচ্ছে যে আমরা নিজেরাই পুনরাবৃত্তি করছি এবং আমরা আছি । তবে বিবেচনা করুন যে এই সংজ্ঞাটি একটি একক ফাইলে বিচ্ছিন্ন হওয়ার সম্ভাবনা রয়েছে। তবে আমরা আপনার .val.ডেটাতে পৌঁছানোর আগেই মধ্যবর্তীটি নির্দিষ্ট করার আওয়াজকে সরিয়ে ফেলেছি ।

record i;
i.tag = INT;
i.int_.val = 12;

record r;
r.tag = REAL;
r.real_.val = 57.0;

পরিবর্তে, এটি শেষে চলে যায়, যেখানে এটি কম খারাপ হয়। : ডি

আর একটি জিনিস যা এর অনুমতি দেয় তা হ'ল উত্তরাধিকারের একটি রূপ। সম্পাদনা করুন: এই অংশটি মান সি নয়, তবে একটি জিএনইউ এক্সটেনশন ব্যবহার করে।

if (r.tag == INT) {
    integer x = r;
    x.val = 36;
} else if (r.tag == REAL) {
    real x = r;
    x.val = 25.0;
}

integer g = { INT, 100 };
record rg = g;

আপ-ingালাই এবং ডাউন-ingালাই।


সম্পাদনা: সচেতন হওয়ার জন্য একটি গোছা হ'ল আপনি যদি সি 99 মনোনীত প্রারম্ভিকগুলির সাথে এর মধ্যে একটি নির্মাণ করছেন। সমস্ত সদস্য সূচনাকারী একই ইউনিয়নের সদস্যের মাধ্যমে হওয়া উচিত through

record problem = { .tag = INT, .int_.val = 3 };

problem.tag; // may not be initialized

.tagসূচনাকারী, একটি সর্বোচ্চকরন কম্পাইলার দ্বারা উপেক্ষিত যাবে না কারণ .int_সূচনাকারী যে অনুসরণ করে alias লেখা একই ডাটা এরিয়া। যদিও আমরা লেআউটটি জানি (!), এবং এটি ঠিক হওয়া উচিত । না, তা নয়। পরিবর্তে "অভ্যন্তরীণ" ট্যাগটি ব্যবহার করুন (এটি যেমন আমরা চাই ঠিক তেমন বাইরের ট্যাগকে ওভারলে করে, তবে সংকলককে বিভ্রান্ত করে না)।

record not_a_problem = { .int_.tag = INT, .int_.val = 3 };

not_a_problem.tag; // == INT

.int_.valনেই একই এলাকায় যদিও কারণ কম্পাইলার জানে যে ওরফে .valঅধিক চেয়ে অফসেট হয় .tag। এই অভিযোগযুক্ত সমস্যাটি সম্পর্কে আপনি আরও আলোচনার লিঙ্ক পেয়েছেন?
এমএম

5

void *একটি পৃথক অ্যারের সাথে আপনি একটি অ্যারে করতে পারেন size_t.তবে আপনি তথ্যের প্রকারটি হারাবেন।
আপনার যদি কোনওভাবে তথ্যের টাইপ রাখতে হয় তবে তৃতীয় অ্যারে ইনট রাখুন (যেখানে ইন্টটি একটি অঙ্কিত মান হয়) তারপরে সেই ফাংশনটি কোড করুন যা enumমানের উপর নির্ভর করে কাস্ট করে ।



3

ইউনিয়ন যাওয়ার মানক উপায়। তবে আপনার অন্যান্য সমাধানও রয়েছে। এর মধ্যে একটি ট্যাগ পয়েন্টার , যার মধ্যে একটি পয়েন্টারের "ফ্রি" বিটগুলিতে আরও তথ্য সংরক্ষণ করা জড়িত ।

আর্কিটেকচারের উপর নির্ভর করে আপনি কম বা উচ্চ বিটগুলি ব্যবহার করতে পারেন তবে নিরাপদ এবং সর্বাধিক বহনযোগ্য উপায় হ'ল প্রান্তিকৃত স্মৃতির সুবিধা গ্রহণ করে অব্যবহৃত লো বিটগুলি ব্যবহার করা । উদাহরণস্বরূপ 32-বিট এবং 64৪-বিট সিস্টেমে পয়েন্টারগুলি intঅবশ্যই 4 এর গুণক হতে হবে (ধরে intনেওয়া একটি 32-বিট প্রকারের) এবং 2 কমপক্ষে উল্লেখযোগ্য বিটগুলি 0 হওয়া আবশ্যক, সুতরাং আপনি এগুলি আপনার মানের ধরণের সঞ্চয় করতে ব্যবহার করতে পারেন । অবশ্যই আপনাকে পয়েন্টারটিকে ডিফারেন্স করার আগে ট্যাগ বিটগুলি সাফ করতে হবে। উদাহরণস্বরূপ, যদি আপনার ডেটা প্রকারটি 4 টি বিভিন্ন ধরণের মধ্যে সীমাবদ্ধ থাকে তবে আপনি নীচের মতো এটি ব্যবহার করতে পারেন

void* tp; // tagged pointer
enum { is_int, is_double, is_char_p, is_char } type;
// ...
uintptr_t addr = (uintptr_t)tp & ~0x03; // clear the 2 low bits in the pointer
switch ((uintptr_t)tp & 0x03)           // check the tag (2 low bits) for the type
{
case is_int:    // data is int
    printf("%d\n", *((int*)addr));
    break;
case is_double: // data is double
    printf("%f\n", *((double*)addr));
    break;
case is_char_p: // data is char*
    printf("%s\n", (char*)addr);
    break;
case is_char:   // data is char
    printf("%c\n", *((char*)addr));
    break;
}

আপনি যদি নিশ্চিত করতে পারেন যে ডেটাটি 8-বাইট প্রান্তিকিত (যেমন 64৪-বিট সিস্টেমের পয়েন্টারগুলির জন্য, long longএবং uint64_t...), আপনার কাছে ট্যাগটির জন্য আরও একটি বিট থাকবে।

এটির একটি অসুবিধা আছে যে যদি ডেটা অন্য কোনও ভেরিয়েবলে ডেটা সংরক্ষণ না করা থাকে তবে আপনার আরও মেমরির প্রয়োজন হবে। অতএব যদি আপনার ডেটার ধরণ এবং ব্যাপ্তি সীমাবদ্ধ থাকে তবে আপনি মানগুলি সরাসরি পয়েন্টারে সঞ্চয় করতে পারেন। এই কৌশলটি ক্রোমের ভি 8 ইঞ্জিনের 32-বিট সংস্করণে ব্যবহৃত হয়েছে , যেখানে এটি অন্য কোনও বস্তুর কাছে পয়েন্টার (ডাবল, বড় পূর্ণসংখ্যা, স্ট্রিং বা কিছু বস্তুর মতো) বা একটি 31 এর মতো অবস্থানের ঠিকানার উল্লেখযোগ্য বিটটি পরীক্ষা করে che -বিট স্বাক্ষরিত মান (বলা হয় smi- ছোট পূর্ণসংখ্যা )। যদি এটি হয় তবে intক্রম মান পেতে একটি গাণিতিক ডান শিফটটি 1 বিট করে নাহলে পয়েন্টারটিকে অবজ্ঞা করা হয়।


বেশিরভাগ বর্তমান -৪-বিট সিস্টেমে ভার্চুয়াল ঠিকানার স্থান 64৪ বিটের তুলনায় এখনও অনেক সঙ্কুচিত, সুতরাং সর্বাধিক উল্লেখযোগ্য বিটগুলি ট্যাগ হিসাবেও ব্যবহার করা যেতে পারে । আর্কিটেকচারের উপর নির্ভর করে আপনার ট্যাগ হিসাবে ব্যবহার করার বিভিন্ন উপায় রয়েছে। এআরএম , k and কে এবং আরও অনেককে শীর্ষ বিটগুলি উপেক্ষা করার জন্য কনফিগার করা যেতে পারে , সেগফল্ট বা অন্য কোনও বিষয় নিয়ে চিন্তা না করে আপনাকে এগুলি অবাধে ব্যবহারের অনুমতি দেয়। উপরের লিঙ্কযুক্ত উইকিপিডিয়া নিবন্ধ থেকে:

ট্যাগ পয়েন্টার ব্যবহারের একটি উল্লেখযোগ্য উদাহরণ হ'ল এআরএম on৪ এর আইওএস on-তে উদ্দেশ্যমূলক-সি রানটাইম, উল্লেখযোগ্যভাবে আইফোন 5 এস-তে ব্যবহৃত হয়। আইওএস 7-এ, ভার্চুয়াল ঠিকানাগুলি 33 বিট (বাইট-এ্যালাইন্টেড), তাই শব্দ-সংযুক্ত ঠিকানাগুলিতে কেবলমাত্র 30 বিট ব্যবহার করা হয় (3 টি কমপক্ষে গুরুত্বপূর্ণ বিট 0 হয়), ট্যাগগুলির জন্য 34 বিট রেখে। উদ্দেশ্য-সি শ্রেণীর পয়েন্টারগুলি শব্দের সাথে সংযুক্ত থাকে এবং ট্যাগ ক্ষেত্রগুলি অনেকগুলি উদ্দেশ্যে যেমন একটি রেফারেন্স গণনা সংরক্ষণ করা এবং অবজেক্টটির ডেস্ট্রাক্টর রয়েছে কিনা তা ব্যবহার করা হয়।

MacOS এর প্রাথমিক সংস্করণগুলিতে ডেটা অবজেক্টের রেফারেন্স সঞ্চয় করতে হ্যান্ডলস নামে ট্যাগ করা ঠিকানা ব্যবহার করা হয়েছিল। ঠিকানার উচ্চ বিটগুলি নির্দেশ করে যে ডেটা অবজেক্টটি যথাক্রমে লক, শুদ্ধযোগ্য এবং / অথবা কোনও উত্স ফাইল থেকে উদ্ভূত কিনা। এটি যখন সামঞ্জস্যতা সমস্যার সৃষ্টি করে তখন ম্যাকোস যখন ঠিকানা 7 সিস্টেমের 24 বিট থেকে 32 বিটের দিকে উন্নত করে।

https://en.wikipedia.org/wiki/Tagged_pointer#Examples

X86_64 এ আপনি এখনও যত্ন সহ উচ্চ বিট ব্যবহার করতে পারেন । অবশ্যই আপনার এই সমস্ত 16 টি বিট ব্যবহার করার দরকার নেই এবং ভবিষ্যতের প্রমাণের জন্য কিছু বিট ছেড়ে দিতে পারেন

মজিলা ফায়ারফক্সের পূর্ববর্তী সংস্করণগুলিতে তারা ভি 8 এর মতো ছোট পূর্ণসংখ্যার অপ্টিমাইজেশন ব্যবহার করে, টাইপটি (int, স্ট্রিং, অবজেক্ট ... ইত্যাদি) সঞ্চয় করতে ব্যবহৃত 3 টি কম বিট সহ small কিন্তু জাগারমনকি যেহেতু তারা অন্য পথ নিয়েছিল ( মজিলার নতুন জাভাস্ক্রিপ্ট মান উপস্থাপনা , ব্যাকআপ লিঙ্ক )। মানটি এখন সর্বদা একটি 64-বিট ডাবল যথার্থ ভেরিয়েবলে সংরক্ষণ করা হয়। যখন এটি doubleএকটি স্বাভাবিক করা হয়, এটি সরাসরি গণনায় ব্যবহার করা যেতে পারে। তবে যদি এর উচ্চ 16 বিটগুলি সমস্ত 1s হয়, যা কোনও NaN বোঝায় , কম 32-বিট ঠিকানাটি (32-বিট কম্পিউটারে) সরাসরি মান বা মানের মধ্যে সংরক্ষণ করবে, বাকি 16 বিট ব্যবহার করা হবে টাইপ সংরক্ষণ করতে। এই কৌশলটিকে ন্যান-বক্সিং বলা হয়বা নন-বক্সিং। এটি 64৪-বিট ওয়েবকিটের জাভাস্ক্রিপ্ট কোর এবং মজিলার স্পাইডারমনকিতে পয়েন্টারটি কম 48 বিটে সংরক্ষণ করা রয়েছে। যদি আপনার মূল ডেটা টাইপটি ভাসমান-পয়েন্ট হয় তবে এটি সেরা সমাধান এবং খুব ভাল পারফরম্যান্স সরবরাহ করে।

উপরোক্ত কৌশলগুলি সম্পর্কে আরও পড়ুন: https://wingolog.org/archives/2011/05/18/value- বিবরণীকরণ- ইন- জাভাস্ক্রিপ্ট- বাস্তবায়ন

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.