c++ - 需要有关在 C++ 中有效操作内存的建议吗?

标签 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 ​​字符串,因为它比 std::string 更具挑战性。 所以问题是关于操纵字符串以最小化内存使用。我必须使用全局指针到字符(因为不必定义字符数组的大小)。 似乎 func CountSize() 虽然返回了所需的内容(字符串中数字的长度),但该字符串仍然浪费了一些内存并且大小变量不是我想要的。我也无法通过使用 sizeof() 和指向字符的指针来获取大小。 任何人都可以帮助我吗?

最佳答案

好的,所以你想使用 char* 字符串,大概是为了将来处理它们。这是令人钦佩的。但是你首先需要一个关于 c 字符串管理命令的速成类(class)......

你应该将每个 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 字符串时,它事先并不知道它有多长。如果你有一个用内容“Hi”初始化的 char[100],那么计算机如何知道在“i”之后停止,而不是“H”或“i”之后的字符 5? C 字符串无效,除非它们以空终止符结尾,写为“\0”。空终止符向计算机表明,“好的,我们到此为止了。”串完了这有点漂亮,但可能会出现问题,因为空终止符在字符数组中占据了一个位置。存储“Hi”需要一个 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_sstrcat_s 函数。 _s 代表“安全”。这些函数为它们正在修改的字符串的大小采用一个额外的参数,并确保不违反大小限制。所有字符串修饰符函数都确保附加了前面提到的空终止符,但在您的代码中,您没有为它们提供适当的空间来执行此操作。因此,非字符串安全函数将一个字符 PAST 写入您声明的内存到未知内存中——糟糕——非常糟糕。这些函数的字符串安全版本反而会使您的程序因错误而崩溃,提醒您出现问题并需要修复。您的函数的字符串安全实现如下所示:

    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,对整数列表使用 std::vector。所以嘿,我知道你想学习低级的东西来应对挑战,我自己也去过那里。但是一旦您知道如何去做,使我上面描述的基本上与字符串处理无关的所有内容的 C++ 功能就是一个天赐之物,您将永远、永远不想回头。

作为一个小旁注,我建议查看 Sieve of Eratosthenes用于质数检查。这是一个很好的实现练习,会给您的代码带来巨大的性能提升。

关于c++ - 需要有关在 C++ 中有效操作内存的建议吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5705292/

相关文章:

c++ - 重载的子类函数不能调用相似名称的父类

c++ - 当类具有 operator() 时的结构特化

C++ ceil 和负零

testing - 低内存开销 XML 解析

c - strcpy 损坏 char 数组(字符串值)

c++ - 如何找到继承类的分配地址

c++ - 如何获取Microsoft TTS语音的耗时?

c++ - 使用函数和数组从文件中读取和打印数据

android - android中的内存模拟

linux - 如何在多线程环境中实现垃圾回收?