basic c question regarding char *

Just going through the code and not trying to turn a blind eye to things I don't fully grasp.

We use lots of uninitialised pointers:

char *WhateverString

If I understand this right, before being initialised, whatever this will contain will be garbage because it's undefined right?

Secondly, we use char *WhateverString, to define a pointer to a string. We may not yet know it's size, so later on it will hold whatever we pass to it? Such as:

char *OnePastLastEXEFilenameSlash

we can't ever know what that's going to be before hand, hence using an empty char * to begin with? We're just telling the compiler, we're going to stick an unknown bunch of chars here later?

thanks.
zipzoomzap
Just going through the code and not trying to turn a blind eye to things I don't fully grasp.

We use lots of uninitialised pointers:

char *WhateverString

If I understand this right, before being initialised, whatever this will contain will be garbage because it's undefined right?

Secondly, we use char *WhateverString, to define a pointer to a string. We may not yet know it's size, so later on it will hold whatever we pass to it? Such as:

char *OnePastLastEXEFilenameSlash

we can't ever know what that's going to be before hand, hence using an empty char * to begin with? We're just telling the compiler, we're going to stick an unknown bunch of chars here later?

thanks.


Consider this statement:


char *foo;

If used in a struct, it creates some space in the struct for a pointer. If used in a function, it creates some space in the stack for a pointer. This is the same as if you were saying:


int z;

z is a space that an int fits into. char *foo does not create a space to store characters itself, just the pointer.

Consider this statement:


char bar[64];

If used in a struct, it creates some space for 64 chars. If used in a function, it creates that space on the stack

"foo" and "bar" are how you would reference that pointer and that space. Through the magic of C arrays, both "foo" and "bar" are pointers, although bar is a special type of pointer that your compiler can highlight certain mistakes with.

You could say:


foo = bar;

This replaces the value that is stored in the space on the stack/struct referred to be the word "foo" with the pointer to the start of the storage of bar.

In the case of *OnePastLastEXE..., we have space for a pointer. Above it, we have a path to the EXE as a char[MAX_PATH]. The latter EXE path has allocated space for a lot of chars. OnePastLastExe has allocated space for a pointer. The value stored in that space is a pointer to some space within the EXE Path.

Hope that helps.

Neil
Note that OnePastLastEXEFilenameSlash is stored in the struct win32_state, and that state is initialized like this:

1
    win32_state state = {};


When you initialize a struct with "{}" it tells the compiler to clear everything in it with zeroes.

When initializing the state, we know that the value of OnePastLastEXEFilenameSlash is zero (a NULL pointer in this case) until sometime later when it's updated in the way that the previous poster explained.

Do you have another example from the source code that looks confusing?
the pointer is just a memory address right?

That memory address will take us to the memory that contains the data we want?


I think i'm just getting confused as to when we're accessing a memory address or when we're going directly to data, or if they're the same and I should be ignoring the memory part. I know I'm probably not making sense.

I look at the code and I get it, it makes sense just not the why.
Yes on both questions. You seem to get it.

Accessing memory through a pointer is essentially the same as accessing data directly since everything is stored in memory at some address. The difference is that whenever we have a pointer, we need to explicitly specify when we need to reach through the pointer to access the memory. We do that with the dereference operator *.

Whenever we are dereferencing a pointer with "*mypointer", we are accessing the data located at the address that the pointer points to. It's the same thing with "mypointer->something", which is just another way of writing "(*mypointer).something".

In case of OnePastLastEXEFilenameSlash we want to use it to calculate how long the file path is where the executable is stored. You see how this is done in the Win32GetEXEFileName function.

So we have the pointer to EXEFileName (containing the full path including file name), and we have OnePastLastEXEFilenameSlash that points to the last slash in the char array. We can get the length of the directory portion of the path by taking the difference of the two pointers. This is used in the Win32BuildEXEPathFileName function when creating other paths with the same base directory.

I'm not sure if I'm making any sense or if I'm answering your question. You could try reading through this to see if the pieces fall into place.

The best way to learn is through experimentation, so play around with this stuff and see if it helps. :)
I've been having similar questions and I feel like I'm finally starting to get it a bit of a grasp on it.

I found this resource particularly helpful in testing and rounding out my understanding. Specifically sections six and eight.

It's just a FAQ but the answers are linked to, not listed inline, so there's this bonus of being able to try and answer the question yourself before clicking to see the answer.


Another thing that helped me was changing what I thought about / pictured in my head when I looked at / thought about a pointer. I found I had this habit of picturing the data the pointer was pointing to. (Pros probably do this automatically and have no problem.) But it helped me to instead force myself to picture that spot in memory where the pointer/address is stored. Subtle, minor different. But just trying consciously to do that seemed to help.

It's kind of tricky, though, because you have arrays which are like pointers but not really. And then there's string literals which sort of have their own rules. And what happens when you start to pass these things around to functions? Oh and don't even ask me about the heap, that's the stuff of folklore.. I'm not even sure it exists.

Anyway, I see why "pointers" are a common catching point for beginners, despite being very simple conceptually.
If you'd like to see Caseys explanation of pointers, watch the intro to C day 3 (link to episode guide https://forums.handmadehero.org/jace/videos/intro-to-c/day3.html ) starts at minute 8:05.
big thanks to all of you guys.

I think it's just the char pointer types that's throwing me off and how C works with strings in general. I don't even think twice when it comes to structs or numeric types for example. I just go: ah we have a struct that exists somewhere, we need to access it in this function so we need something that points to it, and when we use the function, we pass the thing that points to it, the memory address. As so:

void bla(some_struct *Structure)
{
// do stuff;
}

some_struct MyStruct = {};

bla(&MyStruct);

I'm correct with that right?

I did actually watch Casey's video, I should probably recap. The links given so far are helping also, thanks again.
After stepping through with the debugger, I'm getting it more and more.

To clarify something else, we do the following:

for(char *Scan = State->EXEFilename; *Scan; ++Scan)
{
//stuff
}

The condition is basically: keep repeating while not zero right? As soon as the loop hits NUL (\0) it becomes false as its equal to zero, and it exits.
Yeah, that's exactly right. Initially Scan is a char pointer to the beginning of a sequential bunch of chars.

Each step we move forward one char (it moves by one char because it's a char*, as apposed to a int32* or something).

The condition, *Scan, is sort of a terse way to check if the current char we're pointing to is the terminating null, which is at the end of every "string" in C. (Since the for loop condition is checked before running each iteration, once we do hit the end of the string, we don't actually run the loop on the null character itself.)

So it's basically scanning the whole string character by character, front to back.


Interesting tidbit, it's checking for null which in char is '\0' but in numbers is just 0. The char '0' is actually numerically something else, just the way 'A' is actually 65 or whatever.

Edited by Nines Baobaberson on