স্ট্রিংস্ট্রিম, স্ট্রিং এবং চর * রূপান্তর বিভ্রান্তি


141

আমার প্রশ্নটিতে সেদ্ধ করা যেতে পারে, stringstream.str().c_str()লাইভ ইন মেমরি থেকে স্ট্রিংটি কোথায় ফিরে আসে এবং কেন এটি একটিতে নির্ধারিত করা যায় না const char*?

এই কোড উদাহরণটি এটির চেয়ে ভাল ব্যাখ্যা করবে

#include <string>
#include <sstream>
#include <iostream>

using namespace std;

int main()
{
    stringstream ss("this is a string\n");

    string str(ss.str());

    const char* cstr1 = str.c_str();

    const char* cstr2 = ss.str().c_str();

    cout << cstr1   // Prints correctly
        << cstr2;   // ERROR, prints out garbage

    system("PAUSE");

    return 0;
}

অনুমান যে stringstream.str().c_str() একটি বরাদ্দ করা যেতে পারে const char*তা একটি বাগের দিকে নিয়ে যায় যা ট্র্যাক ডাউন করতে আমাকে কিছুটা সময় নেয়।

বোনাস পয়েন্টের জন্য, যে কেউ প্রতিস্থাপন করতে পারে তা কেন ব্যাখ্যা করতে পারে cout বিবৃতিটি সাথে

cout << cstr            // Prints correctly
    << ss.str().c_str() // Prints correctly
    << cstr2;           // Prints correctly (???)

স্ট্রিংগুলি সঠিকভাবে মুদ্রণ করে?

আমি ভিজ্যুয়াল স্টুডিও ২০০৮-এ সংকলন করছি।

উত্তর:


201

stringstream.str()সম্পূর্ণ প্রকাশের শেষে ধ্বংস হওয়া একটি অস্থায়ী স্ট্রিং অবজেক্ট প্রদান করে। যদি আপনি ( stringstream.str().c_str()) থেকে কোনও সি স্ট্রিংয়ের পয়েন্টার পান তবে এটি স্ট্রিংকে নির্দেশ করবে যা মুছে ফেলা হয়েছে যেখানে স্টেটমেন্ট শেষ হবে। এজন্য আপনার কোডটি আবর্জনা মুদ্রণ করে।

আপনি সেই অস্থায়ী স্ট্রিং অবজেক্টটিকে অন্য কোনও স্ট্রিং অবজেক্টে অনুলিপি করতে পারেন এবং সেই স্ট্রিং থেকে সি স্ট্রিং নিতে পারেন:

const std::string tmp = stringstream.str();
const char* cstr = tmp.c_str();

নোট করুন যে আমি অস্থায়ী স্ট্রিং করেছি const, কারণ এতে যে কোনও পরিবর্তন এটি পুনরায় বরাদ্দ করতে পারে এবং এভাবে cstrঅবৈধ রেন্ডার হতে পারে । কল করার ফলাফলটি মোটেও সংরক্ষণ না করা এবং সম্পূর্ণ প্রকাশের শেষ না হওয়া পর্যন্ত কেবল str()ব্যবহার করা নিরাপদ cstr:

use_c_str( stringstream.str().c_str() );

অবশ্যই, পরবর্তীগুলি সম্ভবত সহজ নয় এবং অনুলিপি করা খুব ব্যয়বহুল। পরিবর্তে আপনি যা করতে পারেন তা হ'ল অস্থায়ী একটি constরেফারেন্সের সাথে আবদ্ধ । এটি তার আজীবন রেফারেন্সের আজীবন প্রসারিত করবে:

{
  const std::string& tmp = stringstream.str();   
  const char* cstr = tmp.c_str();
}

আইএমও এটিই সেরা সমাধান। দুর্ভাগ্যক্রমে এটি খুব বেশি পরিচিত নয়।


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

