将char数组的数组传递给函数

Passing an array of an array of char to a function

本文关键字:数组 函数 char      更新时间:2023-10-16

在我的程序中,我可能需要加载一个大文件,但并非总是如此。所以我定义了:

char** largefilecontents;
string fileName="large.txt";

当我需要加载文件时,程序调用这个函数:

bool isitok=LoadLargeFile(fileName,largefilecontents);

函数为:

bool LoadLargeFile(string &filename, char ** &lines)
{
    if (lines) delete [] lines;
    ifstream largeFile;
    #ifdef LINUX
    largeFile.open(filename.c_str());
    #endif
    #ifdef WINDOWS
    largeFile.open(filename.c_str(),ios::binary);
    #endif      
    if (!largeFile.is_open()) return false;
    lines=new char *[10000];
    if (!lines) return false;
    largeFile.clear();
    largeFile.seekg(ios::beg);
    for (int i=0; i>-1; i++)
    {
        string line="";
        getline(largeFile,line);
        if (largeFile.tellg()==-1) break; //when end of file is reached, tellg returns -1
        lines[i]=new char[line.length()];
        lines[i]=const_cast<char*>(line.c_str());
        cout << lines[i] << endl; //debug output
    }
    return true;
}

当我查看这个函数的调试输出时,"cout <<"行[我]& lt; & lt;结束;",很好。但是当我像这样在主程序中检查这个时,它就乱了:

for (i=0; i<10000; i++)
  cout << largefilecontents[i] << endl;

所以在函数LoadLargeFile()中,结果很好,但是没有LoadLargeFile(),结果就全乱了。我的猜测是,char ** &lines部分的功能是不正确的,但我不知道这应该是什么。

有人能帮帮我吗?提前感谢!

问题是您使用line.c_str的地方。您首先使用lines[i]=new char[line.length()];为您的新字符串分配内存,但随后您使用指向局部变量line中的c_str的指针覆盖line[i]中的指针。当line超出作用域时,这个c_str将被销毁。你需要做的是使用strcpy,而不是做简单的指针赋值。

试试下面的

lines[i]=new char[line.length()+1]; //Thanks @Claptrap for the catch
strncpy(lines[i], line.c_str(), line.length()+1); //Note, strcpy is not considered safe.
                                                  //Use strncpy

如果你想坚持严格的c++方式,那么我建议你使用@christian-hackl的建议来创建

std::vector<std::string> largefilecontents; //Since in your case 10000 is fixed
...
    bool LoadLargeFile(string &filename, std::vector<std::string> &lines)
    {
    ...
    //lines=new char *[10000];
    //if (!lines) return false;
    lines.resize(10000); //Since in your case 10000 is fixed
    ...
    for...
        if(i >= lines.size()) break; //safety net in case more than 10000 lines
        lines[i]="";
        getline(largeFile,lines[i]);
        if (largeFile.tellg()==-1) break; //when end of file is reached, tellg returns -1
        //lines[i]=new char[line.length()];
        //lines[i]=const_cast<char*>(line.c_str());
旁注:数组v/s向量

数组是原始的c++数据类型,而向量是类,是标准模板库的一部分。这意味着理论上矢量比数组慢。但实际上,速度的下降是可以忽略不计的。向量提供了两个巨大的好处,成本可以忽略不计

  • 内存管理。您不必删除内存,只要vector对象超出作用域,分配的内存就会被删除。在您的示例中,您似乎并不一定需要它,因为您的数组永远不会在
  • 这种微不足道的情况下被销毁。
  • 的灵活性。对于数组,您必须事先知道预期的大小。而向量则根据需要扩展。同样,你不一定需要它,因为正如你所说,矢量大小是固定的。

与向量

相关的代价有两个
  • 性能:vector可能比在数组上操作稍微慢一些,但由于大多数(如果不是全部)操作都被描述为内联函数,编译器将对其进行优化,并使其或多或少与数组一样高效。对于固定大小的向量尤其如此。
  • Memory: Vector是一个内部使用数组的容器,这意味着Vector对象会有轻微的内存开销。但是与你可能做的节省相比,不是分配一个巨大的数组,而是逐渐扩展向量。只有在创建大量小向量时,才会考虑vector的内存开销。在您的情况下,这个开销是微不足道的。

引用:

  1. 数组vs向量vs列表
  2. 现实世界中的c++ STL向量vs数组
  3. http://www.cplusplus.com/forum/beginner/6321/
  4. http://www.cprogramming.com/tutorial/stl/vector.html

检查什么是值传递,什么是引用传递。问题是,在函数终止后,这些更改不会被保留。

然而,我觉得问题开始于你如何声明你的2D字符数组。我认为你应该静态地做,如果你可以很容易地动态地做的话,像这样:

// We return the pointer
char **get(int N, int M) /* Allocate the array */
{
    /* Normally, you should check if allocation was ok. */
    char** ary = new char*[N];
    for(int i = 0; i < N; ++i)
        ary[i] = new char[M];
    return ary;
}
void delete2Darray(char** p, int N) {
    int i;
    for(i = 0 ; i < N ; i++)
        delete [] p[i];
    delete p;
}
void foo(char** p)
{
  printf("%sn", p[0]);
  strcpy(p[0], "sam");
  printf("%sn", p[0]);
}
int main()
{
    char** A = get(1, 5);
    strcpy(A[0], "dad");
    foo(A);
    printf("%sn", A[0]);
    delete2Darray(A, 1);
    return 0;
}

输出:

dad
sam
sam
下面是一个简单的C语言例子。(这是我的伪网站)。

   lines[i]=new char[line.length()];
   lines[i]=const_cast<char*>(line.c_str());

不要做你期望他们做的事

lines[i] = new char [line.length()]为字符串分配一些字节,但不包括字符串的结尾。

lines[i]=const_cast<char*>(line.c_str());将其设置为指向一个局部字符串变量,该变量持续一次迭代。你需要做的是在堆上分配一个缓冲区就像你为指针所做的那样然后复制到它

lines[i] = new char[line.length() + 1];
strcpy(lines[i], line.c_str() );

如果这是学校的练习,那么我就不多说了,但是如果它是一个真正的程序,那么你应该使用vector<string>来保持事情的简单,而不是摆弄原始数组。

另一件事是,最好在读入行之前将所有指针初始化为NULL,以便函数的调用者知道何时停止处理指针。

这样你就可以写

for (int j = 0; lines[j] != NULL; ++j)
  cout << lines[j] << endl;

这段代码有许多不足之处,因此很难对其进行推理。例如:

if (!lines) return false;

是无用的,因为new(在任何正常环境中)不会返回null,而是在内存耗尽时抛出异常。

下一个问题是,为什么是神奇的数字10000?如果文件包含超过10000行,会发生什么?看看你的循环:

for (int i=0; i>-1; i++)

它只是增加i,直到它达到int的最大值,此时增加会产生未定义的行为(例如看似随机崩溃)。

下一行:

getline(largeFile,line);

在操作之后不检查流是否有错误,而只是假设读取工作。这可不是什么好习惯。

最后,这一行:

lines[i]=const_cast<char*>(line.c_str());

丢弃char const *的cost也会产生未定义的行为。

你甚至尝试过只是在std::vector<std::string>中存储"大文件"的内容吗?很有可能你的假设对于一个普通的字符串向量来说太大了,因此你必须使用指针,这是完全错误的。