স্ট্যান্ড :: স্ট্রিং প্যারামিটারে 0 পাস করা থেকে কীভাবে সেরা রক্ষা করবেন?


20

আমি ঠিক কিছু ঝামেলা বুঝতে পেরেছি। প্রতিবার আমি std::stringকোনও পন্থা লিখেছি যা কোনও পরামিতি হিসাবে গ্রহণ করে , আমি নিজেকে অপরিবর্তিত আচরণের জন্য উন্মুক্ত করেছি।

উদাহরণস্বরূপ, এটি ...

void myMethod(const std::string& s) { 
    /* Do something with s. */
}

... এভাবে বলা যেতে পারে ...

char* s = 0;
myMethod(s);

... এবং এটি রোধ করার জন্য আমি কিছু করতে পারি না (যা আমি অবগত)।

সুতরাং আমার প্রশ্ন: কেউ এ থেকে কীভাবে তাদের রক্ষা করতে পারে?

একমাত্র দৃষ্টিভঙ্গি যা মনে আসে তা হ'ল std::stringপ্যারামিটার হিসাবে স্বীকৃতিপ্রাপ্ত যে কোনও পদ্ধতির দুটি সংস্করণ সর্বদা রচনা করা :

void myMethod(const std::string& s) {
    /* Do something. */
}

void myMethod(char* s) {
    if (s == 0) {
        throw std::exception("Null passed.");
    } else {
        myMethod(string(s));
    }
}

এটি কি সাধারণ এবং / বা গ্রহণযোগ্য সমাধান?

সম্পাদনা: কেউ কেউ উল্লেখ করেছেন যে প্যারামিটার হিসাবে const std::string& sপরিবর্তে আমার গ্রহণ করা উচিত std::string s। আমি রাজী. আমি পোস্টটি পরিবর্তন করেছি। আমি মনে করি না যদিও এই উত্তরটি পরিবর্তন করে।


1
ফুটো বিমূর্ততার জন্য হুর! আমি সি ++ বিকাশকারী নই, তবে স্ট্রিং অবজেক্টের c_strসম্পত্তি পরীক্ষা করতে না পারার কোনও কারণ আছে কি ?
ম্যাসন হুইলার

6
শুধু চর * কনস্ট্রাক্টরে 0 নির্ধারণ করা অপরিজ্ঞাত আচরণ, সুতরাং এটি কলারদের ত্রুটি
র‌্যাচেট ফ্রিক

4
@ratchetfreak আমি জানি না যে char* s = 0এটি অপরিজ্ঞাত । আমি এটি আমার জীবনে কমপক্ষে কয়েকশবার দেখেছি (সাধারণত আকারে char* s = NULL)। আপনার কি এটি ব্যাক আপ করার জন্য একটি রেফারেন্স আছে?
জন ফিৎসপ্যাট্রিক

3
আমি std:string::string(char*)কনস্ট্রাক্টরকে বোঝাতে
চাইছিলাম

2
আমি মনে করি আপনার সমাধানটি ভাল, তবে আপনার কিছু করা উচিত নয় বলে বিবেচনা করা উচিত। :-) আপনার পদ্ধতিটি স্পষ্টভাবে একটি স্ট্রিং নেয়, কোনও বৈধ ক্রিয়া বলার সময় কোনওভাবেই নাল পয়েন্টারটি অতিক্রম করে না - ঘটনাচক্রে যে কোনও কলকারী ঘটনাক্রমে এই পদ্ধতিগুলির সম্পর্কে নুলকে জড়িয়ে দিচ্ছে যত তাড়াতাড়ি এটি তাদের উপরে ফুঁকছে (পরিবর্তে) লগ ফাইলে রিপোর্ট করার চেয়ে উদাহরণস্বরূপ) আরও ভাল। তাহলে সেখানে কম্পাইল সময় তারপর আপনি এটি করতে হবে এ কিছু এটিকে রোধ করার জন্য একটি উপায় ছিল, অন্যথায় আমি এটা রেখে দিতে পারে। এই প্রোগ্রামটিতে। (বিটিডাব্লু, আপনি কি নিশ্চিত যে আপনি const std::string&সেই প্যারামিটারটির জন্য একটি গ্রহণ করতে পারবেন না ...?)
গ্রিম দ্য ওপেনার

