সিতে মান দ্বারা স্ট্রাইকগুলি পাশ করার পরিবর্তে কোনও পয়েন্টার পাস করার চেয়ে কোনও ডাউনসাইড রয়েছে?


157

সিতে মান দ্বারা স্ট্রাইকগুলি পাশ করার পরিবর্তে কোনও পয়েন্টার পাস করার চেয়ে কোনও ডাউনসাইড রয়েছে?

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

রিটার্নের মান হিসাবে ব্যবহার করার সময় এটি আরও বেশি আকর্ষণীয় হতে পারে। সি শুধুমাত্র ফাংশন থেকে একক রিটার্ন মান আছে, তবে আপনার প্রায়শই বেশ কয়েকটি প্রয়োজন। সুতরাং একটি সহজ সমাধান হ'ল এগুলি একটি কাঠামোয় স্থাপন করা এবং এটি ফিরিয়ে দেওয়া।

এর পক্ষে বা বিপক্ষে কোনও কারণ আছে কি?

যেহেতু আমি এখানে যে বিষয়ে কথা বলছি তা সবার কাছে স্পষ্ট নাও হতে পারে, তাই আমি একটি সহজ উদাহরণ দেব।

আপনি যদি সি তে প্রোগ্রামিং করে থাকেন, আপনি তাড়াতাড়ি বা পরে এর মতো দেখতে ফাংশনগুলি লিখতে শুরু করবেন:

void examine_data(const char *ptr, size_t len)
{
    ...
}

char *p = ...;
size_t l = ...;
examine_data(p, l);

এটি কোনও সমস্যা নয়। একমাত্র সমস্যা হ'ল আপনাকে আপনার সহকর্মীর সাথে একমত হতে হবে যাতে প্যারামিটারগুলি ক্রম হওয়া উচিত যাতে আপনি সমস্ত কার্যক্রমে একই সম্মেলনটি ব্যবহার করেন।

আপনি যখন একই ধরণের তথ্য ফিরিয়ে দিতে চান তবে কী হবে? আপনি সাধারণত এরকম কিছু পান:

char *get_data(size_t *len);
{
    ...
    *len = ...datalen...;
    return ...data...;
}
size_t len;
char *p = get_data(&len);

এটি সূক্ষ্মভাবে কাজ করে, তবে এটি অনেক বেশি সমস্যাযুক্ত। এই বাস্তবায়নের ব্যতীত কোনও রিটার্ন মান হ'ল একটি রিটার্ন মান। উপরের দিক থেকে বলার কোনও উপায় নেই যে ফাংশনটি get_data কোন লেনটি নির্দেশ করে তা দেখার অনুমতি নেই। এবং এমন কিছুই নেই যা সংকলকটি পরীক্ষা করে তোলে যে কোনও মানটি সেই পয়েন্টারের মাধ্যমে আসলে ফিরে আসে। সুতরাং পরের মাসে, যখন অন্য কেউ কোডটি সঠিকভাবে না বুঝে সংশোধন করে (কারণ সে ডকুমেন্টেশনটি পড়ে না?) কারও নজরে না নিয়েই এটি ভেঙে যায়, বা এটি এলোমেলোভাবে ক্রাশ শুরু করে।

সুতরাং, আমি প্রস্তাবিত সমাধানটি হ'ল সহজ কাঠামো

struct blob { char *ptr; size_t len; }

উদাহরণগুলি এইভাবে আবার লেখা যেতে পারে:

void examine_data(const struct blob data)
{
    ... use data.tr and data.len ...
}

struct blob = { .ptr = ..., .len = ... };
examine_data(blob);

struct blob get_data(void);
{
    ...
    return (struct blob){ .ptr = ...data..., .len = ...len... };
}
struct blob data = get_data();

