I'm new to C++ and am trying the learn the concept of pointer. Could someone tell me why the C++ statement below is illegal? It seems to me to be legit but I have been told its illegal.
int null = 0, *p = null;
The C++ standard allows pointers to be assigned the value 0 as a constant.
However, the code:
int null = 0;
int *p = null;
does not set p to the constant 0, it sets it to the value of null, which is an integer variable.
If we generalize a little bit, and put int null = 0; on a line, and int *p = null; in a completely different line, with some code in between. There is nothing saying that the code in between doesn't do null = 4; - however, I don't think that is the main reason for not allowing this, but rather that it is easier to write a compiler that checks "is this the integer constant 0" than "is this named constant of the value zero". What if the constant is from another compile unit (a link-time constant)?
Also, reading 0 is much easier than having 46 different coding standards, each of which uses a different name for null.
Note that even if you make const int null = 0;, it is still not the constant 0 - it's a constant of the same value as 0, but not the same, lexically, as 0.
If you try compiling this code then you should get the following error:
error: cannot initialize a variable of type 'int *' with an lvalue of type 'int'
Essentially what is happening is that you are trying to initialize a pointer with a variable. The only thing that is allowed in C++ is assigning a pointer directly to a zero.
int * p = 0;
Something like the example above would work.
To define a pointer you need to use ampersand sign (&) as a prefix to the object you are passing.
int null = 0, *p = &null;
That is going to fix error: cannot initialize a variable of type 'int *' with an value of type 'int'
Related
You may have to forgive me as I'm new to C++ and may have made some fundamental errors with the code I have worked up so far.
static tuple<read_result, uint8_t*> m_scan_record(bool skip, uint32_t& size, FILE* file)
{
read_result result;
tuple<read_result, uint32_t*> rd_rec_size_result = m_read_generic_t<uint32_t>(file);
result = (read_result)get<0>(rd_rec_size_result);
if (result != read_success )
{
return tuple<read_result, uint8_t*>(result, nullptr);
}
size = (uint32_t) get<1>(rd_rec_size_result);
if ( skip )
{
fseek(file, size, SEEK_CUR);
}
// ...
}
template<typename T>
static tuple<read_result, T*> m_read_generic_t(FILE* file)
{
T ret = 0;
read_result result = m_read_from_file_to_buffer(&ret, sizeof(T), file);
if (result == read_success)
{
return tuple<read_result, T*>(result, &ret);
}
return tuple<read_result, T*>(result, nullptr);
}
When I compile this code I am getting this error:
cast from ‘std::__tuple_element_t<1, std::tuple<read_result, unsigned int*> >’ {aka ‘unsigned int*’} to ‘uint32_t’ {aka ‘unsigned int’} loses precision [-fpermissive]
My intentions and what I am expected to do/happen:
In the declaration of m_scan_record, the size argument is declared with a & which is intended to allow me to pass the value by reference, analogous to using the REF c# keyword
I make a call to generic (template) function m_read_generic_t which is called with the specified type <unit32_t> and therefore (according to its definition) will return a type of tuple<read_result, uint32_t*>
Once I have the tuple returned by m_read_generic_t, I want to take the unit32_t value pointed to by the second value of the tuple, and put that value into the size variable mentioned at point 1, above, which presumably will then be accessible to the calling function one step further up the stack.
From the above points you can hopefully see that my intention (and I appreciate that I may be far away in reality!) is that at this line:
size = (uint32_t) get<1>(rd_rec_size_result);
all I am doing is simply grabbing a 'pointed to' value and putting it into a variable of a matching type, much like the oft-cited textbook example:
uint32_t v = 123;
uint32_t* ptr_to_v = &v;
uint32_t x = ptr_to_v; // x == 123
Clearly this is not what is really going on with my code, though, because if it were, I presume that the cast would be un-needed. But if I remove it, like this:
size = get<1>(rd_rec_size_result);
then I get a compile-time error:
a value of type "std::__tuple_element_t<1UL, std::tuple<read_result, uint32_t *>>" cannot be assigned to an entity of type "uint32_t"
I believe therefore that I am doing something badly wrong - but I can't work out what. Is this to do with the way I am taking the pointer out of the tuple; or is there something else going on when it comes to the getting a uint32_t value from a uint32_t* ?
This is all in a C++ environment on Ubuntu 20.04, FWIW
Many thanks in advance for any/all suggestions; please go easy on me!
tuple<read_result, uint32_t*> rd_rec_size_result = ...
The 2nd member of this tuple, as explicitly declared here, is a pointer to a uint32_t. That's what uint32_t * means, in C++.
size = (uint32_t) get<1>(rd_rec_size_result);
This retrieves the uint32_t * and attempts to convert it to a uint32_t. C++ does not work this way. Although this conversion can be forced your compiler has every right to believe that whatever this code is trying to do it must be wrong.
Perhaps I was wondering initially, your intention was to dereference the pointer. This is the reason for your compilation error, in any case. If your intention was to, truly, dereference this pointer, then this would've been a simple matter of changing this to
size = *get<1>(rd_rec_size_result);
However, that's not going to be the end of your troubles. Even after this compilation error is fixed, this way, the shown code will still be badly, badly broken.
This is because m_read_generic_t returns a pointer to a local object, which will get destroyed when the function returns, and attempting to dereference this pointer, here, will make demons fly out of your nose.
The real fix here is to change m_read_generic_t to not return a pointer as the 2nd value in the tuple in the first place, thus eliminating the compilation error in the first place.
I'm reading a piece of code which does this
void *ptr_to_something = (void*)0x23; // I made this up, it's just a pointer to something
void *addr;
void *dst = (void*)(addr = ptr_to_something); // This is the line that confuses me
it seems to assign a pointer to something to another pointer of the same thing. And that's okay.. but then the result is enclosed in parenthesis, cast to the same thing and somehow reassigned to a third pointer to the same thing.
Is this valid C++ at all? Is it guaranteed that assigning the result of an assignment yields the same assigned object?
It is valid in C++ to do:
int a, b, c;
a = b = c = 1;
So, therefore the result of an assignment is the value of that assignment.
What the cast is for, is a mystery*. Have you tried removing it, and does that generate a warning?
*not a mystery, just perhaps unnecessary.
This is valid C++. The choice to format the code this way is odd and not advised, but it's perfectly legal.
This code is equivalent to this (what I consider to be better written) code:
void *ptr_to_something = (void*)0x23;
void *addr = ptr_to_something;
void *dst = addr;
I have code that looks like this:
char* talk[516] = {(char*)1};
#define testValue (*(int*)talk[0])
I receive a null pointer exception when the following line of code is then called.
testValue = 0;
Why is that? Haven't all value of the talk[] been initialised?
EDIT
What I want to do is, there are 516 number values (floats and ints) which are stored in char* array. testValue should point to the int value that is stored in the first element of the array. The next value along might be #define testValue2(*(float*)talk[1]).
For char* talk[516] = {0};, all the elements (i.e char*) of talk are initialized with value 0 (i.e. null pointer).
For char* talk[516] = {(char*)1};, the 1st element of talk is initialized with value 1, which is not a valid value for pointer. (And the other elements are still initialized as null pointer.) *(int*)talk[0] fails, because you're trying convert a char* to int*, which points to memory address 1, and then dereference on it.
You should initialize elements with valid value, such as:
int i;
float f;
void* talk[516] = {&i, &f, /*...*/};
#define testValue (*(int*)talk[0])
#define testValue2 (*(float*)talk[1])
testValue = 1;
testValue2 = 1.5;
1. Note if use new to initialize elements of talk, you need to delete them at last.
2. You could use loop to initialize talk too.
3. Using char* instead of void* is confusing, IMO.
4. Consider about using std::map instead of macro magic.
char* talk[516] = {0};
This means that the elements of the array talk are all NULL pointers. So when you try to deference it you get that exception.
See the following substitution sequence
testValue = 0;
#
(*(int*)talk[0]) = 0;
#
char* c = talk[0]; // == 1
(*(int*)c) = 0;
#
char* c = talk[0];
int* i = (int*)c; // == ? (*)
(*i) = 0;
* Alignment issues on pointer casting
Casting a char* to int* results in undefined behavior, since int* has stricter alignment requirements than char*.
new learner ; something puzzle about pointer;
As I learn from books, before using the pointer it must be initialized , so we usually use like this
int a = 12;
int * p = &a;
so I understand why int* p = 12 is wrong ,because it has no address;
then I find something today while coding , That is from this :
char * months[12] = {"Jan", "Feb", "Mar", "April", "May" , "Jun", "Jul"
,"Aug","Sep","Oct","Nov","Dec"};
Then another usually used situation came to my mind , That is :
char *p = "string"; (this is ok , why int * a = 12 can't be allowed ?)
I am puzzled. when is it initialized and how ? and why int * a = 12 can't be auto initialized ? maybe something about the arrange of memory.
First off:
int a = 12;
int* p = &a;
This works because &a is a memory address.
int* p = 12;
This fails mostly because 12 is not a memory address. It's also true that 12, by itself, has no address, but this would be better reflected by a snippet like int* p = &12; (which wouldn't work, as you correctly noted).
An interesting property of pointers is that they are often used to designate the start of a list of values. For instance, take this array of integers:
int a[] = {1, 3, 7, 13};
It can trivially be turned into an integer pointer.
int* p = a; // magic!
The pointee is the first element of a, so *p == 1. Now, you can also do p[0] (which is 1, too), p[1] == 3, p[3] == 7, and p[4] == 13.
The reason char* foo = "bar" works is that "bar" is not a single value: it's a character array in disguise. Single characters are denoted by single quotes. As a matter of fact:
"bar"[0] == 'b'
"bar"[1] == 'a'
"bar"[2] == 'r'
The compiler has special support for string literals (quoted strings) that make it possible to assign them straight to pointers. For instance, char* foo = "bar" is valid.
A C99-compliant compiler also has support for array literals. For instance, int* p = (int [3]){1, 2, 3}; is valid. The character array and the int array will be given a global address, because the people who made C felt that it was a useful thing to do.
int* p = 12 is wrong because the assigned value may or may not belongs to memory address. You are forcing p to point at that location.
char *p = "string" is allowed because compiler already has set the space for the string and p is pointing to the first character of that string.
It comes down to types.
In both C and C++, the type of a plain integer literal like 12 is int. There is no implicit conversion from the type int to the type int*, which makes sense: a pointer and an integer are, conceptually, completely different things. So int *p = 12; is invalid.
In C, a plain string literal like "abc" is translated into a static array of chars (of size exactly sufficient to store abc plus a terminating null char). The type "array of chars" is implicitly convertible to the type char* (pointer to char) - arrays are said to decay into pointers. So the assignment char *p = "abc"; is valid.
But there's a catch: it's undefined behavior to modify that array (both in C and C++). That conversion is in fact deprecated (or even illegal) in C++, and you should use const char * instead.
In reality the gcc compiler will warn you about:
char* p = "hello";
This is because "hello" is now treated as an equivalent to const char*.
so this would be better:
const char* p = "hello";
But yes as other people have described, "hello" has an address which points to the start of a fixed sequence of characters.
int* p = 12 is wrong in the sense that it does something that is almost definitely not what you think it does. Assuming that your compiler doesn't complain that you are trying to implicitly cast an int into a int *, this is not illegal. What you did was, point p at memory location 12, which is almost definitely something you shouldn't be reading. The assignment is legal, but if you dereference that pointer you are in undefined behavior territory. If you are in user mode, then *(int*)12 is probably a segmentation fault.
Using C terminology, the difference between these two cases is that string literals exist, which are an array of char (and thus an lvalue, so you can take their address or point to them); but in C90 there are no other literals. 12 is an integer constant, not a literal. You can't do &(12), because the language says so. Brace-enclosed initializer lists are not values. Constants are rvalues; literals are lvalues.
In C++ the behaviour is the same, however C++ uses different terminology for the same thing. In C++, constants are all called "literals", however they are also all rvalues (except for string literals) so you cannot take their address.
C99 added array literals of other types.
The language has made an exception and allows string literals to be used to initialize char const*. Some compilers are less strict and allow string literals to be used to initialize char* as well.
Update, in response to comment by Pascal Cuoq
In C and C++, the following are valid ways to initialize variables using string literals:
char carr[] = "abc";
char carr[10] = "abc";
char const* cp = "abc";
The following are valid ways to initialize variables using integer literals in an initializer list:
int iarr[] = {1, 2, 3};
int iarr[10] = {1, 2, 3};
However, the following is not a valid way to initialize a variable using integer literals in an initializer list:
int const* ip = {1, 2, 3};
That's what I meant when I said the language has made an exception and allows string literals to be used to initialize char const*.
Many programmers are confused by the syntax of specifying a pointer.
int* p;
int *p;
int * p;
All of the above declare the same thing: a pointer, p, which either be NULL or the address of an integer-size storage unit in memory.
Thus
int * p = 12;
declares a pointer, p, and assigns it the value 12.
In C and C++ pointers are just variables with special meaning to the compiler such that you are allowed to use special syntax to access the memory location whose value they hold.
Think about this a different way for a moment.
Think of the number "90210". That could be your bank balance. It could be the number of hours since you were born.
Those are all simple "integer" interpretations of the number. If I tell you that it is a Zip Code - suddenly it describes a place. 90210 isn't Beverly Hills in California, it is the [postal] address of Beverly Hills in California.
Likewise, when you write
int * p = 12;
you are saying that "12" is the address of an integer in memory, and you're going to remember that fact in the pointer-variable p.
You could write
int * p = &12;
This will force the compiler to generate an integer storage unit in the program executable containing the native integer representation of 12, and then it will generate code which loads the address of that integer into the variable p.
char* p = "hello";
is very different.
12; // evaluates to an integer value of 12
"hello"; // evaluates to an integer value which specifies the location of the characters { 'h', 'e', 'l', 'l', 'o', 0 } in memory, with a pointer cast.
int i = 12; // legal
char h = 'h'; // legal
const char* p = "hello"; // legal
uintptr_t p = "hello"; // legal
The double-quotes in C and C++ have a special meaning, they evaluate to a pointer to the string contained in them, rather than evaluating to the string itself.
This is because
"The quick brown fox jumped over the lazy dog"
wouldn't fit into a CPU register (which are 32 or 64 bits depending on your machine). Instead, the text is written into the executable and the program instead loads the address of it (which will fit into a register). And this is what in C/C++ we call a pointer.
I have found on stackoverflow one good hint: always initialize declared pointer with NULL.
This will help understanding that newly created pointer cannot be used without further actions.
So the actions that should follow have to assign proper address to the pointer (initialize that pointer).
To do what was probably your intention with original code
int *p = 12;
you have to do for example:
int *p;
p = malloc(sizeof(p)); /* pointer p holds address of allocated memory */
*p = 12;
or another example:
int *p;
const int a = 12;
p = &a; /* pointer p holds address of variable a */
Why char *p = "string"; is correct was answered by others above. This is just another way of pointer initialization.
Also, similarly, in one of answers here on stackoverflow I have found really nice option for your case:
int *p = &(int){12};
int main()
{
int* nir = new int; // creating dynamic memory
*nir = 7; // assigning value
cout << *nir << endl;
delete nir; // deleting
nir = 0; // **is this line for assigning the address nir=0? 0 is also part of memory right? Why didn't we put NULL?
*nir = 8; // **can i do like this and change the value, so that the output can be 8 now?
cout << *nir << endl;
delete nir;
nir = 0;
return 0;
}
This is the code that I created to understand new. But, even though it was compiled fine by Code::Blocks, during the runtime, it crashes. I have two questions, which I have already mentioned in comment part.
nir = 0;
is this line for assigning the address nir = 0? 0 is also part of memory right? Why didn't we put nir = NULL?
*nir = 8;
can I do like this and change the value, so that the output can be 8 now? After all, I have already deleted the *nir value.
nir=0;
This sets the pointer to NULL. 0 and NULL are the same in this context.
*nir=8
This is wrong as nir in not a valid pointer. It's no suprise that it crashes!
cout<<*nir<<endl;
This is also wrong as nir is invalid pointer. You cannot read or write.
delete nir;
This is harmless, as deleting a NULL pointer is safe (it does nothing).
This code snippet is wrong
nir=0; //**is this line for assigning the address nir=0? 0 is also part of memory right? Why didn't we put NULL?
*nir=8; //**can i do like this and change the value, so that the output can be 8 now?
cout<<*nir<<endl;
delete nir;
nir=0;
You did not allocate memory and are trying to write to address 0.
*nir=8; //**can i do like this and change the value, so that the output can be 8 now?
Usually the program will crash.
As for the line
nir = 0;
then it is equivalent to
nir = NULL;
In C++ NULL usualy defined as 0 or ( long )0 and so on.
According to the C++ Standard
1 A null pointer constant is an integer literal (2.14.2) with value
zero or a prvalue of type std::nullptr_t. A null pointer constant can
be converted to a pointer type; the result is the null pointer value
of that type and is distinguishable from every other value of object
pointer or function pointer type.
You tagged c++ so I recommend using nullptr instead of 0/NULL
nir = nullptr;
The problem
The literal 0 (which is essentially of type int) also serves as a null pointer literal in C++. This kludge results in ambiguity and bugs.
Solution
Use the nullptr keyword instead of 0 to indicate a null pointer value
source
A short breakdown of errors you purposely committed:
int main()
{
int* nir = new int; // allocating dynamic memory
*nir = 7; // assigning value
cout << *nir << endl;
delete nir; // deleting
nir = 0; // **is this line for assigning the address nir=0?
// 0 is also part of memory right? Why didn't we put NULL?
The previous comment is wrong. For historical reasons, assigning a literal 0 to a pointer variable means setting it to a null pointer constant. This is not guaranteed to be 0 [!!!]. NULL and nullptr_t are more modern...
*nir = 8; // **can i do like this and change the value,
// so that the output can be 8 now?
cout << *nir << endl;
On some systems you can do that. But your computing platform is now irretrievably corrupted. Modern systems catch the culprit and raise General Protection Fault, which only kills your program.
delete nir;
Because programmers are keen to avoid useless work, the above (delete NULL) is defined as a no-op
nir = 0;
return 0;
The previous two lines are useless, as nir is never used again and main per standard returns 0 unless it explicitly doesn't, quite in contrast to any other function.
}