Sorry your browser is not supported!

You are using an outdated browser that does not support modern web technologies, in order to use this site please update to a new browser.

Browsers supported include Chrome, FireFox, Safari, Opera, Internet Explorer 10+ or Microsoft Edge.

Dark GDK / One for the experts... (I/O Data Retrieval Weirdness)

Author
Message
dark coder
21
Years of Service
User Offline
Joined: 6th Oct 2002
Location: Japan
Posted: 9th Jul 2008 23:37
What's this then?

memblock = new char [size];
*(memblock + size) = '�';


Matt W
18
Years of Service
User Offline
Joined: 4th Mar 2006
Location:
Posted: 9th Jul 2008 23:39
Yes but you can't guarantee that when you try to access that memory (which you haven't allocated) your program won't segfault because you're accessing memory which you shouldn't, which is why you need to allocate 1 more byte.

Did you read that page which Benjamin linked? I think it would help.
Benjamin
21
Years of Service
User Offline
Joined: 24th Nov 2002
Location: France
Posted: 9th Jul 2008 23:40
Quote: "No, I believe that if something works once, it will work again."

You're horribly mistaken then. Just hope that the data you are overwriting isn't important.

David R
21
Years of Service
User Offline
Joined: 9th Sep 2003
Location: 3.14
Posted: 9th Jul 2008 23:43
Quote: "No, I believe that if something works once, it will work again."


