为什么通过***char传递给函数的以nullptr结尾的数组会丢失终止元素?

Why does a nullptr terminated array passed to function through ***char loose the termination element?

本文关键字:数组 结尾 元素 终止 nullptr char 为什么 函数      更新时间:2023-10-16

注意1:我不是在寻找不同的解决手头问题的方法。我很好奇这里到底发生了什么。

注2:我在c++上下文中这样做,但我假设这也适用于c,因此使用c标签。(除了空指针的表示)

这是关于c字符串和访问原始函数。我将使用argv和argc来说明数组应该以nullptr结束。我这样声明它们:

int argc = 1;
char **argv = (char**) malloc( (argc + 1) * sizeof(char*) );
argv[0] = (char*)"argument 0";
argv[1] = nullptr;

如果我像这样声明一个函数:func1(int &f_argc, char **f_argv)我可以访问函数范围内的所有元素,包括f_argv[f_argc],这是nullptr,但我不能修改原来的argv指向不同的地址,因为函数中的f_argv是原始指针的值传递副本。它在内存中有不同的地址。

如果我像这样声明函数:func2(int &f_argc, char ***f_argv),我可以通过函数中的*f_argv访问原来的argv,但是最后一个元素(应该是nullptr)被切断了。这意味着,如果我试图检查函数内的终止nullptr,我就会尝试访问数组范围之外的元素,从而在运行时导致核心转储。

Q1:为什么在func2中到达nullptrf_argv被切断,而在func1中没有?

Q2:有没有一种方法可以从函数内部获得对原始argv的写访问,而不删除终止符?

编辑:(添加代码以显示我的意思)

#include <iostream>
#include <cstring>
void func1(int &f_argc, char **f_argv) {
    using std::cout;
    using std::endl;
    cout << "    In function:" << endl;
    cout << "    argv passed as **f_argv" << endl;
    cout << "    f_argv = " << f_argv << " , &f_argv = " << &f_argv << endl;
    for (int pos = 0; pos < f_argc; pos++) {
        if (f_argv[pos] != nullptr) {
            cout << "    f_argv[" << pos << "] = "" << f_argv[pos] << """ << endl;
        } else {
            cout << "    f_argv is prematurely terminated" << endl;
        }
    }
    if (f_argv[f_argc] == nullptr) {
        cout << "    f_argv is correctly terminated" << endl;
    } else {
        cout << "    f_argv[" << f_argc << "] = "" << f_argv[f_argc] << """ << endl;
        cout << "    f_argv is not terminated" << endl;
    }
    // Intention is to copy argv here, add elements, terminate it with
    // nullptr and change original argv to point to copy. This wouldn't
    // work in this function, as &f_argv != &argv.
    return;
}
void func2(int &f_argc, char ***f_argv) {
    using std::cout;
    using std::endl;
    cout << "    In function:" << endl;
    cout << "    array passed as ***f_argv" << endl;
    cout << "    f_argc = " << f_argc
         << " , &f_argc = " << &f_argc << endl;
    cout << "    *f_argv = " << *f_argv
         << " , f_argv = " << f_argv << endl;
    for (int pos = 0; pos < f_argc; pos++) {
        cout << "    about to check: "
             << "if (*f_argv[" << pos << "] != nullptr)" << endl;
        if (*f_argv[pos] != nullptr) {
            cout << "    *f_argv[" << pos << "] = ""
                 << *f_argv[pos] << """ << endl;
        } else {
            cout << "    *f_argv is prematurely terminated" << endl;
        }
    }
    if (*f_argv[f_argc] == nullptr) {
        cout << "    *f_argv is correctly terminated" << endl;
    } else {
        cout << "    *f_argv[" << f_argc << "] = ""
             << *f_argv[f_argc] << """ << endl;
        cout << "    *f_argv is not terminated" << endl;
    }
    // Intention is to copy argv here, add elements, terminate it with
    // nullptr and change original argv to point to copy.
    return;
}

// --------------------------------------------
int main() {
    using std::cout;
    using std::endl;
    int argc=1;
    char **argv = (char**) malloc( (argc + 1) * sizeof(char*) );
    argv[0] = (char*)"argument 0";
    argv[1] = nullptr;
    cout << "Before function call" << endl;
    cout << "argv = " << argv << " , &argv = " << &argv << endl;
    for (int i = 0; i < argc; i++) {
        if (argv[i] != nullptr) {
            cout << "argv[" << i << "] = "" << argv[i] << """ << endl;
        } else {
            cout << "argv is prematurely terminated" << endl;
        }
    }
    if (argv[argc] == nullptr) {
        cout << "argv is correctly terminated" << endl;
    } else {
        cout << "argv[" << argc << "] = "" << argv[argc] << """ << endl;
        cout << "argv is not terminated" << endl;
    }
    // run one of these
    //func1(argc, argv);
    func2(argc, &argv);
    free(argv);
    return 0;
}

如果运行func2,运行程序会在这一行产生一个核心转储:

if (*f_argv[f_argc] == nullptr) {

下标操作符的优先级高于解引用操作符。*f_argv[f_argc]*(f_argv[f_argc])。你需要的是(*f_argv)[f_argc]

因为你正在使用c++,你应该考虑通过引用f_argv - void f(int &f_argc, char **& f_argv);