1
"অবশ্যই, বাইন্ড-টু-কনস্ট্যান্ড-রেফারেন্স সমাধানটি নন-অনুলিপির গ্যারান্টি দেয়" <- এটি হয় না। সি ++ 03 এ, অনুলিপি নির্মাণকারীকে অ্যাক্সেসযোগ্য হওয়া দরকার এবং প্রয়োগকারীটিকে আরম্ভকারীটিকে অনুলিপি করার এবং কপির সাথে রেফারেন্সটি আবদ্ধ করার অনুমতি দেওয়া হয়।
জোহানেস স্কাউব -

1
আপনার প্রথম উদাহরণটি ভুল। C_str () দ্বারা প্রত্যাবর্তিত মানটি ক্ষণস্থায়ী। বর্তমান বিবৃতি শেষ হওয়ার পরে এটির উপর নির্ভর করা যায় না। সুতরাং আপনি এটি একটি ফাংশনটির মান পাস করতে ব্যবহার করতে পারেন তবে আপনাকে স্থানীয় ভেরিয়েবলের জন্য সি_এসটার () এর ফলাফলটি কখনই অর্পণ করা উচিত নয়।
মার্টিন ইয়র্ক

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

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

13

আপনি যা করছেন তা একটি অস্থায়ী তৈরি করছে। এই অস্থায়ীটি সংকলক দ্বারা নির্ধারিত স্কোপে বিদ্যমান, যেমন এটি কোথায় চলছে তার প্রয়োজনীয়তাগুলি পূরণ করার জন্য এটি যথেষ্ট দীর্ঘ।

বিবৃতিটি const char* cstr2 = ss.str().c_str();সম্পূর্ণ হওয়ার সাথে সাথেই সংকলকটি অস্থায়ী স্ট্রিংটিকে চারপাশে রাখার কোনও কারণ দেখতে পাবে না এবং এটি ধ্বংস হয়ে যায় এবং এভাবে আপনারconst char * মুক্ত স্মৃতিতে ইঙ্গিত করা হচ্ছে।

আপনার বক্তব্যটির string str(ss.str());অর্থ হল যে অস্থায়ীটি আপনি স্থানীয় স্ট্যাকের উপর stringচাপিয়ে strদিয়েছেন তার জন্য কনস্ট্রাক্টরটিতে ব্যবহৃত হয় এবং আপনি যতক্ষণ প্রত্যাশা করতেন ততক্ষণ থাকে: ব্লকের সমাপ্তি অবধি, বা ফাংশন লিখেছেন। অতএব const char *অভ্যন্তরীণটি এখনও ভাল স্মৃতি রয়েছে যখন আপনি চেষ্টা করেন cout


6

এই লাইনে:

const char* cstr2 = ss.str().c_str();

ss.str()স্ট্রিংস্ট্রিমের সামগ্রীগুলির একটি অনুলিপি তৈরি করবে। আপনি যখন c_str()একই লাইনে কল করবেন , আপনি বৈধ ডেটা উল্লেখ করছেন, তবে সেই লাইনের পরে স্ট্রিংটি ধ্বংস হয়ে যাবে, char*আপনাকে অজানা স্মৃতিতে নির্দেশ করে।


5

এসএসডিস্ট্রিং স্ট্রিং অবজেক্ট ss.str () দ্বারা প্রত্যাবর্তিত একটি অস্থায়ী বস্তু যা অভিব্যক্তির মধ্যে সীমাবদ্ধ একটি জীবনকাল থাকবে। সুতরাং আপনি ট্র্যাশ না পেয়ে কোনও অস্থায়ী বস্তুটিতে পয়েন্টার বরাদ্দ করতে পারবেন না।

এখন, একটি ব্যতিক্রম রয়েছে: আপনি যদি অস্থায়ী বস্তুটি পেতে কোনও কনস্ট্যান্ড রেফারেন্স ব্যবহার করেন তবে এটি বৃহত্তর জীবনের জন্য ব্যবহার করা আইনী। উদাহরণস্বরূপ আপনার করা উচিত:

#include <string>
#include <sstream>
#include <iostream>

using namespace std;