কোনও কারণে, আমি মনে করি যে বেশিরভাগ লোকেরা সহজাতভাবে পরীক্ষা_ডাটা একটি কাঠামোয় একটি পয়েন্টার নিয়ে যায়, তবে কেন তা আমি দেখছি না। এটি এখনও একটি পয়েন্টার এবং একটি পূর্ণসংখ্যা পায়, এটি আরও পরিষ্কার যে তারা একসাথে চলে। এবং get_data ক্ষেত্রে আমি আগে বর্ণিতভাবে গণ্ডগোল করা অসম্ভব, যেহেতু দৈর্ঘ্যের কোনও ইনপুট মান নেই, এবং অবশ্যই একটি ফিরে দৈর্ঘ্য থাকতে হবে।


এর মূল্য কী, void examine data(const struct blob)এটি ভুল।
ক্রিস লুটজ

ধন্যবাদ, একটি পরিবর্তনশীল নাম অন্তর্ভুক্ত করতে এটি পরিবর্তন করে।
dkagedal

1
"উপরের দিক থেকে বলার কোনও উপায় নেই যে ফাংশনটি get_data কোন লেনটি নির্দেশ করে তা দেখার অনুমতি দেয় না। এবং এমন কোনও কিছুই নেই যা সংকলকটি পরীক্ষা করে এটি পরীক্ষা করে যে কোনও মানটি সেই পয়েন্টারের মাধ্যমে আসলে ফিরে আসে কিনা।" - এটি আমার কাছে মোটেই অর্থবহ নয় (সম্ভবত কোনও ফাংশনের বাইরে উপস্থিত দুটি লাইনের কারণে আপনার উদাহরণটি অবৈধ কোড বলে); দয়া করে বিস্তারিত বলতে পারেন?
অ্যাডাম স্পায়ার্স

2
ফাংশনটির নীচের দুটি লাইন সেখানে ফাংশনটি কীভাবে ডাকা হয় তা চিত্রিত করার জন্য রয়েছে। ফাংশন স্বাক্ষরটি বাস্তবায়নের শুধুমাত্র পয়েন্টারে লিখতে হবে এমন কোনও ইঙ্গিত দেয় না। এবং সংকলকটির জানার কোনও উপায় নেই যে এটি পয়েন্টারে একটি মান লেখা হয়েছে কিনা তা যাচাই করা উচিত, সুতরাং রিটার্ন মান প্রক্রিয়াটি কেবলমাত্র ডকুমেন্টেশনে বর্ণিত হতে পারে।
dkagedal

1
লোকেরা সিতে বেশিবার এটি না করার প্রধান কারণটি isতিহাসিক। সি 98 এর পূর্বে, আপনি মান দ্বারা স্ট্রাইকগুলি পাস বা ফিরিয়ে দিতে পারেন না , সুতরাং সমস্ত সিস্টেম ইন্টারফেসগুলি যা সি 89 এর পূর্বাভাস দেয় এবং যৌক্তিকভাবে এটি করা উচিত (যেমন gettimeofday) পরিবর্তে পয়েন্টার ব্যবহার করা হয়, এবং লোকেরা এটি উদাহরণ হিসাবে গ্রহণ করে।
zwol

উত্তর:


202

ছোট স্ট্রাক্টের জন্য (উদাহরণস্বরূপ, বিন্দু) মান দ্বারা পাস করা পুরোপুরি গ্রহণযোগ্য। তবে, গতি ছাড়াও, আরও একটি কারণ রয়েছে যেগুলি আপনাকে বড় মানের স্ট্রাক্টটি মূল্য দিয়ে পাস / ফিরিয়ে দেওয়ার ক্ষেত্রে সাবধানতা অবলম্বন করবে: স্পেস স্প্যাক।

