0

Notice the difference in both the codes in line "len = strlen(s);" in code 1: it is written before "strcpy(str,s)" and in code 2 its written after. What difference is it making? I ran my code in Dev C++ and i am getting different output. shouldnt the output be same in both cases?

Code 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;
}

Output 1 :

parameterised constructor
Hello World len = 1819043144 strlen = 11

Code 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;
}

Output 2 :

parameterised constructor
 len = 11 strlen = 1
  • 3
    Boiling down the snippets to a minimal version showing only the pieces of interest (the different `strlen` and `strcpy` calls and the output) might improve your question. – lubgr Aug 16 '18 at 07:28
  • Pretty sure it's undefined behavior, you strcpy without allocating the needed memory or anything. – Kaldrr Aug 16 '18 at 07:29

3 Answers3

5

The problem is here:

char str[];

this is a zero length array (BTW in standard C++ you cannot have zero length arrays).

This array can contain at most 0 chars, so in your case a zero length array is rather pointless.

Your are basically overwriting memory that does not belong to you and the behaviour of both of your programs is undefined. Because of the fact that the behaviour is undefined, it is also pointless to reason why the output is different in both versions.

Try this which lets you have strings up to a size of 100:

char str[100];

For working with strings in C++ you should rather use std::string than raw C strings.

Jabberwocky
  • 48,281
  • 17
  • 65
  • 115
1

The code is wrong, so the output is really undefined. You define char str[]; but at no point do you allocate a memory buffer to hold the contents of the string you're copying. In some cases this code will likely crash.

Better define: char *str;

And in the constructor:

str = new char[len+1];

Also don't forget to delete [] str in the destructor

Eyal Cinamon
  • 939
  • 5
  • 16
0

Oops, incomplete arrays should be avoided in C++!

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

should at least raise some warnings, because you declare an incomplete array (size 0!) which is not the last member of a Plain Old Data (C-like) struct. Even if not a syntax error, you can simply not store anything in that array. If you try, you will overwrite the content of next member, here len. BTW, it is the reason why you get inconsistent results.

What can be done?

The C++ idiomatic way of storing character strings is... std::string, but it gives you a simplistic wrapper over the native class.

class String {
private:
    std::string str;

The second simple way, if you can, is to declare a large enough array. It is what can be called the lazy C way. No allocation question here:

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

If you are brave enough, you can manage explicit allocation in your class: you allocate memory in constructor, and free it in destructor. But that means that you immediately need non default copy/move constructors and assignment operators, because you now need to be specific on which object owns the allocated data and can no longer rely on default implementations

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;
    }
};

And this is a simple use case because there is no exception safety problem...

TL/DR: unless you have a very good reason, just rely on standard C++ classes whenever you can. An when you cannot, then apply the Rule of Three, or Rule of Five to manage resource ownership.

Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252