我编写了一个小程序来查找既是质数又是特定数 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)......
你应该将每个 new
与 delete
配对
您的目标是尽量减少内存使用,对吗?嗯,每次你用 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
。这是为了确保您的字符串有空终止符的空间。
你应该使用字符串安全函数
sprintf
、strcpy
和 strcat
(以及其他)已被弃用,取而代之的是新的 sprintf_s
、strcpy_s
和 strcat_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/