我写了一个小程序来查找主要数字和特定数字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()来获得大小。 任何人都可以帮助我吗?
答案 0 :(得分:2)
好的,所以你想使用char *字符串,大概是为了将来处理它们。这是令人钦佩的。但是你首先需要在c-string管理的诫命中使用速成课程......
您应该将每个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-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
。这是为了确保你的字符串有空间终止。
您应使用字符串安全功能
sprintf
,strcpy
和strcat
(及其他)已弃用,以支持新的sprintf_s
,strcpy_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)
将整数存储为字符串,并询问有效的内存管理?