উত্তর:


21

আপনার নিজের রক্ষা করা উচিত বলে আমি মনে করি না। এটি কলারের পক্ষে অপরিবর্তিত আচরণ। এটি আপনি নন, কলকারী যিনি কল করেছেন std::string::string(nullptr), এটিই অনুমোদিত নয়। সংকলকটি এটি সংকলন করতে দেয়, তবে এটি অন্যান্য অপরিবর্তিত আচরণগুলিও সংকলন করতে দেয়।

একইভাবে "নাল রেফারেন্স" পাওয়া হবে:

int* p = nullptr;
f(*p);
void f(int& x) { x = 0; /* bang! */ }

যিনি নাল পয়েন্টারটিকে অবহেলা করেন তিনি ইউবি তৈরি করছেন এবং এর জন্য দায়ী।

তদুপরি, অপরিজ্ঞাত আচরণের পরে আপনি নিজেকে রক্ষা করতে পারবেন না , কারণ অপরিবর্তিত অপরিশোধিত আচরণটি কখনও ঘটেনি তা ধরে নেওয়ার পুরোপুরি অধিকার রয়েছে, তাই c_str()শূন্য হলে চেকগুলি অপ্টিমাইজ করা যায়।


এখানে একটি সুন্দর লিখিত মন্তব্য আছে যা একই জিনিসটি বলেছে, তাই আপনাকে অবশ্যই সঠিক হতে হবে। ;-)
গ্রিম দ্য ওপেনার

2
এখানে শব্দগুচ্ছটি ম্যাকিয়াভেলির নয়, মারফির বিরুদ্ধে সুরক্ষিত। একজন ভাল পারদর্শী দূষিত প্রোগ্রামার অসুস্থ রচনাগুলি অবিলম্বে পাঠানোর উপায় খুঁজে পেতে পারে এমনকি তারা যদি যথেষ্ট চেষ্টা করে তবে নাল রেফারেন্স তৈরি করতে পারে, তবে এটি তাদের নিজস্ব কাজ এবং তারা যদি সত্যিই চান তবে আপনি তাদেরও পায়ে গুলি করতে দিতে পারেন। দুর্ঘটনাজনিত ত্রুটিগুলি রোধ করা আপনার পক্ষে সর্বোত্তম প্রত্যাশা করা যেতে পারে। এই ক্ষেত্রে সত্যিই বিরল যে কেউ দুর্ঘটনাক্রমে কোনও চর * এস = 0 এ পাস করবে; একটি ফাংশন যা একটি সুগঠিত স্ট্রিং জন্য জিজ্ঞাসা।
ইয়ংজান

2

নীচের কোডটি সুস্পষ্ট পাসের জন্য একটি সংকলন ব্যর্থতা 0এবং char*মান সহ একটি রান-টাইম ব্যর্থতা দেয় 0

মনে রাখবেন যে আমি সাধারণত বোঝাতে চাই না যে একজনের সাধারণত এটি করা উচিত তবে কোনও সন্দেহ নেই যে ফোনকারী ত্রুটি থেকে সুরক্ষা ন্যায়সঙ্গত হওয়ার ক্ষেত্রেও এমন ঘটনা ঘটতে পারে।

struct Test
{
    template<class T> void myMethod(T s);
};

template<> inline void Test::myMethod(const std::string& s)
{
    std::cout << "Cool " << std::endl;
}

template<> inline void Test::myMethod(const char* s)
{
    if (s != 0)
        myMethod(std::string(s));
    else
    {
        throw "Bad bad bad";
    }
}

template<class T> inline void Test::myMethod(T  s)
{
    myMethod(std::string(s));
    const bool ok = !std::is_same<T,int>::value;
    static_assert(ok, "oops");
}

int main()
{
    Test t;
    std::string s ("a");
    t.myMethod("b");
    const char* c = "c";
    t.myMethod(c);
    const char* d = 0;
    t.myMethod(d); // run time exception
    t.myMethod(0); // Compile failure
}

