12

I have a bit of a problem with my constructor. In my header file I declare:

char short_name_[2]; 
  • and other variables

In my constructor:

Territory(std::string name, char short_name[2], Player* owner, char units);
void setShortName(char* short_name);
inline const char (&getShortName() const)[2] { return short_name_; }

In my cpp file:

Territory::Territory(std::string name, char short_name[2], Player* owner, 
                     char units) : name_(name), short_name_(short_name), 
                    owner_(owner), units_(units)
{ }

My error:

Territory.cpp: In constructor ‘Territory::Territory(std::string, char*, Player*, char)’: Territory.cpp:15:33: error: incompatible types in assignment of ‘char*’ to ‘char [2]’

I already figured out that char[2] <=> char* but I'm not sure how to handle this about my constructor and get/setters.

Kimbluey
  • 1,199
  • 2
  • 12
  • 23
vicR
  • 789
  • 4
  • 23
  • 52
  • 4
    `I already figured out that char[2] <=> char* ` not really. – Rapptz Apr 09 '13 at 23:23
  • but i thought for the c++ compiler is char[2] equivalent to char* ?! i really have no idea how to init this constructor and the getters corretly... – vicR Apr 09 '13 at 23:30
  • 1
    Arrays and pointers are *very* different things. Read section 6 of the [comp.lang.c FAQ](http://www.c-faq.com/); the rules in this area are essentially the same for C and C++. – Keith Thompson Apr 09 '13 at 23:37

3 Answers3

20

Raw arrays in C++ are kind of annoying and fraught with peril. This is why unless you have a very good reason to you should use std::vector or std::array.

First off, as others have said, char[2] is not the same as char*, or at least not usually. char[2] is a size 2 array of char and char* is a pointer to a char. They often get confused because arrays will decay to a pointer to the first element whenever they need to. So this works:

char foo[2];
char* bar = foo;

But the reverse does not:

const char* bar = "hello";
const char foo[6] = bar; // ERROR

Adding to the confusion, when declaring function parameters, char[] is equivalent to char*. So in your constructor the parameter char short_name[2] is really char* short_name.

Another quirk of arrays is that they cannot be copied like other types (this is one explanation for why arrays in function parameters are treated as pointers). So for example I can not do something like this:

char foo[2] = {'a', 'b'};
char bar[2] = foo;

Instead I have to iterate over the elements of foo and copy them into bar, or use some function which does that for me such as std::copy:

char foo[2] = {'a', 'b'};
char bar[2];
// std::begin and std::end are only available in C++11
std::copy(std::begin(foo), std::end(foo), std::begin(bar));

So in your constructor you have to manually copy the elements of short_name into short_name_:

Territory::Territory(std::string name, char* short_name, Player* owner, 
                     char units) : name_(name), owner_(owner), units_(units)
{ 
    // Note that std::begin and std::end can *not* be used on pointers.
    std::copy(short_name, short_name + 2, std::begin(short_name));
}

As you can see this is all very annoying, so unless you have a very good reason you just should use std::vector instead of raw arrays (or in this case probably std::string).

David Brown
  • 13,336
  • 4
  • 38
  • 55
2

When a function wants an array as argument, it gets a pointer to the first element of an array instead. This pointer cannot be used to initialize an array, because it's a pointer, not an array.

You can write functions that accept references to arrays as arguments:

void i_dont_accept_pointers(const char (array&)[2]) {}

The problem here is, that this array reference cannot be used to initialize another array.

class Foo {
  char vars[2];
  Foo(const char (args&)[2])
    : vars(args)  // This will not work
  {}
};

C++ 11 introduced std::array to eliminiate this and other problems of arrays. In older versions, you will have to iterate through the array elements and copy them individually or use std::copy.

Oswald
  • 31,254
  • 3
  • 43
  • 68
0

C++ as C holds most of the rules of C.

In case of C, always use char* to pass array because that how C looks at it. Even sizeof (short_name_) will be 8 or 4 when passed to function. Now, you have a space of 2 bytes in variable short_name_

Constructor allocated memory for two bytes in short_name_ and you need to copy the bytes into that or use a char * pointer and assume it's size if 2.

Chapter 9 from Expert C Programming: Deep C Secrets is good read to understand it.

For simplicity this could be C style code.

#include <stdio.h>
#include <iostream>
using namespace std;
class Territory {
    private:
        string s;
        char c[2];
    public:
    Territory(std::string a, char b[2] /*visualize as char *b*/) {
        s  = a;
        c[0] = b[0]; //or use strcpy
        c[1] = b[1];
    }
    void PrintMe() {
        printf ("As string %s as char %c or %s\n",this->s.c_str(), c[0],c);
    }
};


main () {
    Territory a("hello", "h");
    a.PrintMe();
}
  • It's 2020 and buffer overflows are still a thing. I think examples/comments should reflect strncpy() instead of that classic strcpy(), particularly where beginners are looking. – JGurtz Oct 15 '20 at 20:06