সি এর মধ্যে একটি স্ট্রিং আক্ষরিক "লাইফ-টাইম"


85

নিম্নলিখিত ফাংশনটি দিয়ে ফিরে আসা পয়েন্টারটি কি অ্যাক্সেসযোগ্য হবে না?

সুতরাং সি / সি ++ এর স্থানীয় ভেরিয়েবলের আজীবন কার্যত কেবল ফাংশনের মধ্যেই ডান? যার অর্থ, char* foo(int)সমাপ্তির পরে , যে পয়েন্টারটি এটি ফিরে আসে তার কোনও মানে হয় না, তাই না?

স্থানীয় ভেরিয়েবলের জীবনকাল সম্পর্কে আমি কিছুটা বিভ্রান্ত। একটি ভাল ব্যাখ্যা কি?


10
আপনার ফাংশনে কেবলমাত্র "var" হ'ল প্যারামিটার int rc। এর জীবদ্দশায় প্রতিটি এক-এ শেষ হয় return। আপনি যে পয়েন্টারগুলি ফিরিয়ে দিচ্ছেন তা হ'ল স্ট্রিংগুলিতে। স্ট্রিং লিটারেলের স্থিতিশীল স্টোরেজ সময়কাল থাকে: তাদের জীবনকাল কমপক্ষে প্রোগ্রামের মতো দীর্ঘ হয়।
কাজ

14
@ পেড্রো আলভেস কেন? পদ্ধতিগুলি বিমূর্তনের অনুমতি দেয়; ভবিষ্যতে যদি স্ট্রিংটি অনুবাদ সংস্থান থেকে পড়তে হবে তবে কোনও পণ্যের ভি 1 (বা ভি0.5) এর জন্য আন্তর্জাতিকীকরণের সহায়তা প্রয়োজন নেই?
dlev

4
@ পেড্রোআলভস "আপনার কোডটি নিশ্চিতভাবে কাজ করে (এবং সংকলনের চেষ্টা করলে আপনি এটি দেখতে পারেন)," এটি অনুসরণ করে না। অনেক (সর্বাধিক? মূলত প্রত্যেকটি?) সি সংকলক অবৈধ কোড গ্রাস করবে এবং প্রায়শই কোড প্রকাশ করে যা কাজ করে appears তবে এটি অন্য সংকলক (বা একই সংকলকটির আলাদা সংস্করণ) এ চেষ্টা করুন এবং এটি পড়ে যেতে পারে।
ডিএমকেকে --- প্রাক্তন মডারেটর বিড়ালছানা

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

4
strerrorস্পষ্টতই আপনি ফাংশনটি কখনও দেখেন নি ।
কাজ

উত্তর:


86

হ্যাঁ, স্থানীয় ভেরিয়েবলের আজীবন সময়টি ( {, }) তৈরি করা হয় যেখানে এটি তৈরি করা হয়।

স্থানীয় ভেরিয়েবলগুলির স্বয়ংক্রিয় বা স্থানীয় স্টোরেজ রয়েছে। স্বয়ংক্রিয় কারণ সেগুলি তৈরি হওয়ার সুযোগটি শেষ হলে তারা স্বয়ংক্রিয়ভাবে ধ্বংস হয়ে যায়।

যাইহোক, আপনার এখানে যা আছে তা একটি স্ট্রিং আক্ষরিক, যা একটি বাস্তবায়ন সংজ্ঞায়িত পঠনযোগ্য মেমরির মধ্যে বরাদ্দ করা হয়। স্ট্রিং লিটারালগুলি স্থানীয় ভেরিয়েবল থেকে পৃথক এবং প্রোগ্রামের আজীবন এগুলি জীবিত থাকে। তাদের স্থায়ী সময়কাল [রেফার 1] আজীবন রয়েছে।

সাবধানতার কথা!

