2014-10-07 117 views
0

在嘗試創建shell的幾天之後,我尋求一些幫助。我已經用不同的數據結構開始了4次左右的時間,並請求解決以下問題。我有一個字符串,我需要分解成單獨的參數,並有一個指針。我最終通過參數傳遞給一個exec功能,但因爲我似乎無法正確填寫ARGS我得到有趣的結果,這裏是一個簡化的版本發生了什麼使用const將std字符串轉換爲char *使用const轉換

char* args[100]; 
int counter=0; 
string temp = "some text and stuff here"; 
stringstream s (temp); 
while(s>> temp) 
{ 
    cout << "TOKEN " << counter << " =" << temp <<endl; 
    args[counter]=const_cast<char *> (temp.c_str()); 
    counter++; 
} 
//print the debug info 
     for(int ii=0; args[ii] != NULL; ii++) 
    { 
     cout << "Argument OUT " << ii << ": " << args[ii] << endl; 
    } 

此代碼不工作,我不能把握,爲什麼。 結果在args的每個值中存儲「here」,但計數器得到更改。

TOKEN 0 =some 
TOKEN 1 =text 
TOKEN 2 =and 
TOKEN 3 =stuff 
TOKEN 4 =here 
Argument OUT 0: here 
Argument OUT 1: here 
Argument OUT 2: here 
Argument OUT 3: here 
Argument OUT 4: here 
+0

c_str返回一個const指針是有原因的! – 2014-10-07 16:34:57

+1

@NeilKirk常量問題與指向同一地點的指針無關。請注意,他需要非const指針,因爲這是exec *()函數系列所要求的,儘管它們不修改傳遞的字符串,所以'const_cast'在這裏是安全的。這是重複使用導致問題的相同'std :: string'對象。 – cdhowie 2014-10-07 16:38:19

回答

1

可能是因爲temp對象正在重新使用其內部分配。當您存儲c_str()結果時,您只能存儲內存地址。 std::string類不會在您每次從字符串流中讀入它時創建一個全新的分配,而是會重新使用它現有的分配(如果可能)。

此外,使用c_str()返回一個指針後,你做了什麼其他到從中獲得調用不確定的行爲std::string對象。

如果可能,只需將args更改爲std::vector<std::string>。如果這是不可能的,那麼你需要strdup()c_str()返回的指針,以便創建一個全新的分配來複制當前字符串的值。當然,當你完成時,你必須記住free()的分配。

此外,鑄造const限定符並寫入指針導致未定義的行爲。 您至少需要將args更改爲const char * args[100];,但我強烈建議使用字符串向量來代替。

  • 傳遞一個非const參照字符串到任何標準庫函數或

  • http://en.cppreference.com/w/cpp/string/basic_string/c_str

    c_str()獲得的指針可以通過被無效

  • 在字符串上調用非const成員函數,不包括operator[]at(),front(),back(),begin(),rbegin(),end()rend()

http://en.cppreference.com/w/cpp/string/basic_string/c_str

寫入通過c_str()訪問的字符數組是未定義的行爲。


基於您的評論表示您需要使用exec(),這聽起來像你需要指針-TO-char的數組。但是,我們仍然可以使用矢量來做到這一點。您需要一個矢量來存放std::string對象,該對象將擁有char*分配。然後你可以使用另一個矢量來保存實際的指針。事情是這樣的:

const char * binaryPath = "/bin/foo"; 

std::vector<std::string> argStrings; 
std::vector<char *> argPointers; 

std::string temp = "some text and stuff here"; 
istringstream s(temp); 

// argv[0] should always contain the binary's path. 
argPointers.push_back(binaryPath); 

while (s >> temp) { 
    argStrings.push_back(temp); 

    std::cout << "TOKEN " << argStrings.size() 
       << " =" << argStrings.back() << std::endl; 

    // We cast away the const as required by the exec() family of functions. 
    argPointers.push_back(const_cast<char *>(argStrings.back().c_str())); 
} 

// exec() functions expect a NULL pointer to terminate the arguments array. 
argPointers.push_back(nullptr); 

// Now we can do our exec safely. 
execv(binaryPath, &argPointers[0]); 

在這種情況下argStrings擁有實際的字符串分配,以及我們使用argPointers只需要保持我們將傳遞到execv()指針數組。 const_cast是安全的,因爲execv()不會修改字符串。 (該參數是char * const []用於與舊C代碼的兼容性;功能表現得好像參數爲const char * const []。)

+0

我看到一個矢量是現在走的路,謝謝。如果我更改爲矢量,我仍然必須更新指向矢量的args指針,以便我可以傳遞給exec系統調用。我是否希望參數仍然是const char * args [100],因爲我將不得不進行轉換。 (也許像char ** args的東西)? – 2014-10-07 12:35:12

+0

@ChaseCollisParker嗯,這是有點棘手,因爲你需要匹配現有的API。這將取決於您使用的特定的exec調用(有很多,具有不同的參數類型)。如果您希望我們可以進一步討論SO聊天。 – cdhowie 2014-10-07 15:16:32

+0

@ChaseCollisParker查看我更新的答案。 – cdhowie 2014-10-07 16:23:25

2

當你這樣做:

args[counter]=const_cast<char *> (temp.c_str()); 

你是不是複製的字符串,只存儲一個指向它的內容。所以當然他們都指向相同的temp字符串,這使得打印它們時的值完全相同。

如果您只是使用std::vector<std::string>代替args,這將會容易得多。

0

您需要單獨存儲每個串,存儲指針到一個臨時對象不是去的方式。例如。

#include <iostream> 
#include <vector> 
#include <sstream> 

using namespace std; 

void exec(char* args[]) 
{ 
    for (int i = 0; args[i] != NULL; ++i) 
     cout << args[i] << endl; 
} 

int main() 
{ 
    string temp = "some text and stuff here"; 
    stringstream s (temp);  

    vector<string> tokens; 
    while(s>> temp) 
    { 
    tokens.push_back(temp); 
    } 

    int counter = 0; 
    char *args[100]; 
    for (auto it = tokens.begin(); it != tokens.end(); ++it) 
    args[counter++] = const_cast<char*>(it->c_str()); 
    args[counter] = NULL; 

    exec(args); 

    return 0; 
} 

可以運行它here