0

Let me start by saying that I have, in fact, searched for this question in stackoverflow, but I have not found an answer to this exact question, so if this is a duplicate, feel free to just link me the question.

Also, I apologize for my novice, I'm just starting out with the c++ language.

I'm studying pointers, and in order to illustrate them, we can use this common example...

int main()
{

    // Assign value to string variable
    string foo = "hello";

    // Assign a pointer which is the address of foo
    string* bar = &foo;

    // Modify the value of the pointer
    *bar = "world";
    
    // Print the value stored at bar's address,
    // which is the same address of foo
    cout << *bar << endl; // world

    // Print the value of foo, which should be the same
    // as bar, because we modified the value at the same address
    cout << foo << endl; // world
    
}

And this completely makes sense. However, let's consider this (based on the number of questions that ask about it, seemingly counterintuitive) example.

int main()
{

    // Init a string pointer
    string* bar;

    // Do the same type of assignment we did in the previous example
    *bar = "world"; // BEHAVIOR IS UNDEFINED

    // Novice intuition tells us that the output would be "world"
    // based on the previous example.
    cout << *bar << endl;

}

Based on the other answers I've read, this is actually undefined behavior. Reason being because... and I'll just quote this answer... "The value of that storage location is a pointer. Since you did not initialize it to a valid pointer, its value is either a valid pointer or an invalid pointer; which is it? That's up to your compiler to decide. It could always be invalid. It could always be valid. Or it could be left up to chance." by Eric Lippert

And, ok, fair enough, that seems like a reasonable explanation as to why this is the way it is...

But to me this second example, as evidenced by my - and many others - intuition to gravitate towards it, seems like a very basic and common practice. I can see many situations where, to make use of pointers, I would want to initialize a pointer to some sort of "blank" or "null" value, and then make use of it later by giving it a value.

Or... am I completely misunderstanding the purpose of pointers? Does my second example not make any sense whatsoever? Is there a different way of approaching what I'm trying to achieve that I'm missing? Or is the first example the only way that we deal with pointers in this way; we must start with a variable and then init our pointer.

Please correct me if my terminology is incorrect. Again, I'm a beginner.

But ok so, I looked into nullptr and initializing my pointer like string* foo = nullptr but that seemed to lead to a segmentation fault error, pptaszni's comment seems to work appropriately.

int main()
{

    std::string* bar = nullptr;

    bar = new std::string("world");

    cout << *bar << endl;

}

This question is different, and should be reopened, because I am looking for the common practice in dealing with a seemingly common situation. All of these questions just describe "that there is undefined behavior" and I have already acknowledged that I understand that that is the case. My question is: "How do I do the thing that I'm trying to do, given that I can't do it the way I illustrated it in the second example"

πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190
Alec Mather
  • 742
  • 5
  • 20
  • `*bar = "world";` modifies the contents of `foo`. – Richard Critten Nov 01 '22 at 16:37
  • "I would want to initialize a pointer to some sort of "blank" or "null" value" -- you'll be glad to learn that C++ has something that's called `nullptr`. – Sam Varshavchik Nov 01 '22 at 16:37
  • Undefined behavior is, well, *undefined*. Because the compiler makes the assumption that your code is valid, undefined behavior could lead to all kind of weird things to happen. – Some programmer dude Nov 01 '22 at 16:38
  • `*bar = "world"; // BEHAVIOR IS UNDEFINED` Undefined Behaviour means that you __can't reason__ about the whole program. All you can do is it remove the UB and fix any remaining bugs. Have a read of [Undefined behavior](https://en.cppreference.com/w/cpp/language/ub) . – Richard Critten Nov 01 '22 at 16:38
  • C++ is a description of an abstract machine, and undefined behaviour is stated by the standard as "this standard imposes no requirements on the observable behaviour of a program with undefined behaviour". You aren't guaranteed *printed output* from your second snippet – Caleth Nov 01 '22 at 16:39
  • 2
    "I would want to initialize a pointer to some sort of "blank" or "null" value" - yeah, it's a common practice (well, maybe not specifically with strings). You can do `std::string* strPtr = nullptr;` and later `strPtr = new std::string("abc");` or `strPtr = &otherValidString;`. – pptaszni Nov 01 '22 at 16:39
  • And a pointer is really what it names imply: They are something which points somewhere else. Before you dereference a pointer you *must* make it point somewhere valid. It might be simpler to understand pointers if you take out a pen and some paper. Draw two boxes, label one `foo` and the other `bar`. Now draw an arrow from the `bar` box to the `foo` box. That's really how pointers work. – Some programmer dude Nov 01 '22 at 16:40
  • Also, uninitialized local variables will have *indeterminate* values. Using an indeterminate value in any way leads to undefined behavior. – Some programmer dude Nov 01 '22 at 16:40
  • A pointer is "just" a variable that holds the address of another variable. If you don't initialize it, it will contain a a random memory address. Imagine writing to a random address in memory, you can't control what happens after that (program might even seem to work). That is what undefined behavior is all about (and usually the result of programming errors). – Pepijn Kramer Nov 01 '22 at 16:40
  • 1
    Dereferencing a null pointer also has undefined behaviour. `*bar` has the hard precondition that `bar` points to a live `std::string` – Caleth Nov 01 '22 at 16:43
  • If, as you say, "MANY other people have this EXACT question." surely you could have found one of those instances? If one of the answers is deficient, maybe explain what you don't understand about it? – Nathan Pierson Nov 01 '22 at 16:52
  • You also might want to work with a simpler type like `int` to better understand pointers, since std::string themselves utilize dynamic memory and pointers, and you are getting 2 layers of "pointer" (one is implied) without understanding the basics of pointers. – franji1 Nov 01 '22 at 16:52
  • @franji1 no, std::string is sufficiently well behaved, such that you don't have to care that there are pointers involved in it's implementation – Caleth Nov 01 '22 at 16:58
  • @NathanPierson my question, like the others, asks about something that leads to undefined behavior, but as far as I see it, none of these questions actually lead to an answer that says... "hey, I know what you're TRYING to do, and you're doing it wrong, so here's how you SHOULD do it" – Alec Mather Nov 01 '22 at 17:10
  • @AlecMather It seems like the misunderstanding is that you think `*bar = "world";` is an instance of "mak[ing] use of [the pointer] later by giving it a value". You need to give the pointer `bar` _itself_ a valid value to use it. `*bar = "world";` is assigning a value to the `std::string` pointed to by `bar`, but in your example you never did anything to make `bar` point to anything meaningful. There are many ways you _could_ make `bar` point to something meaningful--pptaszni gave you one!--but you have to make _the pointer_ some valid value in its own right before you start dereferencing it. – Nathan Pierson Nov 01 '22 at 17:15
  • @AlecMather Don't use raw pointers in c++, you don't need them. End of Story. – πάντα ῥεῖ Nov 01 '22 at 17:15
  • 1
    Your question is about a local variable of pointer type that is not initialized. But the scenario is more general than that. Use of **any** uninitialized variable is undefined behavior, regardless of the type of the variable. Is this dangerous? Yes! Is it a common source of terrible bugs? Yes! Is this why we made all variables in C# either automatically initialized to zero or illegal to read before initialized? Yes! – Eric Lippert Nov 01 '22 at 22:59
  • If your question is a moral one -- what *should* I do -- some possible answers are: get in the good habit of always initializing every variable, use a static analysis tool which identifies when you've forgotten to initialize a variable, or use a language which does not have this problem in the first place. – Eric Lippert Nov 01 '22 at 23:01

1 Answers1

2

A pointer refers to an existing place in memory. To represent a pointer that doesn't point to anything, you can use nullptr. In your first example, bar points to foo. Every time you use *bar, you're referring back to foo. In your second example, there is no value you're pointing to, therefore doing anything with *bar is UB. You always either need to point to an existing variable on the stack string* ptr = &existingString;, or allocate memory using the standard library (e.g. new/malloc).

Mikdore
  • 699
  • 5
  • 16
  • Comments are not for extended discussion; this conversation has been [moved to chat](https://chat.stackoverflow.com/rooms/249282/discussion-on-answer-by-mikdore-how-to-init-a-pointer-and-assign-it-a-value-by-i). – Samuel Liew Nov 03 '22 at 13:01