需要在c++中有效操作内存的建议

Need suggestion for manipulating memory effectively in C++?

本文关键字:内存 操作 有效 c++      更新时间:2023-10-16

我写了一个小程序来查找一个数字n的质数和因数。我从一个文件中得到数字n,并把它打印到另一个文件中。

#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <string>
#include <cstring>
#include <iostream>
#include <fstream>
using namespace std;
bool PrimeFactor(int );
int countSize(int);

char * p_str = "";//store result of prime factor
int size = 0;//length of p_str
int main(int argc, char** argv) {
std::ifstream fin;
fin.open("input.txt");
std::ofstream fout;
fout.open("output.txt", std::ios::app);
int N;//number to factor
while (fin >> N){   
    //Find prime factor
    PrimeFactor(N);
    fout << p_str;// print string storing result
    fout << endl;
}
fin.close();
fout.close();
return 0;
}
bool PrimeFactor( int n ){
int count = 0;// count divisors
for (int i = 2 ; i < n; i++){
    if ( n % i == 0 ){
        // convert integer to string
        char * tmpstr = new char[ countSize(i) ]  ;// allocate according to size of integer (not waste memory)
        sprintf( tmpstr, "%d ", i); // NOTE : if not have the blank after %d -> no space
        // condition : prime and not duplicate existing number in global string
        if ( PrimeFactor(i) && !strstr( p_str, tmpstr ) ){
            char * new_p = new char [ size + countSize(i) ];
            strcpy( new_p, p_str );// copy the global string to new place
            strcat( new_p, tmpstr );// append new integer value
            p_str = new_p ; // let glocal string be renewed with appending new result
            size = size + countSize(i);
            cout << size << endl;
        }
        count ++;
    }
}
if (count > 0)//not prime
    return false;
return true;
}
//counting the number of digit of an integer
int countSize(int n){
    int count = 0;
    if (n < 10)
        return 1;
    while ( n >= 10 ){
        count++;
        n = n/10;
    }
    return count + 1;
}

使用这种方法,如果我不将创建的数字存储在C-string中并检查下一个数字是否已经是字符串中的数字,则结果可能会重复。我选择C-string,因为它比std::string更具挑战性。所以问题是关于操作字符串以最小化内存使用。我必须使用一个指向char的全局指针(因为不必定义char数组的大小)。似乎funccountsize()虽然返回需要的东西(字符串中数字的长度),字符串仍然浪费一些内存&大小变量不是我想要的。此外,我不能通过使用sizeof()和一个指向char的指针来获得大小。有人能帮我吗?

好,所以你想使用char*字符串,大概是为了将来得到它们的句柄。这是令人钦佩的。但是你首先需要一个c字串管理的速成班…

您应该将每个newdelete配对

你的目标是尽量减少内存使用,对吗?好吧,每次你用new创建一个字符串,但然后不删除它,你正在泄漏内存。这通常是不好的做法,而且肯定会浪费内存。在您的示例中,您使用new []进行分配以生成一个数组,并且new []调用必须与delete []配对。在PrimeFactor函数中:
strcpy( new_p, p_str );// copy the global string to new place
strcat( new_p, tmpstr );// append new integer value
// delete original contents of p_str
delete [] p_str;
p_str = new_p ; // let glocal string be renewed with appending new result

您还需要在程序的最后使用delete [],以便在p_str退出之前清理它的内存。

您应该始终为空终止符

留出空间

当计算机读取c字串时,它事先不知道它有多长。如果你有一个字符[100]初始化为内容"Hi",计算机如何知道在"i"之后停止,而不是"H",或者"i"之后的字符5 ?c字符串除非以空结束符结束,否则无效,写为''。空终止符向计算机表示,"好,我们完成了。"绳子断了。这有点漂亮,但可能会出现问题,因为空结束符占用了字符数组中的一个位置。存储"Hi"需要char [3];——char[0]是'H', char[1]是'i', char[2]是''。所以你的新字符串分配代码应该是这样的:

    char * tmpstr = new char[ countSize(i) + 1 ]  ;// allocate according to size of integer (not waste memory)
    ...
        char * new_p = new char [ size + countSize(i) + 1 ];