তবে নোট করুন যে স্ট্রিং আক্ষরিক বিষয়বস্তুগুলিকে সংশোধন করার যে কোনও প্রয়াস হ'ল একটি অপরিজ্ঞাত আচরণ (ইউবি)। ব্যবহারকারী প্রোগ্রামগুলিকে স্ট্রিং আক্ষরিক বিষয়বস্তু সংশোধন করার অনুমতি নেই।
সুতরাং, constস্ট্রিংকে আক্ষরিক ঘোষণা করার সময় কিছুটা সময় ব্যবহার করতে এটি সর্বদা উত্সাহিত হয় ।

পরিবর্তে,

প্রকৃতপক্ষে, সি ++ তে এটি স্ট্রিংকে আক্ষরিক হিসাবে ঘোষণা করা হ্রাস করা হয়েছে constযদিও সি তে নেই তবে যাইহোক, স্ট্রিংকে আক্ষরিক ঘোষণা constদিয়ে আপনাকে এই সুবিধাটি দেওয়া হয় যে আপনি স্ট্রিংকে আক্ষরিক সংশোধন করার চেষ্টা করলে কম্পাইলাররা সাধারণত আপনাকে একটি সতর্কতা দেয় দ্বিতীয় কেস

নমুনা প্রোগ্রাম :

আউটপুট:

সিসি 1: সতর্কতাগুলি ত্রুটিগুলি
প্রগ হিসাবে বিবেচিত হবে :
ফাংশনটিতে 'প্রধান': প্রগ্যাক: 9: ত্রুটি: ' স্ট্রাইকপি'-র 1 টি আর্গুমেন্ট পয়েন্টার টার্গেট টাইপ থেকে বাছাইয়ের যোগ্যতা ছাড়বে

লক্ষ্য করুন যে সংকলকটি দ্বিতীয় মামলার জন্য সতর্ক করে, তবে প্রথমটির জন্য নয়।


বেশ কয়েকটি ব্যবহারকারী এখানে জিজ্ঞাসা করা প্রশ্নের উত্তর দিতে:

অবিচ্ছেদ্য আক্ষরিক সাথে কি চুক্তি?

অন্য কথায়, নিম্নলিখিত কোডটি বৈধ?

উত্তরটি হল, কোনও এই কোডটি বৈধ নয়। এটি দুর্গঠিত এবং একটি সংকলক ত্রুটি দেবে।

কিছুটা এইরকম:

স্ট্রিং লিটারেলগুলি হ'ল এল-মান, যেমন: আপনি স্ট্রিং আক্ষরিকের ঠিকানা নিতে পারেন, তবে এর সামগ্রীগুলি পরিবর্তন করতে পারবেন না।
যাইহোক, অন্য কোন লিটারেল ( int, float, char, ইত্যাদি) R-মান (সি মান শব্দটি ব্যবহার হয় একটি অভিব্যক্তি মান এবং তাদের ঠিকানা আদৌ নেওয়া যাবে না এই জন্য)।


[রেফার 1] সি 99 স্ট্যান্ডার্ড 6.4.5 / 5 "স্ট্রিং লিটারালস - শব্দার্থবিজ্ঞান":

অনুবাদ পর্বে In-এ, বাইট বা মান শূন্যের কোডটি প্রতিটি মাল্টবাইট চরিত্রের অনুক্রমের সাথে যুক্ত হয় যা স্ট্রিং আক্ষরিক বা আক্ষরিক থেকে ফলাফল results মাল্টিবাইট চরিত্রের সিক্যুয়েন্সটি পরে স্থির স্টোরেজ সময়কাল এবং সিক্যুয়েন্সটি পর্যাপ্ত রাখার জন্য যথেষ্ট দৈর্ঘ্যের একটি অ্যারে শুরু করতে ব্যবহৃত হয় । চরিত্রের স্ট্রিং লিটারালগুলির জন্য, অ্যারের উপাদানগুলিতে টাইপ চর থাকে এবং মাল্টিবাইট অক্ষর ক্রমের পৃথক বাইটগুলির সাথে আরম্ভ করা হয়; প্রশস্ত স্ট্রিং লিটারালগুলির জন্য, অ্যারের উপাদানগুলির মধ্যে wchar_t টাইপ থাকে এবং বিস্তৃত অক্ষরের ক্রম দিয়ে শুরু করা হয় ...