প্রচুর সি প্রোগ্রামিং এম্বেড থাকা সিস্টেমগুলির জন্য, যেখানে মেমরি একটি প্রিমিয়ামে থাকে এবং স্ট্যাকের মাপগুলি কেবি বা এমনকি বাইটেও মাপা যায় ... আপনি যদি মান দ্বারা স্ট্রাইকগুলি পাস বা ফিরিয়ে দিচ্ছেন তবে সেই স্ট্রাইকগুলির অনুলিপিগুলি স্থাপন করা হবে এই স্ট্যাকটি, সম্ভবত এই পরিস্থিতির কারণে এই সাইটের নামকরণ হয়েছে ...

আমি যদি এমন একটি অ্যাপ্লিকেশন দেখি যা মনে হয় অতিরিক্ত স্ট্যাক ব্যবহার করে, মান দ্বারা পাস করা স্ট্রাক্টগুলি আমি প্রথমে সন্ধান করা জিনিসগুলির মধ্যে একটি।


2
"আপনি ক্ষণস্থায়ী বা মূল্য দ্বারা structs মধ্যে ফিরে করছি থাকলে, সেই structs এর কপি স্ট্যাকের উপর স্থাপন করা হবে" আমি কল চাই braindead কোনো টুলচেইন যাতে আছে। হ্যাঁ, এটি দুঃখজনক যে এতগুলি এটি করবে, তবে এটি সি স্ট্যান্ডার্ডের জন্য কল করা কিছু নয়। একটি বুদ্ধিমান সংকলক এটি সর্বোপরি অনুকূলিত করবে।
মনিকা পুনরায়

1
: @KubaOber এই কেন যে কাজ পেতে হয় stackoverflow.com/questions/552134/...
রডি

1
একটি সংক্ষিপ্ত রেখা আছে যা একটি ছোট কাঠামোকে একটি বৃহত কাঠামো থেকে আলাদা করে?
জোসি থম্পসন

63

এটি না করার একটি কারণ যা উল্লেখ করা হয়নি তা হ'ল এটি বাইনারি সামঞ্জস্যের বিষয়টি নিয়ে একটি সমস্যার কারণ হতে পারে।

ব্যবহৃত সংকলকটির উপর নির্ভর করে সংকলক বিকল্পগুলি / প্রয়োগের উপর নির্ভর করে স্ট্রাকচারগুলি স্ট্যাক বা রেজিস্টারগুলির মধ্য দিয়ে যেতে পারে

দেখুন: http://gcc.gnu.org/onlinesocs/gcc/Code-Gen-Options.html

-fpcc-struct হয় রিটার্ন

-freg-struct হয় রিটার্ন

দুটি সংকলক যদি একমত না হন তবে বিষয়গুলি ধাপে ধাপে ধাপে ধাপে উঠতে পারে। এটি না করার প্রধান কারণগুলি স্ট্যাকের ব্যবহার এবং পারফরম্যান্সের কারণগুলির উদাহরণ হিসাবে বলা বাহুল্য।


4
এটি আমি যে ধরণের উত্তর খুঁজছিলাম।
dkagedal

2
সত্য, তবে এই বিকল্পগুলি পাস-বাই-মানের সাথে সম্পর্কিত নয়। তারা রিটার্নিং স্ট্রকের সাথে সম্পর্কিত যা সম্পূর্ণ আলাদাভাবে আলাদা thing রেফারেন্স অনুসারে জিনিসগুলি ফিরানো সাধারণত উভয় পায়ে গুলি চালানোর একটি নিশ্চিত আগুনের উপায়। int &bar() { int f; int &j(f); return j;};
রডি

19

করতে সত্যিই সমাবেশ জমি গভীরে যান, এই প্রশ্নের উত্তর এক দরকার:

(নীচের উদাহরণে x86_64 তে জিসিসি ব্যবহার করা হয়েছে MS এমএসভিসি, এআরএম ইত্যাদি অন্যান্য আর্কিটেকচার যুক্ত করার জন্য যে কেউ স্বাগত)

আসুন আমাদের উদাহরণ প্রোগ্রাম আছে:

// foo.c

