【问题标题】:Shouldnt output be same in both case?两种情况下的输出不应该相同吗?
【发布时间】:2018-08-16 07:23:55
【问题描述】:

注意“len = strlen(s);”行中两个代码的区别在代码 1 中:它写在 "strcpy(str,s)" 之前,而在代码 2 中它写在之后。它有什么不同?我在 Dev C++ 中运行我的代码,我得到了不同的输出。两种情况下的输出不应该相同吗?

代码 1:

#include <iostream>
#include <string.h>
using namespace std;

class String{
    private:
        char str[];
        int len;
    public:
        String()
        {
            cout << "Default Constructor" << endl;
            len = 0;
            strcpy(str,"");
        }
        String(char s[])
        {
            cout << "parameterised constructor" << endl;
            len = strlen(s);
            strcpy(str,s);
        }
        void print()
        {
            cout << str << " len = " << this->len << " strlen = " << strlen(str) << endl;
        }
};

int main()
{
    String str2("Hello World");
    str2.print();
    return 0;
}

输出 1:

parameterised constructor
Hello World len = 1819043144 strlen = 11

代码 2:

#include <iostream>
#include <string.h>
using namespace std;

class String{
    private:
        char str[];
        int len;
    public:
        String()
        {
            cout << "Default Constructor" << endl;
            len = 0;
            strcpy(str,"");
        }
        String(char s[])
        {
            cout << "parameterised constructor" << endl;
            strcpy(str,s);
            len = strlen(s);
        }
        void print()
        {
            cout << str << " len = " << this->len << " strlen = " << strlen(str) << endl;
        }
};

int main()
{
    String str2("Hello World");
    str2.print();
    return 0;
}

输出 2:

parameterised constructor
 len = 11 strlen = 1

【问题讨论】:

  • 将 sn-ps 简化为仅显示感兴趣的部分(不同的 strlenstrcpy 调用和输出)的最小版本可能会改善您的问题。
  • 很确定这是未定义的行为,你 strcpy 没有分配所需的内存或任何东西。

标签: c++ dev-c++


【解决方案1】:

问题出在这里:

char str[];

这是一个零长度数组(顺便说一句,在标准 C++ 中你不能有零长度数组)。

此数组最多可以包含 0 个字符,因此在您的情况下,零长度数组是毫无意义的。

您基本上是在覆盖不属于您的内存,并且您的两个程序的行为都是未定义的。由于行为是未定义的,因此解释为什么两个版本的输出不同也是没有意义的。

试试这个,它可以让你的字符串最大为 100:

char str[100];

要在 C++ 中处理字符串,您应该使用 std::string 而不是原始 C 字符串。

【讨论】:

    【解决方案2】:

    代码错误,所以输出真的是未定义的。您定义了char str[];,但在任何时候都没有分配内存缓冲区来保存您正在复制的字符串的内容。在某些情况下,此代码可能会崩溃。

    更好地定义:char *str;

    在构造函数中:

    str = new char[len+1];

    也别忘了在析构函数中delete [] str

    【讨论】:

    • 不要忘记 nul 终止符str = new char[len + 1];
    【解决方案3】:

    糟糕,在 C++ 中应该避免不完整的数组!

    class String{
        private:
            char str[];
            int len;
    

    至少应该提出一些警告,因为您声明了一个不完整的数组(大小为 0!),它不是普通旧数据(C 类)结构的最后一个成员。即使不是语法错误,您也不能在该数组中存储任何内容。如果你尝试,你会覆盖下一个成员的内容,这里是len。顺便说一句,这就是您得到不一致结果的原因。

    可以做什么?

    存储字符串的 C++ 惯用方式是...std::string,但它为您提供了对本机类的简单包装。

    class String {
    private:
        std::string str;
    

    如果可以的话,第二种简单的方法是声明一个足够大的数组。这就是所谓的惰性C方式。这里没有分配问题:

    #define SIZE 1024
    ...
    class String {
    private:
        char str[SIZE];
        int len;
    

    如果你足够勇敢,你可以在你的类中管理显式分配:你在构造函数中分配内存,在析构函数中释放它。但这意味着您立即需要非默认的复制/移动构造函数和赋值运算符,因为您现在需要明确哪个对象拥有分配的数据并且不能再依赖默认实现

    class String {
    private:
        char *str;
        int len;
    
    public:
        String()
        {
            cout << "Default Constructor" << endl;
            len = 0;
            str = nullptr;
        }
        String(char s[])
        {
            cout << "parameterised constructor" << endl;
            len = strlen(s);
            str = new char[len + 1];
            strcpy(str, s);
        }
        ~String() {                      // dtor
            if (str != nullptr) delete[] str;
        }
        String(const String&other) {
            cout << "copy constructor" << endl;
            len = other.len;
            str = new char[len + 1];
            strcpy(str, other.str);
        }
        String(String&& other) {
            cout << "move constructor" << endl;
            len = other.len;
            str = other.str;             // take ownership
            other.str = nullptr          // other dtor shall not release!
        }
        String& operator = (const String& other) {
            cout << "copy assignment" << endl;
            String tmp = other;
            str = tmp.str;
            len = tmp.len;
            tmp.len = nullptr;
            return *this;
        }
        String& operator = (String&& other) {
            cout << "move assignment" << endl;
            str = other.str;
            len = other.len;
            other.len = nullptr;
        }
        void print()
        {
            cout << str << " len = " << this->len << " strlen = " << strlen(str) << endl;
        }
    };
    

    而且这是一个简单的用例,因为没有异常安全问题...

    TL/DR:除非你有很好的理由,否则只要有可能就依赖标准 C++ 类。如果您不能,则应用Rule of ThreeRule of Five 来管理资源所有权。

    【讨论】:

      猜你喜欢
      • 2015-11-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-17
      • 2022-07-22
      • 2017-09-22
      相关资源
      最近更新 更多