1

কয়েক বছর আগেও আমি এই সমস্যার মুখোমুখি হয়েছি এবং সত্যিই এটি খুব ভয়ঙ্কর জিনিস বলে আমি পেয়েছি। এটি পাস করার মাধ্যমে nullptrবা ঘটনাক্রমে মান 0 দিয়ে একটি int পাস করে ঘটতে পারে এটি সত্যই অযৌক্তিক:

std::string s(1); // compile error
std::string s(0); // runtime error

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

আমি মনে করি এর সাথে ফাংশনটি ওভারলোড করা const char*ভাল ধারণা।

void foo(std::string s)
{
    // work
}

void foo(const char* s) // use const char* rather than char* (binds to string literals)
{
    assert(s); // I would use assert or std::abort in case of nullptr . 
    foo(std::string(s));
}

আমি আশা করি একটি ভাল সমাধান সম্ভব ছিল। তবে, নেই।



1

আপনার পদ্ধতির স্বাক্ষরটি কীভাবে এতে পরিবর্তন করবেন:

void myMethod(std::string& s) // maybe throw const in there too.

যেভাবে কলকারী কল করার আগে একটি স্ট্রিং তৈরি করতে হবে এবং আপনি যে slালুতা নিয়ে উদ্বিগ্ন তা আপনার পদ্ধতিতে আসার আগেই সমস্যা সৃষ্টি করবে, অন্যরা কী বলেছে, এটি কলারের ত্রুটি আপনার নয়।


হ্যাঁ আমার এটি করা উচিত ছিল const string& s, আমি আসলে ভুলে গিয়েছিলাম। তবে তবুও, আমি কি এখনও অপরিজ্ঞাত আচরণের জন্য ঝুঁকিপূর্ণ নই? কলার এখনও একটি পাস করতে পারেন 0, তাই না?
জন ফিটজপ্যাট্রিক

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

আমি একবার এমন ক্লাসে একটি অ-কনস্ট্যান্ট রেফারেন্স প্যারামিটার ব্যবহার করেছি যেখানে আমাকে এটি সঞ্চয় করতে সক্ষম হতে হয়েছিল এবং তাই নিশ্চিত করেছিলাম যে কোনও রূপান্তর বা অস্থায়ীভাবে প্রবেশ করা হচ্ছে না
ক্যাশকো

1

আপনি কীভাবে ওভারলোড সরবরাহ করবেন তার একটি intপরামিতি লাগে ?

public:
    void myMethod(const std::string& s)
    { 
        /* Do something with s. */
    }    

private:
    void myMethod(int);

এমনকি আপনাকে অতিরিক্ত চাপ বোঝাতে হবে না। কল করার চেষ্টা করা myMethod(0)কোনও লিঙ্কারের ত্রুটিটিকে ট্রিগার করবে।


1
এই প্রশ্নের, যেখানে কোড বিরুদ্ধে রক্ষা করে না 0টি char*প্রকার।
বেন ভয়েগট

0

প্রথম কোড ব্লকে আপনার পদ্ধতিটি কখনও কল করা হবে না যদি আপনি এটি দিয়ে একটি কল করার চেষ্টা করেন (char *)0। সি ++ কেবল একটি স্ট্রিং তৈরি করার চেষ্টা করবে এবং এটি আপনার ব্যতিক্রম ছুঁড়ে দেবে। তুমি কি চেষ্টা করেছ?

#include <cstdlib>
#include <iostream>

void myMethod(std::string s) {
    std::cout << "s=" << s << "\n";
}

int main(int argc,char **argv) {
    char *s = 0;
    myMethod(s);
    return(0);
}


$ g++ -g -o x x.cpp 
$ lldb x 
(lldb) run
Process 2137 launched: '/Users/simsong/x' (x86_64)
Process 2137 stopped
* thread #1: tid = 0x49b8, 0x00007fff99bf9812 libsystem_c.dylib`strlen + 18, queue = 'com.apple.main-thread, stop reason = EXC_BAD_ACCESS (code=1, address=0x0)
    frame #0: 0x00007fff99bf9812 libsystem_c.dylib`strlen + 18