typedef struct
{
    double x, y;
} point;

void give_two_doubles(double * x, double * y)
{
    *x = 1.0;
    *y = 2.0;
}

point give_point()
{
    point a = {1.0, 2.0};
    return a;
}

int main()
{
    return 0;
}

সম্পূর্ণ অপ্টিমাইজেশান সহ এটি সংকলন করুন

gcc -Wall -O3 foo.c -o foo

সমাবেশ দেখুন:

objdump -d foo | vim -

এটি আমরা পাই:

0000000000400480 <give_two_doubles>:
    400480: 48 ba 00 00 00 00 00    mov    $0x3ff0000000000000,%rdx
    400487: 00 f0 3f 
    40048a: 48 b8 00 00 00 00 00    mov    $0x4000000000000000,%rax
    400491: 00 00 40 
    400494: 48 89 17                mov    %rdx,(%rdi)
    400497: 48 89 06                mov    %rax,(%rsi)
    40049a: c3                      retq   
    40049b: 0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)

00000000004004a0 <give_point>:
    4004a0: 66 0f 28 05 28 01 00    movapd 0x128(%rip),%xmm0
    4004a7: 00 
    4004a8: 66 0f 29 44 24 e8       movapd %xmm0,-0x18(%rsp)
    4004ae: f2 0f 10 05 12 01 00    movsd  0x112(%rip),%xmm0
    4004b5: 00 
    4004b6: f2 0f 10 4c 24 f0       movsd  -0x10(%rsp),%xmm1
    4004bc: c3                      retq   
    4004bd: 0f 1f 00                nopl   (%rax)

noplপ্যাড বাদে give_two_doubles()27 বাইট give_point()রয়েছে এবং 29 বাইট রয়েছে। অন্যদিকে, এর give_point()চেয়ে কম নির্দেশ পাওয়া যায়give_two_doubles()

মজার বিষয় হ'ল আমরা লক্ষ্য করি যে movসংকলকটি দ্রুত এসএসই 2 রূপগুলিতে movapdএবং অনুকূলিত করতে সক্ষম হয়েছে movsd। তদুপরি, give_two_doubles()আসলে মেমরি থেকে ডেটা ভিতরে এবং আনা যায়, যা জিনিসকে ধীর করে তোলে।

স্পষ্টতই এর বেশিরভাগ এম্বেড থাকা পরিবেশে প্রযোজ্য নাও হতে পারে (যা এখন সি এর জন্য খেলার ক্ষেত্র বেশিরভাগ সময়) field আমি কোনও এসেম্বলি উইজার্ড না তাই কোনও মন্তব্য স্বাগত হবে!


6
নির্দেশের সংখ্যা গণনা মোটেও আকর্ষণীয় নয়, যদি না আপনি বিশাল পার্থক্য দেখাতে পারেন বা হার্ড-টু-প্রেডিক্ট জাম্পের সংখ্যার মতো আরও আকর্ষণীয় দিকগুলি গণনা করতে না পারেন তবে প্রকৃত পারফরম্যান্সের বৈশিষ্ট্য নির্দেশের গণনার চেয়ে অনেক বেশি সূক্ষ্ম ।
dkagedal

6
@ ডাকাগডাল: সত্য। পূর্ববর্তী ক্ষেত্রে, আমি আমার নিজের উত্তরটি খুব খারাপ লিখেছে বলে মনে করি। যদিও আমি নির্দেশাবলীর সংখ্যার উপর খুব বেশি মনোযোগ দিলাম না (ডান্নো আপনাকে এই ধারণাটি কীভাবে দিয়েছে: পি), মূল বক্তব্যটি হ'ল মান অনুসারে কাঠামো পাস করা ছোট প্রকারের জন্য রেফারেন্স দিয়ে যাওয়ার চেয়ে পছন্দনীয়। যাইহোক, মান দ্বারা পাস করা পছন্দ করা হয় কারণ এটি সহজ (কোনও আজীবন জাগরণ নয়, কেউ আপনার ডেটা বা constসমস্ত সময় পরিবর্তন করার বিষয়ে চিন্তা করার দরকার নেই ) এবং আমি খুঁজে পেয়েছি যে পাস-বাই-ভ্যালু অনুলিপিটিতে খুব বেশি পারফরম্যান্স জরিমানা (লাভ না হলে) নেই found , যা বিশ্বাস করতে পারে তার বিপরীতে।
kizzx2

