সি এর কোনও ক্রিয়াকলাপ থেকে একটি `কাঠামো Return ফিরিয়ে দিন


171

আজ আমি কয়েকজন বন্ধুকে শিখিয়েছিলাম কীভাবে সি structএস ব্যবহার করতে হয় । তাদের একজন জিজ্ঞাসা করলেন যে আপনি structকোনও ফাংশন থেকে কোনওটি ফিরে আসতে পারেন কিনা , আমি এর জবাব দিয়েছিলাম: "না! আপনি পয়েন্টারগুলিকে গতিশীলভাবে mallocসম্পাদনা করতে চানstruct পরিবর্তে এর ।"

এমন কারও কাছ থেকে আসছি যিনি প্রাথমিকভাবে সি ++ করেন, আমি প্রত্যাশা করছিলাম যে structমানগুলি অনুসারে আর ফিরে আসতে পারবে না । সি ++ এ আপনি operator =আপনার অবজেক্টগুলির জন্য ওভারলোড করতে পারেন এবং মান দিয়ে আপনার অবজেক্ট ফিরিয়ে দেওয়ার জন্য কোনও ক্রিয়াকলাপ অনুধাবন করতে পারেন complete সি তে তবে আপনার কাছে সেই বিকল্প নেই এবং তাই সংকলকটি আসলে কী করছে তা আমাকে ভাবতে পেরেছিল। নিম্নোক্ত বিবেচনা কর:

struct MyObj{
    double x, y;
};

struct MyObj foo(){
    struct MyObj a;

    a.x = 10;
    a.y = 10;

    return a;
}        

int main () {

    struct MyObj a;

    a = foo();    // This DOES work
    struct b = a; // This does not work

    return 0;
}    

আমি বুঝতে পারি কেন struct b = a;কাজ করা উচিত নয় - আপনি operator =আপনার ডেটা ধরণের জন্য ওভারলোড করতে পারবেন না । এটি কীভাবে a = foo();জরিমানা সংকলন করে? এর মানে কি অন্য কিছু struct b = a;? হয়তো জিজ্ঞাসা করার প্রশ্নটি হ'ল: স্বাক্ষর করতে returnসম্মতিতে বিবৃতিটি ঠিক কী করে =?

[সম্পাদনা]: ঠিক আছে, আমি কেবল struct b = aএকটি সিনট্যাক্স ত্রুটি হিসাবে চিহ্নিত করেছি - এটি সঠিক এবং আমি একটি বোকা! কিন্তু এটি আরও জটিল করে তোলে! ব্যবহার struct MyObj b = aসত্যিই কাজ করে! আমি এখানে কি মিস করছি?


23
struct b = a;একটি সিনট্যাক্স ত্রুটি। আপনি যদি চেষ্টা করেন struct MyObj b = a;?
গ্রেগ হিউগিল

2
@ গ্রেগ হিউগিল: আপনি একেবারে ঠিক বলেছেন। বেশ আকর্ষণীয়ভাবে, তবে, struct MyObj b = a;এটি কাজ করে বলে মনে হচ্ছে :)
মিমিরজাদে

উত্তর:


199

আপনি =কোনও সমস্যা ছাড়াই কোনও ফাংশন (বা অপারেটর ব্যবহার করুন ) থেকে কোনও কাঠামো ফিরে আসতে পারেন । এটি ভাষার একটি সংজ্ঞায়িত অংশ। শুধুমাত্র সমস্যাটি struct b = aহ'ল আপনি একটি সম্পূর্ণ ধরণ সরবরাহ করেন নি। struct MyObj b = aঠিক কাজ করবে। আপনি স্ট্রাকচার পাস করতে পারেন থেকে পাশাপাশি ফাংশন - একটি কাঠামো ঠিক হিসাবে একই কোনো বিল্ট-ইন প্যারামিটার পাসিং, এর বিনিময়ে ভ্যালু ও নিয়োগ উদ্দেশ্যে প্রকার।

এখানে একটি সাধারণ বিক্ষোভ প্রোগ্রাম যা তিনটিই করে - একটি প্যারামিটার হিসাবে একটি কাঠামো পাস করে, একটি ফাংশন থেকে কাঠামো ফেরত দেয় এবং অ্যাসাইনমেন্ট বিবৃতিতে কাঠামো ব্যবহার করে:

#include <stdio.h>

struct a {
   int i;
};

struct a f(struct a x)
{
   struct a r = x;
   return r;
}

int main(void)
{
   struct a x = { 12 };
   struct a y = f(x);
   printf("%d\n", y.i);
   return 0;
}

পরবর্তী উদাহরণটি বেশ হুবহু একই রকম, তবে অন্তর্নির্মিত intপ্রকারটি প্রদর্শনের উদ্দেশ্যে ব্যবহার করে। প্যারামিটার পাসিং, অ্যাসাইনমেন্ট ইত্যাদির জন্য পাস-বাই-ভ্যালু সম্পর্কিত দুটি প্রোগ্রামের একই আচরণ রয়েছে .:

#include <stdio.h>

int f(int x) 
{
  int r = x;
  return r;
}

int main(void)
{
  int x = 12;
  int y = f(x);
  printf("%d\n", y);
  return 0;
}

14
এটা বেশ আকর্ষণীয়। আমি সর্বদা আপনার এইগুলির জন্য পয়েন্টার প্রয়োজন ছাপের মধ্যে ছিলাম। আমি ভুল করেছিলাম :)
মিমারজাদেঃ

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

10
@ কার্লনোরম একটি কাঠামোটি কতটা বড় পেতে পারে যে কোনও অনুলিপি ম্যালোক + বিনামূল্যেের চেয়ে বেশি খরচ হয়?
জোসেফেক্স

7
@ জোসেফএক্স, একক অনুলিপি? সম্ভবত বিশাল। জিনিসটি হ'ল, সাধারণত যদি আপনি মূল্য দিয়ে কাঠামোটি চারপাশে পাস করছেন তবে আপনি সেগুলি অনেকগুলি অনুলিপি করছেন । যাইহোক এটি আসলে এতটা সহজ নয়। আপনি স্থানীয় বা বৈশ্বিক কাঠামোগুলির কাছাকাছি যেতে পারছেন, এই ক্ষেত্রে তাদের বরাদ্দ ব্যয়টি বেশ বিনামূল্যে।
কার্ল নরম

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

33

যেমন একটি কল করার সময় a = foo();, সংকলক ফলাফল কাঠামোর ঠিকানাটি স্ট্যাকের উপর চাপ দিতে পারে এবং এটিকে foo()ফাংশনে একটি "লুকানো" পয়েন্টার হিসাবে পাস করে । কার্যকরভাবে, এটি এমন কিছু হতে পারে:

void foo(MyObj *r) {
    struct MyObj a;
    // ...
    *r = a;
}

foo(&a);

তবে এর সঠিক বাস্তবায়ন সংকলক এবং / অথবা প্ল্যাটফর্মের উপর নির্ভরশীল। কার্ল নরুম নোট অনুসারে, কাঠামোটি যদি যথেষ্ট ছোট হয় তবে এটি একটি রেজিস্টারে সম্পূর্ণরূপে ফিরে যেতে পারে।


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

এটি কি কোনও স্থানীয় ভেরিয়েবলে পয়েন্টার ফিরিয়ে দিবে না? ফিরে আসা কাঠামোর জন্য স্মৃতি fooস্ট্যাক ফ্রেমের অংশ হতে পারে না । এটি এমন জায়গায় থাকতে হবে যা ফিরে আসার পরে বেঁচে থাকবে foo
অ্যান্ডারস আবেল

@ আন্ডারএবেল: আমি মনে করি গ্রেগ বলতে যা বোঝায় তার অর্থ হ'ল কম্পাইলার মূল ফাংশনের ভেরিয়েবলের দিকে একটি পয়েন্টার নেয় এবং এটি ফাংশনে প্রেরণ করে foo। ফাংশনের অভ্যন্তরে foo, আপনি কেবল অ্যাসাইনমেন্টটি করেন
মিমিরজাদে

4
@ আন্ডারএবেল: *r = aশেষে শেষে (কার্যকরভাবে) কলকারের ভেরিয়েবলের স্থানীয় ভেরিয়েবলের একটি অনুলিপি করতে হবে। আমি বলি "কার্যকরভাবে" কারণ সংকলকটি আরভিও বাস্তবায়ন করতে পারে এবং স্থানীয় ভেরিয়েবল aসম্পূর্ণরূপে নির্মূল করতে পারে।
গ্রেগ হিউগিল

3
যদিও এটি সরাসরি প্রশ্নের উত্তর দেয় না, এই কারণেই অনেক লোক c return structগুগলের মাধ্যমে এখানে পড়ে যাবেন : তারা জানেন যে সিডিসিতে eaxমান দ্বারা ফিরে আসে এবং সাধারণভাবে স্ট্রাইকগুলি অভ্যন্তরের সাথে খাপ খায় না eax। এই আমি খুঁজছিলাম ছিল।
সিরো সান্তিলি 郝海东 冠状 病 六四 事件

14

struct bকারণ এটি একটি বাক্য গঠন ত্রুটি লাইন কাজ করে না। আপনি যদি প্রকারটি অন্তর্ভুক্ত করতে এটি প্রসারিত করেন তবে এটি ঠিক কাজ করবে

struct MyObj b = a;  // Runs fine

সি এখানে যা করছে তা মূলত memcpyউত্স কাঠামো থেকে গন্তব্য পর্যন্ত। এটি মূল্য নির্ধারণ এবং প্রত্যাবর্তনের উভয়ের ক্ষেত্রেই সত্য struct(এবং সত্যিকার অর্থে সি এর প্রতিটি মান)


+1, বাস্তবে, অনেক সংকলক প্রকৃতপক্ষে memcpyএই ক্ষেত্রে একটি আক্ষরিক কল প্রেরণ করবে - কমপক্ষে, যদি কাঠামোটি যুক্তিসঙ্গতভাবে বড় হয়।
কার্ল নরম

সুতরাং, একটি ডেটাটাইপ শুরু করার সময়, মেমকি ফাংশন কাজ করে ??
ভুবনসাহনী

1
@ ভূবনসাহনী আপনি এখানে কী জিজ্ঞাসা করছেন তা আমি নিশ্চিত নই। আপনি কিছুটা ব্যাখ্যা করতে পারেন?
জেয়ার্ডপাড়

4
@ জারেডপার - সংকলকরা প্রায়শই কাঠামোগত অবস্থার জন্য ফাংশনটি কল করেন memcpy। আপনি একটি দ্রুত পরীক্ষা প্রোগ্রাম তৈরি করতে এবং জিসিসি এটি দেখতে দেখুন, উদাহরণস্বরূপ। অন্তর্নির্মিত প্রকারের জন্য যা ঘটবে না - সে ধরণের অপ্টিমাইজেশানটি ট্রিগার করার মতো যথেষ্ট বড় নয়।
কার্ল নরুম

3
এটি ঘটানো নিশ্চিতভাবেই সম্ভব - প্রজেক্টটিতে আমি যে প্রকল্পে কাজ করছি তার কোনও memcpyসংকেত সংজ্ঞায়িত করা হয় না , তাই সংকলক যখন নিজের থেকে আলাদা করে ফেলার সিদ্ধান্ত নেয় তখন আমরা প্রায়শই "অপরিজ্ঞাত প্রতীক" লিংক ত্রুটিতে চলে যাই।
কার্ল নরুম

9

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

আসলে আমি তখনও জানতে পেরেছিলাম যখন আমি পয়েন্টার বা গ্লোবাল ভেরিয়েবল ব্যবহার না করে ফাংশনের জন্য একাধিক মানের ফেরত দেওয়ার জন্য আরও ভাল সমাধান বের করার চেষ্টা করছি

এখন নীচে একই জন্য উদাহরণ, যা একজন শিক্ষার্থীর গড় গড়ের বিচ্যুতি গণনা করে।

#include<stdio.h>
struct marks{
    int maths;
    int physics;
    int chem;
};

struct marks deviation(struct marks student1 , struct marks student2 );

int main(){

    struct marks student;
    student.maths= 87;
    student.chem = 67;
    student.physics=96;

    struct marks avg;
    avg.maths= 55;
    avg.chem = 45;
    avg.physics=34;
    //struct marks dev;
    struct marks dev= deviation(student, avg );
    printf("%d %d %d" ,dev.maths,dev.chem,dev.physics);

    return 0;
 }

struct marks deviation(struct marks student , struct marks student2 ){
    struct marks dev;

    dev.maths = student.maths-student2.maths;
    dev.chem = student.chem-student2.chem;
    dev.physics = student.physics-student2.physics; 

    return dev;
}

5

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

আরও সাম্প্রতিক সংস্করণগুলি স্ট্র্টকের মতো বৃহত্তর ডেটা অবজেক্টের কাছাকাছি যেতে দেয়। আমি মনে করি আশির দশক বা নব্বইয়ের দশকের গোড়ার দিকে এই বৈশিষ্ট্যটি ইতিমধ্যে সাধারণ ছিল।

অ্যারেগুলি এখনও পয়েন্টার হিসাবে পাস এবং ফিরে যেতে পারে।


আপনি যদি এটি কোনও স্ট্রাক্টের ভিতরে রাখেন তবে মান দ্বারা একটি অ্যারে ফিরিয়ে দিতে পারেন। আপনি মান অনুসারে যা ফিরিয়ে দিতে পারবেন না তা হ'ল পরিবর্তনশীল দৈর্ঘ্যের অ্যারে।
হান

1
হ্যাঁ, আমি কোনও কাঠামোর ভিতরে একটি অ্যারে রাখতে পারি, তবে আমি উদাহরণস্বরূপ টাইপডেফ অক্ষর লিখতে পারি না [100]; arr foo () {...} আকারটি জানা থাকলেও একটি অ্যারে ফিরিয়ে দেওয়া যায় না।
জর্জিও

ডাউনভোটার কি ডাউনটোটের কারণ ব্যাখ্যা করতে পারে? আমার উত্তরে যদি ভুল তথ্য থাকে তবে আমি এটিকে ঠিক করতে পেরে খুশি হব।
জর্জিও

4

আপনি সি তে স্ট্রাইক বরাদ্দ করতে পারবেনa = b; বৈধ বাক্য গঠন।

আপনার লাইনে কাজ করে না এমন আপনি কেবল টাইপের কিছু অংশ রেখেছেন - স্ট্রাক্ট ট্যাগ।


4

কাঠামো পিছনে কোনও সমস্যা নেই। এটি মান দ্বারা পাস করা হবে

তবে, স্ট্রাক্টে কোনও সদস্য রয়েছে যার একটি স্থানীয় ভেরিয়েবলের ঠিকানা রয়েছে

struct emp {
    int id;
    char *name;
};

struct emp get() {
    char *name = "John";

    struct emp e1 = {100, name};

    return (e1);
}

int main() {

    struct emp e2 = get();

    printf("%s\n", e2.name);
}

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

যেখানে e1.id পুরোপুরি বৈধ হবে কারণ এর মানটি e2.id তে অনুলিপি করা হবে

সুতরাং, আমাদের সর্বদা কোনও ফাংশনের স্থানীয় মেমরি ঠিকানাগুলি এড়াতে চেষ্টা করা উচিত।

অপ্রত্যাশিত যে কোনও কিছু যখন চাইবে ফিরে আসতে পারে


2
struct emp {
    int id;
    char *name;
};

struct emp get() {
    char *name = "John";

    struct emp e1 = {100, name};

    return (e1);
}

int main() {

    struct emp e2 = get();

    printf("%s\n", e2.name);
}

সংকলকগুলির নতুন সংস্করণগুলির সাথে সূক্ষ্মভাবে কাজ করে। আইডির মতোই, নামের বিষয়বস্তু নির্ধারিত কাঠামোর ভেরিয়েবলে অনুলিপি করে।


1
এমনকি সরল: স্ট্রাক্ট এম্প গিগ () {রিটার্ন {100, "জন"}; }
ক্রিস রেড

1

স্ট্রাক var e2 ঠিকানাকে কলি স্ট্যাককে আর্গ হিসাবে ঠেলে দেওয়া হয় এবং মানগুলি সেখানে নির্ধারিত হয়। প্রকৃতপক্ষে, পান () ইক্সের রেগে ই 2 এর ঠিকানা ফেরত দেয়। এটি রেফারেন্স দ্বারা কল করার মতো কাজ করে।

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