এই অ্যারেগুলি পৃথক পৃথক কিনা এগুলি নির্ধারিত নয় যদি তাদের উপাদানগুলির উপযুক্ত মান থাকে। প্রোগ্রামটি যদি এমন অ্যারে সংশোধন করার চেষ্টা করে তবে আচরণটি সংজ্ঞায়িত


যদি ব্যবহারকারী এই জাতীয় কিছু ফিরিয়ে দেয়। চর * a = & "abc"; ফিরে a; এটি কি বৈধ হবে না?
আশ্বিন

@ আশউইন: আক্ষরিক স্ট্রিংয়ের ধরণটি char (*)[4]। এটি কারণ, "এবিসি" টাইপ হয় char[4]এবং 4 টি বর্ণের অ্যারেরের পয়েন্টার হিসাবে ঘোষণা করা হয় char (*)[4], সুতরাং আপনার যদি এটির ঠিকানা নেওয়া দরকার হয় তবে আপনার এটি করা উচিত char (*a)[4] = &"abc";এবং হ্যাঁ, এটি বৈধ।
অলোক

@ সব "এবিসি" হয় char[4]। (কারণ '\0')
asaelr

4
হয়তো এটা একটি ভাল ধারণা সতর্ক করতে যে হবে char const s[] = "text";না না করতে sএকটি অক্ষর আক্ষরিক, সেইজন্য এবং s হবে , সুযোগ শেষে ধ্বংস হয়ে যাবে তাই এটি কোনো জীবিত পয়েন্টার দোলা হবে।
celtschk