15

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

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

মন্তব্য করতে পুনরায় খেলুন:

পয়েন্টার দ্বারা কাঠামো পাস করার অর্থ এই যে কোনও কোনও সত্তার এই অবজেক্টটির মালিকানা রয়েছে এবং কখন এবং কখন মুক্তি দেওয়া উচিত তার সম্পূর্ণ জ্ঞান রয়েছে। স্ট্রাক্টকে মান দ্বারা পাস করা স্ট্রাক্টের অভ্যন্তরীণ তথ্যগুলির জন্য একটি গোপন রেফারেন্স তৈরি করে (অন্য কাঠামোর দিকে নির্দেশক ইত্যাদি)) এটি বজায় রাখা শক্ত (সম্ভব তবে কেন?)।


6
তবে পয়েন্টারটি পাস করা আরও "বিপজ্জনক" নয় কারণ আপনি এটি স্ট্রাক্টে রেখেছেন, তাই আমি এটি কিনছি না।
dkagedal

একটি বিন্যাস রয়েছে এমন একটি কাঠামো অনুলিপি করার দুর্দান্ত পয়েন্ট। এই বিষয়টি খুব সুস্পষ্ট নাও হতে পারে। যাঁরা জানেন না তিনি কী বিষয়ে উল্লেখ করছেন, তাদের জন্য গভীর অনুলিপি বনাম অগভীর অনুলিপি অনুসন্ধান করুন।
জুরোপা

1
সি ফাংশন কনভেনশনগুলির মধ্যে একটি হ'ল ইনপুট পরামিতিগুলির আগে আউটপুট পরামিতিগুলি প্রথমে তালিকাভুক্ত করা হয়, যেমন ইন ফানক (চর * আউট, চর * ইন);
জুরোপা

আপনি বোঝাতে চাইছেন কীভাবে উদাহরণস্বরূপ getaddrinfo () আউটপুট প্যারামিটারটিকে সর্বশেষে রাখে? :-) এখানে হাজার হাজার কনভেনশন রয়েছে এবং আপনি যা চান তা চয়ন করতে পারেন।
dkagedal

10

এখানকার লোকেরা এতক্ষণে উল্লেখ করতে ভুলে গেছেন (বা আমি এটি উপেক্ষা করেছি) তা হ'ল স্ট্রাক্টগুলিতে সাধারণত প্যাডিং থাকে!

struct {
  short a;
  char b;
  short c;
  char d;
}

প্রতি চর 1 বাইট, প্রতি শর্ট 2 বাইট কাঠামোটি কত বড়? নাহ, এটি 6 বাইট নয় কমপক্ষে আর কোনও সাধারণ ব্যবহৃত সিস্টেমে নয়। বেশিরভাগ সিস্টেমে এটি 8 হবে The সমস্যাটি হচ্ছে, প্রান্তিককরণটি ধ্রুবক নয়, এটি সিস্টেম নির্ভর। তাই একই কাঠামোর বিভিন্ন সিস্টেমে বিভিন্ন প্রান্তিককরণ এবং বিভিন্ন আকার থাকবে izes

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


2
হ্যাঁ, তবে প্যাডিংটি মান বা রেফারেন্সের সাহায্যে কাঠামোটি পাস করার উপর নির্ভরতার সাথে বিদ্যমান।
ইলিয়া 13