int main()
{
    stringstream ss("this is a string\n");

    string str(ss.str());

    const char* cstr1 = str.c_str();

    const std::string& resultstr = ss.str();
    const char* cstr2 = resultstr.c_str();

    cout << cstr1       // Prints correctly
        << cstr2;       // No more error : cstr2 points to resultstr memory that is still alive as we used the const reference to keep it for a time.

    system("PAUSE");

    return 0;
}

এইভাবে আপনি দীর্ঘ সময়ের জন্য স্ট্রিং পাবেন।

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

 std::string resultstr = ss.str();
 const char* cstr2 = resultstr.c_str();

আরও ভাল এবং সহজ হবে।


5

ss.str()অস্থায়ী আরম্ভের পরে ধ্বংস করা হয় এর cstr2সম্পূর্ণ। সুতরাং আপনি যখন এটি দিয়ে মুদ্রণ করবেন cout, সেই std::stringঅস্থায়ীটির সাথে সম্পর্কিত সি-স্ট্রিংটি দীর্ঘকালীন হয়ে গেছে, এবং সুতরাং এটি ভাগ্যবান হবে যদি এটি ক্র্যাশ হয়ে যায় এবং দৃ as়ভাবে দাবি করে, এবং ভাগ্যবান নয় যদি এটি আবর্জনা মুদ্রণ করে বা কাজ করে প্রদর্শিত হয়।

const char* cstr2 = ss.str().c_str();

সি-স্ট্রিং যেখানে cstr1পয়েন্ট করে তবে একটি স্ট্রিংয়ের সাথে সম্পর্কিত যা আপনি করার সময় এখনও বিদ্যমান রয়েছেcout - তাই এটি ফলাফলটি সঠিকভাবে মুদ্রণ করে।

নিম্নলিখিত কোডে, প্রথমটি cstrসঠিক (আমি ধরে নিলাম এটি cstr1বাস্তব কোডে আছে?) দ্বিতীয়টি অস্থায়ী স্ট্রিংয়ের সাথে যুক্ত সি-স্ট্রিং প্রিন্ট করে ss.str()। বস্তুটি সম্পূর্ণ-এক্সপ্রেশন যা এটি প্রদর্শিত হয় তা মূল্যায়ন শেষে ধ্বংস হয়। সম্পূর্ণ-এক্সপ্রেশনটি সম্পূর্ণ cout << ...এক্সপ্রেশন - সুতরাং সি-স্ট্রিং আউটপুট থাকা অবস্থায়, যুক্ত স্ট্রিং অবজেক্টটি এখনও বিদ্যমান। কারণ cstr2- এটি খাঁটি খারাপ কাজ এটি সফল হয়। এটি সম্ভবত অভ্যন্তরীণভাবে নতুন অস্থায়ী জন্য একই স্টোরেজ অবস্থান চয়ন করে যা এটি ইতিমধ্যে শুরুতে ব্যবহৃত অস্থায়ী জন্য বেছে নিয়েছিল cstr2। এটি দুর্ঘটনার পাশাপাশি ঘটতে পারে।

cout << cstr            // Prints correctly
    << ss.str().c_str() // Prints correctly
    << cstr2;           // Prints correctly (???)

এর রিটার্ন c_str() সাধারণত সাধারণত অভ্যন্তরীণ স্ট্রিং বাফারকে নির্দেশ করে - তবে এটি কোনও প্রয়োজন নয়। স্ট্রিংটি একটি বাফার তৈরি করতে পারে যদি এর অভ্যন্তরীণ বাস্তবায়ন উদাহরণস্বরূপ সামঞ্জস্যপূর্ণ না হয় (এটি ভাল সম্ভব - তবে পরবর্তী সি ++ স্ট্যান্ডার্ডে স্ট্রিংগুলি স্বচ্ছলভাবে সংরক্ষণ করা দরকার)।

জিসিসিতে স্ট্রিংগুলি রেফারেন্স গণনা এবং অনুলিপি-অনুলিপি ব্যবহার করে। সুতরাং, আপনি পাবেন যে নিম্নলিখিতটি সত্য রাখে (এটি কমপক্ষে আমার জিসিসি সংস্করণে রয়েছে)

string a = "hello";
string b(a);
assert(a.c_str() == b.c_str());

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

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