Page 1 of 1

Printing strings display smileys

Posted: Mon May 19, 2014 6:06 pm
by Roman
I have the following code:

Code: Select all

unsigned char *testb = (unsigned char*) 0x300000;
printf("%d\n", *testb);
*testb = "Q";
printf("%s\n", *testb);
I expect it to print "0", then "Q", but this happens:
Image

What's wrong? My OS has no multitasking, so nothing can overwrite 0x300000...

Re: Strange code behavior.

Posted: Mon May 19, 2014 6:21 pm
by Brendan
Hi,
Roman wrote:I have the following code:

Code: Select all

unsigned char *testb = (unsigned char*) 0x300000;
printf("%d\n", *testb);
*testb = "Q";
printf("%s\n", *testb);
The ASCII character 'Q' has the value 0x51, so "printf("%s\n", *testb);" prints the string at address 0x0000051.


Cheers,

Brendan

Re: Strange code behavior.

Posted: Mon May 19, 2014 8:42 pm
by sortie
Also did you mean 'Q' rather than "Q"?

You don't appear to understand C well. You need to learn C well immediately.

Re: Strange code behavior.

Posted: Mon May 19, 2014 8:45 pm
by bluemoon
Roman wrote:*testb = "Q";
Either you use a crappy compiler, or you ignored the warning:
warning: incompatible pointer to integer conversion assigning to 'unsigned char' from 'char [2]' [-Wint-conversion]

This give a hint on "what you code is not what you wanted™". The compiler tried to put whatever "Q" mean into a single char storage.

Change that to 'Q', and add a zero terminator to the string.

Re: Strange code behavior.

Posted: Mon May 19, 2014 9:00 pm
by AndrewAPrice
Roman wrote:What's wrong? My OS has no multitasking, so nothing can overwrite 0x300000...
Looks like your OS is happy. :)

Seriously though, double quotes are string literals (pointers to null terminates strings) while single quotes are character literals. You're writing a pointer address rather than a character value into memory. Replace "Q" with 'Q'.

Re: Strange code behavior.

Posted: Mon May 19, 2014 10:28 pm
by alexfru
Roman wrote:I have the following code:

Code: Select all

unsigned char *testb = (unsigned char*) 0x300000;
printf("%d\n", *testb);
*testb = "Q";
printf("%s\n", *testb);
What's wrong? My OS has no multitasking, so nothing can overwrite 0x300000...
You don't know enough C, that's what's wrong.

This line

Code: Select all

unsigned char *testb = (unsigned char*) 0x300000;
declares testb to be a pointer to unsigned char and initializes it with memory address 0x300000. That may be an invalid address (e.g. too large, without actual memory behind it or without access rights), btw.

This line

Code: Select all

printf("%d\n", *testb);
goes to the memory at the address that testb contains, 0x300000, grabs an unsigned char from there and calls printf() to print it as a number (e.g. ASCII code of the char). But did you store a character there (in the memory at address 0x300000), in the first place? If you didn't, what do you expect to be printed (assuming the memory is accessible and an unsigned char can be read from there)?

This line

Code: Select all

*testb = "Q";
creates a string literal in your program (2 bytes (those are C bytes, which aren't necessarily 8-bit octets!): one with the ASCII (normally, ASCII, but ultimately depends on the compiler/platform) code of the character 'Q' followed by another with code 0), in its read-only data section (one of the common places; but can also appear in the code section if it's read-only or in the regular data section if read-only/writable isn't supported by the compiler or the CPU), then it takes the memory address of it (the address of 'Q') and after converting it to type unsigned char stores to where testb points, to the memory at address 0x300000. Note, it doesn't store the code of that 'Q' there, it stores the address of that 'Q', possibly truncated and thus made invalid.

Finally, this line

Code: Select all

printf("%s\n", *testb);
goes to the memory again, fetches unsigned char from where testb points (it still points to 0x300000)... You do remember that tesb points to unsigned char, don't you? Then it takes this char (converted to int or unsigned int, depending on the size of char w.r.t. the size of int in your compiler), and passes it to printf() to print this char/int as though it actually was a pointer to an array of char containing some text terminated with a character with value 0. You're lying to printf() here, you're promising it a pointer to char (%s being the promise), but are actually giving it an int (possibly unsigned), whose alignment and size may differ from those of a pointer to char (thus, potentially making printf() access its second parameter incorrectly and damaging or crashing the program) and whose value may not be equal to a valid or expected address (thus making printf() print garbage, damage or crash the program).

So, there, you need to learn about:
- pointers
- arrays
- the relationship between the above: how arrays "decay" to pointers (that is, how they transform into pointers to the first array element)
- string literals (in 2 different contexts: a. initializers of arrays of char and arguments of sizeof, b. everywhere else (in expressions, in initializers of pointers))
- printf()
and some other things.

Re: Strange code behavior.

Posted: Tue May 20, 2014 12:15 am
by iansjack
Apart from your other errors, what makes you so sure that the printf routine can't overwrite 0x300000? Unlikely though it is, that is an assumption that may not be true.

Re: Printing strings display smileys

Posted: Tue May 20, 2014 12:33 pm
by Roman
Thanks everybody, I have got it working.

Re: Printing strings display smileys

Posted: Tue May 20, 2014 12:33 pm
by Roman
My OS laughs at my C skills.

Re: Printing strings display smileys

Posted: Tue May 20, 2014 9:57 pm
by metallevel
Roman wrote:My OS laughs at my C skills.
Forgive me for pointing this out, but if you're writing an OS in C, you really should be good at C :P . In fact, if you don't know C, you probably shouldn't be writing an OS at all! Even if you were writing an OS 100% in assembly, just about every low-level programmer knows C, and if you don't it could be a sign that you don't know much about programming in low-level languages in general.

I don't think a single one of those four lines of code you originally posted was doing what you thought it was doing, and I for one can't remember the last time I saw so many mistakes crammed into so little code. Even if your code seems to be working now, I can only imagine that it's corrupting memory or otherwise doing something awful without you realizing it.

Sorry to break it to you, but you need to learn a lot more about C before writing a kernel in it. Even the newest C programmer knows the difference between single and double quotes.