2011-12-31 65 views
3

对于类,我使用C++创建了一个shell。从C++开始工作已经有一段时间了,所以我遇到了一些麻烦。该项目的一个要求是我必须使用read()系统调用。无法将内存分配给C++中的唯一地址

我需要保持一个命令历史记录(类似于bash的,如果你按向上箭头),其存储最近20个命令。我觉得做到这一点的最好方法是使用指向先前语句的指针数组。我遇到了一个问题,无论我做什么,包含用户输入的字符串总是存储在内存中的相同位置。为了澄清,这意味着,如果用户输入5条语句,然后查看他/她的历史记录,他们将看到最近的语句5次。我的代码看起来有点像这样(我不得不削减一些东西出来,因为在中间有很多的错误处理):

char *history[20]; 
int historyCounter = 0; 

while(true){ 
    char currLine[65]; 
    int charsRead = read(0,currLine,65); 

    char tmp[charsRead]; 
    strcpy(tmp,currLine); //This is my attempt to ensure the char[] is stored int a 
         //unique location every time, but this attempt failed. 

    history[historycounter] = tmp; 
    historycounter++; 
} 

只是要注意,在我的源代码,我做处理时的情况historycounter> 19.只是不在这个片段。

如果需要任何更多的澄清,我很乐意提供。这是我第一次在堆栈溢出发帖,所以如果我犯了新手的任何错误,我都会提前道歉。如果解决方案很痛苦,我也很抱歉。我一直在看这一段时间,而我完全有可能没有直截了当。

+0

不要使用'这里strcpy' - 'read'不空终止其输出等'charsRead'不包括终止空字符的空间,也没有任何终止空字符存在。您应该使用memcpy(并明确记录长度)或添加终止字符(并确保在复制时为其分配空间)。 – bdonlan 2011-12-31 21:07:20

回答

2

在这种情况下,tmp是最有可能是在相同的位置,上堆叠。它在每次迭代时分配,并在迭代结束时释放。

线

history[historycounter] = tmp; 

会导致当你使用它,因为你将使用一个局部变量的地址其范围之外的未定义行为。

如果你想确保唯一地址(和解决问题UB) - 使用new分配内存,并使用delete直到大功告成。确保完成所有分配的指针和delete跟踪。

+0

哇...这解决了我的问题。我现在有点像一个白痴,但我绝对不怕回到另一个问题。非常感谢您的快速和有益的回应!对此,我真的非常感激! – Tom 2011-12-31 20:50:36

+0

@Tom任何时候,对于第一个计时器,您的问题都是非常明确的表述和主题,你会惊讶它是多么令人耳目一新。不要忘记接受答案。 – littleadv 2011-12-31 20:51:28

+3

更好的是,不要使用'new'。使用'std :: string',或者'std :: vector '。更好的是,内存池不会导致碎片。 – 2011-12-31 20:57:56

1

你在用C++或C做你的功课吗?

littleadv告诉你为什么你的程序表现不正确。您从不分配字符串数组,而只是有一个字符串指针数组,但它们最终都指向由tmp标识的相同堆栈位置。

然而,而不是做你自己的指针操作,不像C,C++并为您提供一些较高级别的选项。例如,你可以声明你的数组作为

std::vector<std::string>  listOfCommands; 

这会照顾所有的内存管理的为您服务。也可以考虑创建一个类来封装最后N个命令的存储和检索,而不是直接使用全局变量。提供最少的访问该列表的公共函数(例如,数组和std :: vectors允许您修改任何元素,但在您的应用程序中,您只需要写入结尾,因此应该只有一个写入函数写到最后)。然后你的listOfCommand将成为一个私人数据,你的程序的其余部分甚至不需要知道这些命令是如何存储在内存中的。

这在C什么编程++(以及任何其他OO)的语言是什么。除了全局变量之外,还可以创建隐藏大部分复杂性的自包含构建块(即类),然后创建依赖更简单块的更大块。继续这样做,直到你的整个应用程序完成。

+0

为什么这个人是唯一提倡妥善解决方案的人? WinRAR的。 – Puppy 2011-12-31 22:55:11

+0

@DeadMG因为有时你应该做你所说的话,即使一些聪明的驴子认为它的“坏”。这个答案不回答这个问题。它给出了另一种做事情的方式(你认为是“适当的”,尽管我不知道使用动态分配的方式变得不合适),但这不是OP要求的。 – littleadv 2012-01-01 03:49:29

-1

只需使用一个2-d阵列,跳过关于设法确保唯一的地址和TMP缓冲所有这些东西。

const size_t MAX_LINE_LENGTH = 65; 
typedef char HISTORY_LINE[MAX_LINE_LENGTH+1]; //+1 for null terminator 
const size_t MAX_HISTORY_LENGTH = 20; 
HISTORY_LINE history[MAX_HISTORY_LENGTH]; 
int historyCounter = 0; 

while(true){ 
    char* currLine = history[historycounter]; 

    currLine[0] = '\0'; 
    int charsRead = read(0,currLine,MAX_LINE_LENGTH); 
    if(charsRead > 0) 
     currLine[charsRead] = '\0'; // ensure string is null terminated 

    historycounter++; 
} 

我跳过所有相同的错误检查,但是添加了一行以确保字符串始终为空。

+1

肯定会降低“MAGIC_BUFFER”代码。 – Puppy 2011-12-31 20:56:51

+0

如果每个字符串的数量和大小有固定的限制,这比使用动态内存管理好得多,因为它不会导致内存碎片(对于诸如shell之类的长时间运行的进程很重要)。 – 2011-12-31 20:59:07

+0

@DeadMG:为什么?这些常数是有名的,使用常量比通过原始代码分散的幻数要好得多。如果您反对线路长度限制,请随时选择OP。 – 2011-12-31 21:00:31