(yet another) Quick C++ question

Joined
Jul 8, 2001
Messages
610
Why is this declaration:

Code:
char* text = "text" ;

okay, but this one:

Code:
int* numbers = {1,2,3} ;

spits up this:

C:\Program Files\Microsoft Visual Studio\MyProjects\dfs\main.cpp(7) : error C2440: 'initializing' : cannot convert from 'const int' to 'int *'
Conversion from integral type to pointer type requires reinterpret_cast, C-style cast or function-style cast
C:\Program Files\Microsoft Visual Studio\MyProjects\dfs\main.cpp(7) : error C2440: 'initializing' : cannot convert from 'const int' to 'int *'
Conversion from integral type to pointer type requires reinterpret_cast, C-style cast or function-style cast
C:\Program Files\Microsoft Visual Studio\MyProjects\dfs\main.cpp(7) : error C2440: 'initializing' : cannot convert from 'const int' to 'int *'
Conversion from integral type to pointer type requires reinterpret_cast, C-style cast or function-style cast
Error executing cl.exe.

I'm more curious actually why the char example works than I am with why the int version doesn't. Can anyone explain to me what the difference is between these two cases?
 
I think of char* text = "text"; like this:

Code:
#include <iostream>
using namespace std;

int main() {
    static const char data_in_mem[] = {'t', 'e', 'x', 't', '\0'};
    char* text = (char*)&data_in_mem[0];
    cout << data_in_mem << endl;
    cout << text << endl;
    if ( data_in_mem == text ) {
        cout << "yes" << endl;
    }
}

Basically, when you do:

char* p = "text";

, it's not assigning "text" or { 't', 'e', 'x', 't', '\0' } to p. It's assigning an address that points to where the 't' is at in memory. int* also takes just one element(a pointer). That's why {1, 2, 3} doesn't work.

The char array ends in a null char to serve as an end marker of the array. Functions that deal with char*, start at the 't' spot in memory and move to the next spot until they hit the null char. That's why you can do 'cout << p' and get all the characters for example.

Since 0 is a valid int, if you have an int array, you can't use 0 as an end marker. So even if you could do:

int* i = {1, 2, 3}

and have i represent the spot in memory where 1 is at, doing 'cout << i' would just show an address and 'cout << *i' would just show 1. To spit out all the ints, you'd have to iterate through the array.

Basically, functions that deal with char* do automatic iteration so the arrays can behave as a whole ( string ).
 
Two follow up questions:
Code:
static const char data_in_mem[] = {'t', 'e', 'x', 't', '\0'};
char* text = (char*)&data_in_mem[0];

Why is the data_in_mem[] declared as a "const"? Would that not require that text is declared as const char*?

Also, why could we not do this:
Code:
static const int data_in_mem[3] = {1,2,3};
const int* ptr = data_in_mem;
It would also satisfy
Code:
if(data_in_mem == ptr)
 
exkoubitores said:
I say its cause he got lucky and it decided to compile. I don't think either of them are valid code as the space hasn't been allocated. The array works, because it allocates address space. It has nothing to do if it is a string or not (unless msvc allows some funny things to happen). Also, there is no defined end of array in C/C++, the "\0" in strings says its the end of a string, the array could be MUCH larger (and perhaps smaller).

So if you're going to use pointers without using arrays, you need to do some research on malloc. I strongly suggest using arrays to create the data and if you want, using pointers and references to mess around with (which is probably dangerous in your case).

Actually, the line

Code:
char *text = "text";

is valid C and C++ code. It means make a pointer called text that points to a string constant.

You can change where text points, but you can't modify the string "text."
 
exkoubitores said:
I say its cause he got lucky and it decided to compile. I don't think either of them are valid code as the space hasn't been allocated.
Huh? What space hasn't been allocated?

drizzt81 said:
Two follow up questions:
Code:
static const char data_in_mem[] = {'t', 'e', 'x', 't', '\0'};
char* text = (char*)&data_in_mem[0];

Why is the data_in_mem[] declared as a "const"? Would that not require that text is declared as const char*?

data_in_mem is declared as const because it can't change. The compiler will emit this data into the object file, and it's allowed to mark the data itself as constant. On Windows with the Visual C++ compilers, it will do so, telling the linker to put the "text\0" string into the const data segment. At runtime, trying to write to that data segment gets an exception.

In VC++, you have some control over this with the /GF and /Gf options.

In your code fragment text doesn't need to be declared as const char because you've used a cast to eliminate contsness. When you're not casting it away (either implicitly or explicitly), string literals are const.
 
In the C++ standard, string literals have a special exemption from the ordinary const-preserving rules.

The type of a string literal (i.e. "xyz") is "“array of n const char” and static storage duration" (C++ 1998 standard, 2.13.4/1).

String literals get a special case conversion which breaks the normal const rules: "A string literal (2.13.4) ... can be converted to an rvalue of type “pointer to char”;" (C++ 1998 standard, 4.2/2), which is deprecated (C++ 1998 standard D.4). This conversion exists for compatibility with C (which, if I recall correctly, at the time of the 1998 standard lacked const).

The second problem with your attempt to create a pointer to an array of ints is that C++ does not support array literals (in general). It does support aggregate initializers, which includes array initializers (8.5.1/4), though these require you to specify an array type.

Code:
int numbers[] = {1,2,3};

Should work, though it does something different from what a string literal (no implicit static storage class, no implicit const, etc...).
 
drizzt81 said:
Why is the data_in_mem[] declared as a "const"? Would that not require that text is declared as const char*?

When you do:

char* text = "text";
text[0] = 'g';

, it will compile, but crash.

I used static const to make sure that my char* text example would also behave the same way if I decided to do text[0] = 'g';. Because I used const, I casted to char* to make it work. It was just an attempt to better simulate char* text = "text";.
 
Shadow2531 said:
I used static const to make sure that my char* text example would also behave the same way if I decided to do text[0] = 'g';. Because I used const, I casted to char* to make it work. It was just an attempt to better simulate char* text = "text";.
This is a very intersting point. If you didn't use static on your local, the compiler would end up emitting code to allocate room for the array on the stack, then code to initalize the array each time.

Such a mistake is very common, and robs performance real fast in a function that is called often.
 
drizzt81 said:
Also, why could we not do this:
Code:
static const int data_in_mem[3] = {1,2,3};
const int* ptr = data_in_mem;

You can do that, but you can't tell the size of the array from just ptr alone. Since there's no end marker on the array, you can't have a function safely iterate through the array without knowing the size. In other words, void printIntArray(const int* a) {} wouldn't work for printing out each element of the array and therefore, functions can't treat the array as a whole through ptr like they could with a char* (by incrementing till they hit null).

In this case though, you know you can do sizeof(data_in_mem)/sizeof(data_in_mem[0]) to find the size or hardcode the 3 everywhere needed, so it's not a problem.

@op

However if the compiler supported doing:

int* n = {1, 2, 3};

, you wouldn't be able to iterate through it without knowing the size and it could not be treated as a whole like char* can. (Not that you'd want/need to. Just saying.)
 
Back
Top