2
@ ডাকাগডাল: "বিভিন্ন সিস্টেমে বিভিন্ন আকারের" কোন অংশটি আপনি বুঝতে পারেন নি? এটি কেবল আপনার সিস্টেমে থাকার কারণে, আপনি ধরে নিয়েছেন এটি অন্য যে কোনওটির জন্য অবশ্যই একই হতে পারে - এজন্যই আপনাকে মূল্য দিয়ে পাস করা উচিত নয়। নমুনা পরিবর্তিত হয়েছে যাতে এটি আপনার সিস্টেমেও ব্যর্থ হয়।
মক্কি

2
আমি মনে করি স্ট্রাক প্যাডিং সম্পর্কে মক্কির মন্তব্য বিশেষত এমবেড হওয়া সিস্টেমগুলির জন্য প্রাসঙ্গিক যেখানে স্ট্যাকের আকার একটি সমস্যা হতে পারে।
জুরোপা

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

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

9

আমি মনে করি আপনার প্রশ্নটি বেশ ভালভাবে সংক্ষিপ্তসার করেছে।

মান দ্বারা স্ট্রাইকগুলি পাস করার আর একটি সুবিধা হ'ল মেমরির মালিকানা স্পষ্ট। স্ট্রাকটি হিপ থেকে হয়েছে কিনা তা নিয়ে কোনও আশ্চর্য হওয়ার কিছু নেই, এবং এটি মুক্ত করার দায়িত্ব কার রয়েছে।


9

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

আপডেট: দুঃখিত, আমি আমার সি ++ চিন্তাভাবনা ক্যাপ চালু ছিল। আমি এমন একটি সময় মনে করি যখন কোনও ফাংশন থেকে কোনও কাঠামো ফেরত দেওয়া সি তে আইনী ছিল না, তবে সম্ভবত এটি তখন থেকেই পরিবর্তিত হয়েছে। আমি এখনও বলব যতক্ষণ আপনি অনুশীলনকে সমর্থন করার মতো সমস্ত সংকলক ব্যবহার করার আশা করছেন এটি বৈধ।


মনে রাখবেন যে আমার প্রশ্নটি সি ++ নয়, সি সম্পর্কে ছিল।
dkagedal

কেবল কার্যকর নয় ফাংশন থেকে কাঠামো ফেরানো বৈধ: :)
ইলিয়া

1
আমি ফাংশন থেকে ডেটা ফেরত দেওয়ার জন্য ত্রুটি কোড এবং প্যারামিটার হিসাবে রিটার্নটি ব্যবহার করার জন্য llya এর পরামর্শ চাই।
জুরোপা

8

এখানে এমন কিছু যা উল্লেখ করা হয়নি:

void examine_data(const char *c, size_t l)
{
    c[0] = 'l'; // compiler error
}

void examine_data(const struct blob blob)
{
    blob.ptr[0] = 'l'; // perfectly legal, quite likely to blow up at runtime
}

একটি সদস্যরা const structহয় const, কিন্তু যদি যে সদস্য পয়েন্টার (ভালো হয়char * ), এটা হয়ে char *constবদলে const char *আমরা সত্যিই চাই। অবশ্যই, আমরা ধরে নিতে পারি যে এটি constহ'ল উদ্দেশ্যটির দলিল, এবং যে কেউ এটি লঙ্ঘন করে তারা খারাপ কোড লিখছেন (যা তারা হচ্ছেন), তবে এটি কারওর পক্ষে যথেষ্ট ভাল নয় (বিশেষত যারা কেবল কারণটির কারণ অনুসন্ধানে চার ঘন্টা ব্যয় করেছেন) ক্র্যাশ করে)।

