我刚参加考试时被问到以下问题:
为下面给出的代码编写GenStrLen,InsertChar和StrReverse的每个方法的函数体。您必须考虑以下因素;
- 如何在C ++中构造字符串
- 字符串不得溢出
- 插入字符会使其长度增加1
- 空字符串由StrLen = 0
表示
class Strings {
private:
char str[80];
int StrLen;
public:
// Constructor
Strings() {
StrLen=0;
};
// A function for returning the length of the string 'str'
int GetStrLen(void) {
};
// A function to inser a character 'ch' at the end of the string 'str'
void InsertChar(char ch) {
};
// A function to reverse the content of the string 'str'
void StrReverse(void) {
};
};
我给出的答案是这样的(见下文)。我的一个问题是,使用了许多额外的变量,这让我相信不是以最好的方式做到这一点,而另一件事是不能正常工作....
class Strings {
private:
char str[80];
int StrLen;
int index; // *** Had to add this ***
public:
Strings(){
StrLen=0;
}
int GetStrLen(void){
for (int i=0 ; str[i]!='\0' ; i++)
index++;
return index; // *** Here am getting a weird value, something like 1829584505306 ***
}
void InsertChar(char ch){
str[index] = ch; // *** Not sure if this is correct cuz I was not given int index ***
}
void StrRevrse(void){
GetStrLen();
char revStr[index+1];
for (int i=0 ; str[i]!='\0' ; i++){
for (int r=index ; r>0 ; r--)
revStr[r] = str[i];
}
}
};
如果有人能够大致解释一下解答问题的最佳方式以及原因,我将不胜感激。另外,为什么我的教授会关闭每个类函数,比如“};”,我认为它只用于结束类和构造函数。
非常感谢你的帮助。
答案 0 :(得分:17)
首先,琐碎的};
问题只是风格问题。当我将函数体放在类声明中时,我也这样做。在这种情况下,;
只是一个空语句,并不会改变程序的含义。它可以被排除在函数的末尾(但是不是类的结尾)。
以下是您所写内容的一些主要问题:
str
的内容。它不能保证以\0
字节开头。index
,只能在GetStrLen
内进行设置。程序启动时它可能具有值-19281281。如果有人在致电InsertChar
之前致电GetStrLen
该怎么办?index
中的InsertChar
。如果有人连续两次拨打InsertChar
该怎么办?StrReverse
中,您创建一个名为revStr
的反向字符串,但之后您永远不会对其执行任何操作。 str
中的字符串保持相同的后缀。令我感到困惑的部分是为什么你创建了一个名为index
的新变量,大概是为了跟踪字符串的最后一个字符的索引,当时已经存在一个名为{{1}的变量为了这个目的,你完全忽略了。最后一个字符的索引是字符串的长度,所以你应该保持字符串的长度是最新的,并使用它,例如。
StrLen
然而,你的字符串反转算法完全错误。仔细考虑代码所说的(假设int GetStrLen(void){
return StrLen;
}
void InsertChar(char ch){
if (StrLen < 80) {
str[StrLen] = ch;
StrLen = StrLen + 1; // Update the length of the string
} else {
// Do not allow the string to overflow. Normally, you would throw an exception here
// but if you don't know what that is, you instructor was probably just expecting
// you to return without trying to insert the character.
throw std::overflow_error();
}
}
在其他地方正确初始化和更新)。它说“对于index
中的每个角色,用这个角色向后覆盖整个str
”。如果revStr
开始时为str
,则"Hello World"
最终会显示为revStr
,因为"ddddddddddd"
是d
中的最后一个字符。
你应该做的是这样的事情:
str
注意它是如何工作的。说void StrReverse() {
char revStr[80];
for (int i = 0; i < StrLen; ++i) {
revStr[(StrLen - 1) - i] = str[i];
}
}
。然后我们将StrLen = 10
的第0位复制到str
的第9位,然后将revStr
的第1位复制到str
的位置9
等等,直到我们将revStr
的{{1}}位置复制到StrLen - 1
的位置0。
但是你在str
中有一个反向字符串,你仍然错过了将它放回revStr
的部分,所以完整的方法看起来像
revStr
并且有更聪明的方法可以做到这一点,你不必拥有一个临时字符串str
,但上述功能完全正常,并且是问题的正确答案。
顺便说一句,你真的不需要担心这段代码中的NULL字节(void StrReverse() {
char revStr[80];
for (int i = 0; i < StrLen; ++i) {
revStr[(StrLen - 1) - i] = str[i];
}
for (int i = 0; i < StrLen; ++i) {
str[i] = revStr[i];
}
}
s)。您(或者至少应该)使用revStr
变量跟踪字符串的长度这一事实使得最终的哨兵变得不必要,因为使用\0
您已经知道StrLen
以外的内容{ {1}}应该被忽略。
答案 1 :(得分:4)
int GetStrLen(void){
for (int i=0 ; str[i]!='\0' ; i++)
index++;
return index; // *** Here am getting a weird value, something like 1829584505306 ***
}
你得到一个奇怪的值,因为你从未初始化索引,你刚刚开始递增它。
答案 2 :(得分:4)
您的GetStrLen()
函数无效,因为str
数组未初始化。它可能不包含任何零元素。
您不需要index
成员。只需使用StrLen
跟踪当前字符串长度。
答案 3 :(得分:4)
通过这个考试题目可以学到很多有趣的课程。首先,审查员本身并不是一个流利的C ++程序员!您可能希望查看代码的样式,包括变量和方法名称是否有意义,以及您对(void)
,const
等的使用给出的其他一些注释。 ...方法名称真的需要“Str”吗?毕竟,我们正在使用“Strings”课程!
对于“如何在C ++中构造字符串”,以及(如在C中)这些是以null结尾的,并且不像它们那样存储长度,就像Pascal(和这个类)那样。 [@Gustavo,strlen()
在这里不起作用,因为字符串不是以空字符结尾的字符串。]在“真实世界”中,我们使用std::string
类。
“字符串不能溢出”,但是类的用户如何知道他们是否尝试溢出字符串。 @ Tyler关于抛出std::overflow_exception
(也许带有消息)的建议会起作用,但是如果你正在编写自己的字符串类(纯粹作为练习,你在现实生活中不太可能需要这样做)那么你应该提供自己的异常类。
“插入字符会使其长度增加1”,这意味着GetStrLen()
不会计算字符串的长度,而是纯粹返回在构造时初始化并通过插入更新的StrLen的值。
您可能还想考虑如何测试您的课程。为了便于说明,我添加了一个Print()
方法,以便您可以查看该类的内容,但您应该查看类似Cpp Unit Lite的内容。
对于它的价值,我包括我自己的实现。与目前为止的其他实现不同,我选择在反向函数及其交换帮助器中使用原始指针。我假设使用诸如std::swap
和std::reverse
之类的内容超出了本次考试的范围,但是您需要熟悉标准库,以便您可以继续编程而无需重新发明轮子
#include <iostream>
void swap_chars(char* left, char* right) {
char temp = *left;
*left = *right;
*right = temp;
}
class Strings {
private:
char m_buffer[80];
int m_length;
public:
// Constructor
Strings()
:m_length(0)
{
}
// A function for returning the length of the string 'm_buffer'
int GetLength() const {
return m_length;
}
// A function to inser a character 'ch' at the end of the string 'm_buffer'
void InsertChar(char ch) {
if (m_length < sizeof m_buffer) {
m_buffer[m_length++] = ch;
}
}
// A function to reverse the content of the string 'm_buffer'
void Reverse() {
char* left = &m_buffer[0];
char* right = &m_buffer[m_length - 1];
for (; left < right; ++left, --right) {
swap_chars(left, right);
}
}
void Print() const {
for (int index = 0; index < m_length; ++index) {
std::cout << m_buffer[index];
}
std::cout << std::endl;
}
};
int main(int, char**) {
Strings test_string;
char test[] = "This is a test string!This is a test string!This is a test string!This is a test string!\000";
for (char* c = test; *c; ++c) {
test_string.InsertChar(*c);
}
test_string.Print();
test_string.Reverse();
test_string.Print();
// The output of this program should look like this...
// This is a test string!This is a test string!This is a test string!This is a test
// tset a si sihT!gnirts tset a si sihT!gnirts tset a si sihT!gnirts tset a si sihT
return 0;
}
祝你好好继续学习!
答案 4 :(得分:2)
初始化char数组时,应将其第一个元素设置为0,将index
设置为相同。因此,你在GetStrLen
得到一个奇怪的长度,因为当你找到你正在寻找的0时它取决于神。
[更新] 在C / C ++中,如果没有显式初始化变量,通常会让它们充满随机垃圾(分配给它们的原始内存的内容)。此规则有一些例外,但最佳做法是始终显式初始化变量。 [/ Update]
在InsertChar
中,你应该(在检查溢出之后)使用StrLen
来索引数组(因为注释指定“在字符串'str'的末尾加上一个字符'ch'” ),然后设置新的终止0字符并增加StrLen
。
答案 5 :(得分:2)
void InsertChar(char ch){
str[index] = ch; // *** Not sure if this is correct cuz I was not given int index ***
}
这应该更像是
str[strlen-1]=ch; //overwrite the null with ch
str[strlen]='\0'; //re-add the null
strlen++;
答案 6 :(得分:2)
你的老师在这个问题上给了你非常好的提示,再读一遍并尝试回答你自己。这是我未经测试的解决方案:
class Strings {
private:
char str[80];
int StrLen;
public:
// Constructor
Strings() {
StrLen=0;
str[0]=0;
};
// A function for returning the length of the string 'str'
int GetStrLen(void) {
return StrLen;
};
// A function to inser a character 'ch' at the end of the string 'str'
void InsertChar(char ch) {
if(StrLen < 80)
str[StrLen++]=ch;
};
// A function to reverse the content of the string 'str'
void StrReverse(void) {
for(int i=0; i<StrLen / 2; ++i) {
char aux = str[i];
str[i] = str[StrLen - i - 1];
str[StrLen - i - 1] = aux;
}
};
};
答案 7 :(得分:2)
您不需要index
作为会员数据。你可以在GetStrLen()
中将它变成一个局部变量:只需在那里声明它而不是在类体中声明它。返回index
时获得奇怪值的原因是因为您从未初始化它。要解决此问题,请在index
中将GetStrLen()
初始化为零。
但是有一种更好的方法:当你通过InsertChar()
插入一个字符时,增加StrLen
的值,这样GetStrLen()
只需要返回该值。这将使GetStrLen()
更快:它将以恒定时间运行(无论字符串的长度如何,都是相同的性能)。
在InsertChar()
中,您可以使用StrLen
作为索引,而不是index
,我们已经确定这是多余的。但请记住,您必须确保字符串以'\0'
值终止。还要记住通过增加它来维持StrLen
以使GetStrLen()
的生活更轻松。此外,您必须在InsertChar()
中执行额外步骤以避免缓冲区溢出。当字符串的长度为79个字符时,用户在字符串中插入字符时会发生这种情况。 (是的,79:你必须在终止null上花费一个字符。)
在发生这种情况时,我没有看到关于如何表现的指示,所以它必须取决于你良好的判断力。如果用户尝试添加第80个字符,您可能会忽略该请求并返回,或者您可能会设置错误标记 - 这取决于您。
在StrReverse()
函数中,您有一些错误。首先,您调用GetStrLen()
但忽略其返回值。那为什么叫呢?其次,您正在创建一个临时字符串并对其进行处理,而不是在该类的字符串成员上。所以你的函数不会改变字符串成员,实际上它应该反转它。最后,你可以通过迭代它的一半来更快地反转字符串。
处理成员数据字符串。要反转字符串,您可以将字符串的第一个元素(字符)与其最后一个(不是终止空值,前面的字符!)交换,第二个元素使用倒数第二个,依此类推。当你到达弦乐的中间时,你已经完成了。不要忘记字符串必须以'\0'
字符终止。
当你在解决考试时,这也是一个很好的机会,可以教导你的教师一两个关于C ++的思考:我们不会说f(void)
,因为那属于C89的旧时代。在C ++中我们说f()
。我们还努力在C ++中尽可能使用类初始化列表。还要提醒你的教师const正确性有多重要:当一个函数不应该改变时,对象应该被标记为这样。 int GetStrLen(void)
应为int GetStrLen() const
。
答案 8 :(得分:1)
你不需要弄清楚长度。你已经知道它是strLen。此外,原始问题中没有任何内容表明缓冲区应包含空终止字符串。
int GetStrLen(void){
return strLen;
}
只是在这里使用断言,但另一种选择是抛出异常。
void InsertChar(char ch){
assert(strLen < 80);
str[strLen++] = ch;
}
反转字符串只是交换str缓冲区中的元素。
void StrRevrse(void){
int n = strLen >> 1;
for (int i = 0; i < n; i++) {
char c = str[i];
str[i] = str[strLen - i];
str[strLen - i] = c;
}
}
答案 9 :(得分:1)
我会使用StrLen
来跟踪字符串的长度。由于长度也表示字符串的结尾,我们可以使用它来插入:
int GetStrLen(void) {
return StrLen;
}
int InsertChar(char ch)
{
if (strLen < sizeof(str))
{
str[StrLen] = ch;
++strLen;
}
}
void StrReverse(void) {
for (int n = 0; n < StrLen / 2; ++n)
{
char tmp = str[n];
str[n] = str[StrLen - n - 1];
str[StrLen - n - 1] = tmp;
}
}
答案 10 :(得分:1)
首先为什么使用String.h作为字符串长度? strlen(char [] array)将Lenght或任何char数组返回给int。
你的函数返回一个werid值,因为你从不初始化索引,并且数组的值为零,首先启动然后执行你的方法。