4
@ এসলেটস্ক্ক: আমি পছন্দ করব, তবে প্রশ্নটি বিশেষত স্ট্রিং লিটারাল সম্পর্কে রয়েছে তাই আমি এই বিষয়টি হাতে নিয়েই থাকব, যাইহোক, আমার উত্তর সম্পর্কে আগ্রহী ব্যক্তিদের জন্য এখানে চর [[] = "স্ট্রিং" এবং চরের মধ্যে পার্থক্য কী? * পি = "স্ট্রিং"? বরং সহায়ক হতে হবে।
অলোক সেভ

74

এটা বৈধ। স্ট্রিং লিটারেলের স্থিতিশীল স্টোরেজ সময়কাল থাকে তাই পয়েন্টারটি ঝোলা হয় না।

সি এর জন্য, এটি বিভাগ 6.4.5, অনুচ্ছেদ 6 এ বাধ্যতামূলক করা হয়েছে:

অনুবাদ পর্বে In-এ, বাইট বা মান শূন্যের কোডটি প্রতিটি মাল্টবাইট চরিত্রের অনুক্রমের সাথে যুক্ত হয় যা স্ট্রিং আক্ষরিক বা আক্ষরিক থেকে ফলাফল results মাল্টিবাইট চরিত্রের সিক্যুয়েন্সটি পরে স্থির স্টোরেজ সময়কাল এবং সিক্যুয়েন্সটি পর্যাপ্ত রাখার জন্য যথেষ্ট দৈর্ঘ্যের একটি অ্যারে শুরু করতে ব্যবহৃত হয়

এবং বিভাগে 2.14.5 এর C ++ এর জন্য অনুচ্ছেদ 8-11:

8 সাধারণ স্ট্রিং লিটারাল এবং ইউটিএফ -8 স্ট্রিং লিটারালগুলিকে সংকীর্ণ স্ট্রিং লিটারালও বলা হয়। একটি সংকীর্ণ স্ট্রিং আক্ষরিক ধরণের "অ্যারের অ্যারে const char" রয়েছে, যেখানে এন নীচে সংজ্ঞায়িত স্ট্রিংয়ের আকার এবং স্থিতিশীল স্টোরেজ সময়কাল (3.7) থাকে।

9 একটি স্ট্রিং আক্ষরিক যা আপনার সাথে শুরু হয় u"asdf", যেমন একটি char16_tস্ট্রিং আক্ষরিক। একটি char16_tস্ট্রিং আক্ষরিকের মধ্যে "এন অ্যারে" টাইপ থাকে const char16_t, যেখানে এন নীচে সংজ্ঞায়িত স্ট্রিংয়ের আকার হয়; এটির স্ট্যাটিক স্টোরেজ সময়কাল এবং প্রদত্ত অক্ষরগুলির সাথে আরম্ভ করা হয়। একটি একক সি-চর char16_tসারোগেট জোড়া হিসাবে আকারে একাধিক অক্ষর তৈরি করতে পারে ।

10 একটি স্ট্রিং আক্ষরিক যা ইউ দিয়ে শুরু হয় U"asdf"যেমন একটি char32_tস্ট্রিং আক্ষরিক। একটি char32_tস্ট্রিং আক্ষরিকের মধ্যে "এন অ্যারে" টাইপ থাকে const char32_t, যেখানে এন নীচে সংজ্ঞায়িত স্ট্রিংয়ের আকার হয়; এটির স্ট্যাটিক স্টোরেজ সময়কাল এবং প্রদত্ত অক্ষরগুলির সাথে আরম্ভ করা হয়।

11 একটি স্ট্রিং আক্ষরিক যা এল দিয়ে শুরু হয় L"asdf"যেমন একটি প্রশস্ত স্ট্রিং আক্ষরিক। একটি প্রশস্ত স্ট্রিং আক্ষরিকের মধ্যে "এন অ্যারে" টাইপ রয়েছে const wchar_t, যেখানে এন নীচে সংজ্ঞায়িত স্ট্রিংয়ের আকার; এটির স্ট্যাটিক স্টোরেজ সময়কাল এবং প্রদত্ত অক্ষরগুলির সাথে আরম্ভ করা হয়।


অবগতির জন্য: এই উত্তর থেকে মিশে গেছে stackoverflow.com/questions/16470959/...
Shog9

14

স্ট্রিং লিটারেলগুলি পুরো প্রোগ্রামের জন্য বৈধ (এবং স্ট্যাকের বরাদ্দ নয়), সুতরাং এটি বৈধ হবে।

এছাড়াও, স্ট্রিং লিটারেল শুধুমাত্র পাঠযোগ্য, তাই (ভাল শৈলী জন্য) হয়ত আপনি পরিবর্তন করা উচিত fooকাছেconst char *foo(int)


যদি ব্যবহারকারী এই জাতীয় কিছু ফিরিয়ে দেয়। চর * a = & "abc"; ফিরে a; এটি কি বৈধ হবে না?
আশ্বিন

&"abc"না char*। এটি অ্যারের ঠিকানা এবং এটির ধরণ char(*)[4]। তবে, হয় return &"abc";এবং char *a="abc";return a;বৈধ।
asaelr

@ এসেলার: আসলে, এটি কেবল ভাল স্টাইলের জন্য নয়, বিশদগুলির জন্য আমার উত্তরটি পরীক্ষা করুন।
অলোক

@ সব ঠিক আছে, যদি তিনি পুরো প্রোগ্রামটি লিখেন তবে তিনি লেখার পরিবর্তে স্ট্রিং পরিবর্তন এড়াতে পারবেন constএবং এটি সম্পূর্ণ আইনী হবে তবে এটি এখনও খারাপ শৈলী।
asaelr

যদি এটি পুরো প্রোগ্রামটির জন্য বৈধ হয় তবে আমাদের এটি কেন ম্যালোক করা দরকার?
টমসওয়ায়ার

7

হ্যাঁ, এটি বৈধ কোড, নীচে কেস 1 দেখুন। আপনি কোনও ফাংশন থেকে কমপক্ষে এই উপায়ে নিরাপদে সি স্ট্রিংগুলি ফিরিয়ে দিতে পারেন:

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

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

  • char*সঙ্গে বরাদ্দ একটি বাফার malloc। এটি সংশোধন করা যেতে পারে, তবে অবশ্যই এটি অবশ্যই স্পষ্টভাবে কলার দ্বারা মুক্তি দিতে হবে এবং গাদা বরাদ্দের ওভারহেড থাকতে হবে। strdupএই ধরণের হয়।

  • const char*বা char*কোনও বাফারকে, যা ফাংশনটির আর্গুমেন্ট হিসাবে পাস হয়েছিল (প্রত্যাবর্তিত পয়েন্টারটিকে আর্গুমেন্ট বাফারের প্রথম উপাদানটির দিকে নির্দেশ করার দরকার নেই)। এটি বাফার / মেমরি পরিচালনার দায়িত্ব কলারের উপরে ছেড়ে দেয়। অনেক স্ট্যান্ডার্ড স্ট্রিং ফাংশন এই ধরণের হয়।

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


অবগতির জন্য: এই উত্তর থেকে মিশে গেছে stackoverflow.com/questions/16470959/...
Shog9

6

ভাল প্রশ্ন. সাধারণভাবে, আপনি সঠিক হবে, তবে আপনার উদাহরণটি ব্যতিক্রম। সংকলক স্থিতিশীলভাবে একটি স্ট্রিং আক্ষরিক জন্য গ্লোবাল মেমরি বরাদ্দ করে। সুতরাং, আপনার ফাংশন দ্বারা ফিরে ঠিকানা বৈধ।

এটি সি এর পরিবর্তে সুবিধাজনক বৈশিষ্ট্য, তাই না? এটি কোনও ফাংশনটিকে প্রোগ্রামারকে মেমরিটি সঞ্চিত আছে সেই মেমরির বিষয়ে চিন্তা করতে বাধ্য না করে কোনও পূর্বনির্ধারিত বার্তা ফেরত দেয়।

এছাড়াও @ asaelr এর সঠিক পর্যবেক্ষণ পুনরায় দেখুন const


: ব্যবহারকারী যদি এরকম কিছু ফিরিয়ে দেয় তবে কী হবে। চর * a = & "abc"; ফিরে a; এটি কি বৈধ হবে না?
আশ্বিন

ঠিক। আসলে, কেউ কেবল লিখতে পারে const char *a = "abc";, বাদ দিয়ে &। কারণটি হ'ল ডাবল-কোটেড স্ট্রিংটি তার প্রাথমিক অক্ষরের ঠিকানার সমাধান করে।
thb

3

স্থানীয় ভেরিয়েবলগুলি কেবলমাত্র তাদের ঘোষিত সুযোগের মধ্যে বৈধ হয় তবে আপনি সেই ফাংশনে কোনও স্থানীয় ভেরিয়েবল ঘোষণা করেন না।

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

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


যদি ব্যবহারকারী এই জাতীয় কিছু ফিরিয়ে দেয়। চর * a = & "abc"; ফিরে a; এটি কি বৈধ হবে না?
আশ্বিন

@ আশ্বিন: প্রকারভেদ &"abc"নয় char*, তবে উভয়ই "abc"এবং &"abc"পুরো প্রোগ্রামের পুরো প্রয়োগের ক্ষেত্রে বৈধ।
অসি ব্লক

2

strকখনই বিড়ম্বনা পয়েন্টার হতে পারে না, কারণ এটি স্ট্যাটিক ঠিকানার দিকে নির্দেশ করে যেখানে স্ট্রিং লিটারেলগুলি থাকে।

প্রোগ্রামটি লোড হয়ে গেলে এটি বেশিরভাগই পঠনযোগ্য এবং বৈশ্বিক হবে।

এমনকি আপনি মুক্ত বা সংশোধন করার চেষ্টা করলেও এটি মেমরির সুরক্ষা সহ প্ল্যাটফর্মগুলিতে একটি সেগমেন্টেশন ত্রুটি ফেলবে ।


অবগতির জন্য: এই উত্তর থেকে মিশে গেছে stackoverflow.com/questions/16470959/...
Shog9

যদি এটি কখনই ঝুঁকছে না, আমার কি এটি ম্যালোক করা দরকার? না?
টমসওয়ায়ার

0

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


যদি ব্যবহারকারী এই জাতীয় কিছু ফিরিয়ে দেয়। চর * a = & "abc"; ফিরে a; এটি কি বৈধ হবে না?
আশ্বিন

0

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

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