需要建议在C ++中有效地操作内存吗?

时间:2011-04-18 15:33:42

标签: c++ memory-management

我写了一个小程序来查找主要数字和特定数字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字符串中并检查下一个数字是否已经是字符串中的数字,则结果可能会重复。我选择C-string,因为它比std :: string更具挑战性。 所以问题是关于操纵字符串以最小化内存使用。我必须使用全局指针到char(因为不必定义char数组的大小)。 似乎func CountSize()虽然返回了所需要的东西(字符串中的数字长度),但字符串仍然浪费了一些内存和放大器。 size变量不是我的意思。此外,我无法通过使用带有指向char的指针的sizeof()来获得大小。 任何人都可以帮助我吗?

3 个答案:

答案 0 :(得分:2)

好的,所以你想使用char *字符串,大概是为了将来处理它们。这是令人钦佩的。但是你首先需要在c-string管理的诫命中使用速成课程......

您应该将每个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-string时,它不知道它有多长时间。如果你有一个char [100]用内容“Hi”初始化,那么计算机如何知道在'i'之后停止,而不是'H',或'i'之后的字符5? C字符串无效,除非它们以空终止符结尾,写为'\ 0'。空终止符向计算机指示“好的,我们在这里完成了”。字符串结束了。这有点漂亮,但是因为null-terminator在字符数组中占据了一个点,所以会出现问题。存储“嗨”需要char [3]; - char[0]为'H',char[1]为'i',char[2]为'\ 0'。所以你的新字符串分配代码应如下所示:

    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。这是为了确保你的字符串有空间终止。

您应使用字符串安全功能

sprintfstrcpystrcat(及其他)已弃用,以支持新的sprintf_sstrcpy_s和{{1} } 功能。 _s代表“安全”。这些函数为它们正在修改的字符串的大小采用额外的参数,并确保不会破坏大小限制。所有的字符串修饰符函数都确保了前面提到的空终止符,但是在你的代码中,你并没有给它们提供适当的空间。所以非字符串安全函数正在写一个字符PAST你声明的内存到未知的内存 - 坏 - 非常糟糕。这些函数的字符串安全版本会使您的程序崩溃并发生错误,提醒您出现错误并需要修复。函数的字符串安全实现如下所示:

strcat_s

现在你很安全,如果出现问题,你知道

您应该以C ++方式编写C ++代码

说实话,你上面写的程序实际上不是C ++,它是C.当然,它在C ++环境中运行良好,但该方法完全基于C语言。 C ++程序使用 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 表示字符串,std::string表示整数列表。所以嘿,我明白你想要学习低级别的东西来迎接挑战,我自己一直在那里。但是一旦你知道如何做到这一点,C ++功能基本上使我上面描述的所有内容与字符串处理无关,就是天赐,你永远不会想要回去。

作为一个小小的注释,我建议您查看Sieve of Eratosthenes进行素数检查。这是一个很好的练习,可以为你的代码带来巨大的性能提升。

答案 1 :(得分:1)

对于您要执行的操作,字符串实际上不是一个合适的数据结构。你似乎关心内存消耗,但为什么呢?实际上你的程序内存不足吗?使用字符串执行此任务会带来许多不必要的工作:分配内存,复制字符串以附加新数字,在字符串中搜索现有数字,将整数转换为字符串,计算整数中的位数多于必要的次数, 等等。使用C字符串也可以很容易地引入错误:非空终止的字符串,缓冲区溢出等。例如,当您将整数转换为字符串时,不会为null终止符分配一个字节,因此{{ 1}}溢出你的缓冲区。

更合适的数据结构是一组整数。集合可以只存储一次值。您可以使用sprintf方法查看集合中是否已存在某个项目。使用set可能会使用更多的内存,但是你的程序会快得多,因为你将用O(1)和O(log n)操作替换很多O(N)操作。

您不能使用find来获取已分配数组的大小。这是因为sizeof返回类型的大小,因此当您在C字符串上使用sizeof时,您将获得指针的大小,而不是数组的大小。您必须自己跟踪阵列的大小。

你提到使用C字符串而不是std :: string,因为它更具挑战性。我赞扬你尝试挑战,因为这是一个很好的方式来扩展你的极限和学习新的东西。如果我可以提出一个建议:做一些可能先行的简单化的事情。编写测试以确保它能够执行您认为正在执行的操作。通过手头的工作程序和验证测试,您可以开始优化内存消耗,性能或具有挑战性的数据结构,以获得乐趣。该测试允许您在优化时验证优化是否未引入错误。

答案 2 :(得分:0)

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