Debug mode builds keep everything "happy families" in terms of memory. When you use release mode (i.e. the mode you have to compile in for anyone who doesn't own visual studio) things are much less friendly, and the contents of the memory much less predictable.


09-f9-11-02-9d-74-e3-5b-d8-41-56-c5-63-56-88-c0
Lilith
16
Years of Service
User Offline
Joined: 12th Feb 2008
Location: Dallas, TX
Posted: 9th Jul 2008 23:54 Edited at: 9th Jul 2008 23:57
See below

P.S. I hate some editors

Lilith, Night Butterfly
I'm not a programmer but I play one in the office
Lilith
16
Years of Service
User Offline
Joined: 12th Feb 2008
Location: Dallas, TX
Posted: 9th Jul 2008 23:56 Edited at: 9th Jul 2008 23:57
Believe it or not, I started a document the other day to explain all this character/pointer/array business so that we wouldn't keep addressing the same series of questions. It's a bit raw at the moment but someday I hope to present it to the forum for some cleaning up.

Let me try to explain some of it in as linear a fashion as I can for those who might still not quite understand what's going on.

You can have an array or characters:

char text [100];

which represents space for exactly 100 items of type char. It's empty but still holds room for 100.

You can also declare:

char text [100] = "12345";

which still reserves room for 100 chars but initializes the first six (yes, 6) bytes to the string specified (5 characters) plus one byte of value zero to tell most string related functions. If you were to declare:

char text [] = "12345";

it would only create enough space to hold the six chars, and that includes the terminating zero.

Most string (and I use the term string in its first incarnation, not the string class) related functions are passed the address of the array of characters that holds the string but nothing in that tells the function where the string ends. That's why a string needs to be terminated with a zero, to let the function know to quit processing. However, if you were to run:

int length = strlen(text);

it would assign length the value of 5, for the actual characters that make up the string but wouldn't count the necessary zero terminator. So if you were to use length as the number of bytes you try to save to a file, it would be saved without the terminator.

Bear in mind that file functions that handle text data can do weird things to the data that actually gets saved and the way it's interpreted when it's read back in. That's why you have to be careful in specifying the read/write mode when opening the file.

Now, I'm only guessing how some of the saving was handled, but if it only saved the text array (string) without the zero terminator then the file size is going to reflect that.

Now, using the new operator to reserve space for the data, if the data doesn't have the zero terminator in it, the string won't be properly terminated. So if some string function reads the string and doesn't find the nul terminator it will continue to process past the last char in the string array until it runs into a zero byte. So, we need to add a nul terminator at the end of the the data but we also need to reserve one extra byte in our dynamically allocated space, otherwise we run the risk of overwriting data belonging to some other memory block or having something that actually owns that space overwrite the nul terminator.

Lilith, Night Butterfly
I'm not a programmer but I play one in the office
Mahoney
16
Years of Service
User Offline
Joined: 14th Apr 2008
Location: The Interwebs
Posted: 10th Jul 2008 01:00 Edited at: 10th Jul 2008 01:01
Quote: "Debug mode builds keep everything "happy families" in terms of memory. When you use release mode (i.e. the mode you have to compile in for anyone who doesn't own visual studio) things are much less friendly, and the contents of the memory much less predictable."


Hence the reason I compile in Release mode from the start.
Ludo
16
Years of Service
User Offline
Joined: 23rd Jun 2008
Location: UK, Oxfordshire
Posted: 10th Jul 2008 01:05 Edited at: 10th Jul 2008 01:07
Lil, you said that I needed to have a "�" on the end of the data file to indicate it's end yes? Well I removed this:



And added this:



Onto the end of the data save function and it had the same effect; the rubbish data on the end has gone and the byte of value zero has been included in the memblock size statement:



Its memory is entirely its own now, yes?

Mahoney - I try doing that but it keeps coming up with some weird error about "Debugging information for "my file name" cannot be found or does not match" and then crashes when I click "Do you want to continue debugging? -> Yes"

We are here and it is now.
Mahoney
16
Years of Service
User Offline
Joined: 14th Apr 2008
Location: The Interwebs
Posted: 10th Jul 2008 01:10
Quote: ""Debugging information for "my file name" cannot be found or does not match""


You don't hit F5 to run a release mode build. You hit Ctrl+F5.
Ludo
16
Years of Service
User Offline
Joined: 23rd Jun 2008
Location: UK, Oxfordshire
Posted: 10th Jul 2008 01:13
Mahoney - Actually I was using the UI, not keys.

And that don't create a release mode build becuase my files in the Release folder were not updated...at least not in MS:VS 2008.

We are here and it is now.
Mahoney
16
Years of Service
User Offline
Joined: 14th Apr 2008
Location: The Interwebs
Posted: 10th Jul 2008 01:15
After building in release mode, either hit Ctrl+F5, or click "Run without debugging" under "Debug".
Ludo
16
Years of Service
User Offline
Joined: 23rd Jun 2008
Location: UK, Oxfordshire
Posted: 10th Jul 2008 01:17
Ah, that did it.

We are here and it is now.
Zuka
16
Years of Service
User Offline
Joined: 21st Apr 2008
Location: They locked me in the insane asylum.
Posted: 10th Jul 2008 22:06 Edited at: 10th Jul 2008 22:06
Whoa, didn't see the second page. Never mind.

Sephnroth
21
Years of Service
User Offline
Joined: 10th Oct 2002
Location: United Kingdom
Posted: 11th Jul 2008 01:42
I have tried to read all this, honestly. But I may have missed something. So if this information is all old to you Ludo, please ignore it But I really did read the posts!

The reason you have to zero terminate strings has actually nothing to do with the string object itself. A string containing "ludo" obviously contains ludo just fine and there it is stored in memory. The problem lays with dbText() and other outputting routines which need to read that variable.

Pointers, as we know, are just memory addresses of locations in the RAM. When you have a char *myVar containing "ludo" and you pass it to a function like dbText() your pointer tells the function where to begin reading. It then continues to read single characters until it hits a null terminator (which you assign using the escape sequence \0 - ie, myVar = "myString\0"). If the null terminator is not present it continues reading from the RAM until it hits a byte of memory that just happens to contain a value equal to \0.

If you're lucky then your program will crash. If you are unlucky then your program will tinker on like normal treating some bad block of memory as if it was legit and anything could happen - bad news because its real hard to debug when your program doesnt think there is anything wrong! You could end up overwriting other variables (that may not even belong to your program) or reading it strange values.

So its especially important when you are dealing with memory directly and convering it into displayable text (or just passing it to a text display function) that you ensure the string is null terminated at the point it should be - you cannot assume it just will be.

Now let me quickly deal with your comment on arrays and pointers. They are, in many ways, exactally the same thing. The index of an array provides a memory offset to a pointer. Imagine:

int myInt[5];

The variable myInt is now, for all intents and purposes, a pointer. Using in your code myInt[0] passes the value at the exact address of that pointer. However, if you used say myInt[2] then what its actually doing is:

location of myInt + (sizeof(int) * index) bytes;

So, as an int is 4 bytes then myInt[2] is location of myInt offset by 8 bytes. Its just pointer maths done on your behalf! This is why you must be so careful with array sizes and error checking the index value of arrays. If you try and index an array past its actual size then, if your lucky, again you get the crash. If you're unlucky then it pulls a random value out of the ram at a location of the intended variables start point + index * sizeof(type) and you could get /anything/

and finally, for those very reasons you can never assume that something that works once will work again if its not written correctly. I can see why you would draw that conclusion, but its a flawed logic. It only works if you assume that the only factors are those provided by your program. But unfortunately its not just your program running - windows is running all the while too as well as any number of other programs and services running along side your program which each could be doing different things at different times - which in turn could change depending on machine and again change after a simple reboot!

If you read the wrong peice of memory you might get the value "5" today which happens to not crash your program. But tomorrow your user may have left a new program running which uses that address in ram to store "24" and your program will die.

Bottom line - take extra special care when dealing with memory! and ALWAYS zero terminate strings that you have created manually!

Hope that helps in some way

Lilith
16
Years of Service
User Offline
Joined: 12th Feb 2008
Location: Dallas, TX
Posted: 11th Jul 2008 02:13 Edited at: 11th Jul 2008 02:17
I don't mean to be a stickler here but I feel I should make a distinction. A pointer is not an address. It's a variable that can hold an address. As a variable it can, well.... vary. Just like an int, float or other built-in types it can be assigned a value, typically an address, can be incremented/decrement, short-cut assigned and used in pointer arithmetic, which is not necessary as straight forward as regular arithmetic. And the type of pointer determines more than just what gets stored in it and what it does when it varies.

The fact that an array represents the address of the first byte of an array allows a pointer that contains the address of that first byte to be taken to be the same as the array as long as it's a pointer to the same type as the elements of the array. What you have to be careful about is that if you were to increment the pointer using, say, the ++ operator, it now points to the second element of the array, not the first, and you've effectively shortened your usable array by one element. Mind you, frequently pointers are used deliberately this way because, if handled properly, speeds up processing.

Just because a pointer can pass the address of an array (char for example, to a function, unless you put a marker (nul, for example 2) in the array or pass the number of elements to deal with to the function, it won't know where to stop processing.

Edit: You don't have to add a nul terminator when using a string literal. When you pass the address of the literal to a function it automatically appends the terminator when the literal is stored. However,

char myText [] = {'1', '2', '3', 'A', 'B'};

although it will store the characters, does not have a nul terminator unless you add it.

Lilith, Night Butterfly
I'm not a programmer but I play one in the office

Login to post a reply

Server time is: 2024-09-30 01:41:49
Your offset time is: 2024-09-30 01:41:49