libsystem_c.dylib`strlen + 18:
-> 0x7fff99bf9812:  pcmpeqb (%rdi), %xmm0
   0x7fff99bf9816:  pmovmskb %xmm0, %esi
   0x7fff99bf981a:  andq   $15, %rcx
   0x7fff99bf981e:  orq    $-1, %rax
(lldb) bt
* thread #1: tid = 0x49b8, 0x00007fff99bf9812 libsystem_c.dylib`strlen + 18, queue = 'com.apple.main-thread, stop reason = EXC_BAD_ACCESS (code=1, address=0x0)
    frame #0: 0x00007fff99bf9812 libsystem_c.dylib`strlen + 18
    frame #1: 0x000000010000077a x`main [inlined] std::__1::char_traits<char>::length(__s=0x0000000000000000) + 122 at string:644
    frame #2: 0x000000010000075d x`main [inlined] std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >::basic_string(this=0x00007fff5fbff548, __s=0x0000000000000000) + 8 at string:1856
    frame #3: 0x0000000100000755 x`main [inlined] std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >::basic_string(this=0x00007fff5fbff548, __s=0x0000000000000000) at string:1857
    frame #4: 0x0000000100000755 x`main(argc=1, argv=0x00007fff5fbff5e0) + 85 at x.cpp:10
    frame #5: 0x00007fff92ea25fd libdyld.dylib`start + 1
(lldb) 

দেখা? আপনার উদ্বিগ্ন হওয়ার কিছু নেই।

এখন আপনি যদি এটিকে আরও কিছু করুণভাবে ধরতে চান তবে আপনার কেবল ব্যবহার করা উচিত নয় char *, তাহলে সমস্যাটি দেখা দেবে না।


4
নালপয়েন্টার দিয়ে স্টাডি :: স্ট্রিং তৈরি করা হবে সংজ্ঞায়িত আচরণ
ফ্রিক

2
EXC_BAD_ACCESS ব্যতিক্রমটি শূন্য डीরিফারেন্সের মতো শোনাচ্ছে যা আপনার প্রোগ্রামটি একটি শুভ দিনে একটি সেগফোল্ট দিয়ে ক্র্যাশ করবে
রাচেট ফ্রিক

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

2
@ জনফিটজপ্যাট্রিক আপনি স্ট্যান্ড :: স্ট্রিংয়ে পাস হওয়া নালপয়েন্টার থেকে নিজেকে রক্ষা করতে সক্ষম হতে পারবেন না যদি আপনি মান কমিটিকে এটি অপরিজ্ঞাত আচরণের পরিবর্তে ব্যতিক্রম হতে রাজি করতে না পারেন
র‌্যাচেট ফ্রিক

@ratchetfreak একভাবে আমি মনে করি এটিই আমি উত্তর খুঁজছিলাম। সুতরাং মূলত আমাকে নিজেকে রক্ষা করতে হবে।
জন ফিৎসপ্যাট্রিক

0

আপনি যদি চর সম্পর্কে সম্ভাব্য নাল পয়েন্টার (যেমন বাহ্যিক সি এপিআই থেকে প্রাপ্ত আয়) সম্পর্কে উদ্বিগ্ন হন তবে উত্তরটি হ'ল স্ট্যান্ডিং :: স্ট্রিংয়ের পরিবর্তে ফাংশনের একটি কনস্ট চর * সংস্করণ ব্যবহার করা উচিত। অর্থাত

void myMethod(const char* c) { 
    std::string s(c ? c : "");
    /* Do something with s. */
}

অবশ্যই আপনার যদি স্টাড :: স্ট্রিং সংস্করণ প্রয়োজন হয় তবে আপনি যদি সেগুলি ব্যবহারের অনুমতি দিতে চান তবে।

সাধারণভাবে বহিরাগত API গুলি এবং মার্শাল আর্গুমেন্টগুলিকে বৈধ মানগুলিতে বিচ্ছিন্ন করা ভাল।

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