বিকল্পটি একটি তৈরি করা struct const_blob { const char *c; size_t l }এবং এটি ব্যবহার করা হতে পারে , তবে এটি বরং অগোছালো - এটি typedefআইএন পয়েন্টারগুলির সাথে আমার একই নামকরণ-প্রকল্পের সমস্যায় পড়ে । সুতরাং, বেশিরভাগ লোকেরা কেবল দুটি পরামিতি (বা এই ক্ষেত্রে স্ট্রিং লাইব্রেরি ব্যবহারের বেশি সম্ভাবনা রয়েছে) রাখে।


হ্যাঁ এটি পুরোপুরি আইনী, এবং এমন কিছু যা আপনি কখনও কখনও করতে চান। তবে আমি সম্মত হই যে এটি কাঠামোর সমাধানের একটি সীমাবদ্ধতা যা আপনি পয়েন্টারগুলিকে বিন্দুতে নির্দেশ করতে পারেন না।
dkagedal

সঙ্গে একটি বাজে গ্যাচা struct const_blob সমাধান এমনকি যদি হয় const_blobসদস্যরা থেকে পৃথক হয়ে গেছে blob"পরোক্ষ-const-অন্তরীপ" এ শুধুমাত্র ধরনের struct blob*একটি থেকে struct const_blob*কঠোর এলিয়াসিং শাসনের উদ্দেশ্যে স্বতন্ত্র বিবেচনা করা হবে। ফলে, যদি কোড কাস্ট একটি blob*একটি থেকে const_blob*, এক ধরনের ব্যবহার চুপটি অন্য ধরনের, এই ধরনের যে কোনও ব্যবহারের অনির্ধারিত আচরণ ডাকা হবে যেকোনো বিদ্যমান পয়েন্টার এটি অকার্যকর হবে অন্তর্নিহিত কাঠামো পরবর্তী লিখন (যা সাধারণত নিরীহ হতে পারে, কিন্তু মারাত্মক হতে পারে) ।
সুপারক্যাট

5

Http://www.drpaulcarter.com/pcasm/ এ পিসি অ্যাসেম্বলি টিউটোরিয়ালের পৃষ্ঠা 150 এর সি সম্পর্কে কীভাবে কোনও ফাংশনকে স্ট্রাক্ট ফেরত আনতে দেয় সে সম্পর্কে একটি স্পষ্ট ব্যাখ্যা রয়েছে:

সি কোনও কাঠামোর ধরণটিকে ফানিশনের রিটার্ন মান হিসাবে ব্যবহারের অনুমতি দেয়। স্পষ্টতই EAX রেজিস্টারে কোনও কাঠামো ফেরানো যাবে না। বিভিন্ন সংকলক এই পরিস্থিতিটিকে আলাদাভাবে পরিচালনা করে। একটি সাধারণ সমাধান যা সংকলকরা ব্যবহার করেন তা হ'ল অভ্যন্তরীণভাবে ফাংশনটিকে প্যারামিটার হিসাবে স্ট্রাকচার পয়েন্টার হিসাবে গ্রহণ করে re পয়েন্টারটি ডাকা রুটিনের বাইরে সংজ্ঞায়িত স্ট্রাকচারটিতে রিটার্ন মানটি ব্যবহার করতে ব্যবহৃত হয়।

আমি উপরের বিবৃতিটি যাচাই করতে নীচের সি কোডটি ব্যবহার করি:

struct person {
    int no;
    int age;
};

struct person create() {
    struct person jingguo = { .no = 1, .age = 2};
    return jingguo;
}

int main(int argc, const char *argv[]) {
    struct person result;
    result = create();
    return 0;
}

এই কোডের টুকরোটির জন্য সমাবেশ তৈরি করতে "জিসিসি-এস" ব্যবহার করুন:

    .file   "foo.c"
    .text
.globl create
    .type   create, @function
create:
    pushl   %ebp
    movl    %esp, %ebp
    subl    $16, %esp
    movl    8(%ebp), %ecx
    movl    $1, -8(%ebp)
    movl    $2, -4(%ebp)
    movl    -8(%ebp), %eax
    movl    -4(%ebp), %edx
    movl    %eax, (%ecx)
    movl    %edx, 4(%ecx)
    movl    %ecx, %eax
    leave
    ret $4
    .size   create, .-create