注意+ 1 s。这是为了确保您的字符串有空结束符的空间。

应该使用字符串安全的函数

sprintfstrcpystrcat(以及其他)已经被弃用,取而代之的是新的sprintf_sstrcpy_sstrcat_s函数。_s代表"安全"。这些函数接受一个额外的参数来表示它们正在修改的字符串的大小,并确保大小限制没有被打破。所有字符串修饰符函数都确保添加了前面提到的空结束符,但是在代码中,您没有给它们适当的空间来执行此操作。因此,非字符串安全函数将一个字符写入超出声明内存的未知内存——很糟糕——非常糟糕。这些函数的字符串安全版本将会导致程序崩溃,并出现一个错误,提醒你有什么地方出错了,需要修复。函数的字符串安全实现应该是这样的:

    int tmpstrSize = countSize( i ); // calculate tmpstrSize once
    char * tmpstr = new char[ tmpstrSize + 1 ]  ;// allocate according to size of integer (not waste memory)
    sprintf_s( tmpstr, tmpstrSize + 1, "%d ", i); // NOTE : if not have the blank after %d -> no space
    ...
        int new_pSize = size + tmpstrSize; // calculate new_pSize once
        char * new_p = new char [ new_pSize + 1 ];
        strcpy_s( new_p, new_pSize, p_str );// copy the global string to new place
        strcat_s( new_p, new_pSize, tmpstr );// append new integer value

现在你很安全,如果出了什么问题,你会知道

你应该用c++的方式写c++代码

说实话,你上面写的程序并不是真正的c++,而是C。当然,它在c++环境下运行得很好,但这个方法完全是基于C的。c++程序对字符串使用std::string s,对整数列表使用std::vector s。嘿,我理解你想要为了挑战而学习低级的东西,我自己也经历过。但是一旦你知道了如何做到这一点,c++的功能基本上使我上面描述的所有内容与字符串处理无关,这是一个天赐的,你将永远不会想要回去。

作为一个小旁注,我建议检查埃拉托色尼筛子来检查质数。这是一个很好的实践,将给你的代码一个巨大的性能提升

对于您要做的事情来说,字符串实际上并不是一种合适的数据结构。你似乎很关心内存消耗,但为什么呢?您的程序是否真的耗尽了内存?在此任务中使用字符串会带来许多不必要的工作:分配内存、复制字符串以附加新数字、在字符串中搜索现有数字、将整数转换为字符串、在不必要的情况下计算整数中的位数,等等。使用C字符串也很容易引入bug:字符串不是null终止的,缓冲区溢出,等等。例如,当您将整数转换为字符串时,您不会为空终止符分配字节,因此sprintf会溢出您的缓冲区。

更合适的数据结构应该是一组整数。set可以存储一个值一次且只能存储一次。您可以使用find方法来查看某个项是否已经存在于集合中。使用集合可能会占用更多的内存,但是你的程序会非常非常快,因为你会用O(1)和O(log N)个操作来代替很多O(N)个操作。

你不能使用sizeof来获取分配数组的大小。这是因为sizeof返回类型的大小,所以当你在C字符串上使用sizeof时,你得到的是指针的大小,而不是数组的大小。你必须自己跟踪数组的大小。

你提到使用C字符串而不是std::string,因为它更具挑战性。我推荐你尝试挑战性的事情,因为这是一个很好的方式来扩展你的极限和学习新事物。请允许我提个建议:先做最简单可行的事情。编写测试以确保它执行您认为它正在执行的操作。有了可用的程序和验证测试,您就可以开始优化内存消耗、性能或挑战数据结构了。该测试允许您验证您的优化在优化时没有引入错误。

将整数存储为字符串,您正在询问有效的内存管理?