.globl main
    .type   main, @function
main:
    pushl   %ebp
    movl    %esp, %ebp
    subl    $20, %esp
    leal    -8(%ebp), %eax
    movl    %eax, (%esp)
    call    create
    subl    $4, %esp
    movl    $0, %eax
    leave
    ret
    .size   main, .-main
    .ident  "GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3"
    .section    .note.GNU-stack,"",@progbits

কল তৈরির আগে স্ট্যাক:

        +---------------------------+
ebp     | saved ebp                 |
        +---------------------------+
ebp-4   | age part of struct person | 
        +---------------------------+
ebp-8   | no part of struct person  |
        +---------------------------+        
ebp-12  |                           |
        +---------------------------+
ebp-16  |                           |
        +---------------------------+
ebp-20  | ebp-8 (address)           |
        +---------------------------+

স্ট্যাকের ডানদিকে ক্লিক করুন তৈরি করুন:

        +---------------------------+
        | ebp-8 (address)           |
        +---------------------------+
        | return address            |
        +---------------------------+
ebp,esp | saved ebp                 |
        +---------------------------+

2
এখানে দুটি সমস্যা আছে। সর্বাধিক সুস্পষ্ট একটি হ'ল এটি কোনওভাবে বর্ণনা করে না যে "কীভাবে সি কোনও ফাংশনটিকে স্ট্রাক্ট ফেরত দেয়"। এটি কেবলমাত্র 32-বিট x86 হার্ডওয়ারে এটি কীভাবে করা যেতে পারে তা বর্ণনা করে, যা আপনি যখন রেজিস্ট্রিগুলির সংখ্যা দেখেন তখন সীমাবদ্ধ আর্কিটেকচারগুলির মধ্যে একটি হতে পারে second এবিআই দ্বারা নির্ধারিত হয় (অ-রফতানি বা ইনলাইনড ফাংশন ব্যতীত)। এবং যাইহোক, ইনলাইনড ফাংশনগুলি সম্ভবত এমন এক জায়গাগুলি যেখানে রিটার্নিং স্ট্র্টগুলি সবচেয়ে কার্যকর।
dkagedal

সংশোধনের জন্য ধন্যবাদ। সম্পূর্ণ কনভেনশন কলিং বিস্তারিত জন্য, en.wikipedia.org/wiki/Calling_convention একটি ভাল রেফারেন্স।
জিঙ্গগু ইয়াও

@ ডাকাগডাল: যা তাৎপর্যপূর্ণ তা হল x86 কেবল এইভাবে কিছু করতে পারে তা নয়, বরং সেখানে একটি "সর্বজনীন" পদ্ধতির উপস্থিতি রয়েছে (যেমন এটি) যে কোনও প্ল্যাটফর্মের জন্য সংকলকগণ যে কোনও কাঠামোর প্রকারকে সমর্থন করতে পারবেন না ' স্ট্যাকটি ফুঁ দেওয়ার মতো এত বিশাল। অনেক প্ল্যাটফর্মের সংকলকরা কিছু কাঠামো-প্রকার রিটার্ন মানগুলি পরিচালনা করার জন্য অন্যান্য আরও কার্যকর উপায় ব্যবহার করবে, তবে প্ল্যাটফর্মটি অনুকূলভাবে পরিচালনা করতে পারে এমন কাঠামোর রিটার্নের ধরণের সীমাবদ্ধ করার জন্য ভাষার প্রয়োজন নেই।
সুপারক্যাট

0

আমি কেবল আপনার মানকে স্ট্রাইকগুলি পাস করার একটি সুবিধা উল্লেখ করতে চাই যে একটি অনুকূলিতকরণ সংকলক আপনার কোডটিকে আরও ভাল করতে পারে।

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