%d specifier for an uninitialized character array - c

#include <stdio.h>
int main()
{
char a[8];
printf("%d\n",a) ;
return 0;
}
For the above code the output was this :- 2686744
What is the reason behind this output?
I found that the output doesn't depend on the content of the array, but on the size of the array.I just want the explanation.

char a[8];
printf("%d\n",a);
This code has undefined behavior.
The array expression a is implicitly converted to a char* value, equivalent to &a[0]. That value is then passed to printf -- but since the %d format requires an int argument, the behavior is undefined.
If int and char* happen to have the same size, and if they're passed as function arguments using the same mechanism, then it will likely print a decimal representation of the address of (the initial element of) the array.
But don't do that. If you want to print the address:
printf("%p\n", (void*)a);

You are printing the address of the array as an integer.
If you compile with -Wall to enable warnings your compiler should complain about that.

Related

Pointers and Characters Variables in C

I'm trying to store the memory location of the evil variable in the ptr variable, but instead of the memory location, the variable prints out the value within the variable and not it's location. Both the ptr variable and the &evil syntax print the same result which is the value of the variable and not it's location in memory. Could someone please nudge me in the right direction, and help me determine the syntax needed to store the memory location of a string/char variable in C?
int main()
{
char *ptr;
char evil[4];
memset(evil, 0x43, 4);//fill evil variable with 4 C's
ptr = &evil[0];//set ptr variable equal to evil variable's memory address
printf(ptr);//prints 4 C's
printf(&evil);//prints 4 C's
return 0;
}
That's normal, because the first parameter to printf is a format specifier, which is basically a char* pointing to a string. Your string will be printed as-is because it does not contain any format specifiers.
If you want to display a pointer's value as an address, use %p as a format specifier, and your pointer as subsequent parameter:
printf("%p", ptr);
However, note that your code invokes Undefined Behavior (UB) because your string is not null-terminated. Chances are it was null-terminated "out-of-luck".
Also notice that to be "correct code", cast the pointer to void* when sending it to printf, because different types of pointers may differ on certain platforms, and the C standard requires the parameter to be pointer-to-void. See this thread for more details: printf("%p") and casting to (void *)
printf("%p", (void*)ptr); // <-- C standard requires this cast, although it migh work without it on most compilers and platforms.
You need #include <stdio.h> for printf, and #include <string.h> for memset.
int main()
You can get away with this, but int main(void) is better.
{
char *ptr;
char evil[4];
memset(evil, 0x43, 4);//fill evil variable with 4 C's
This would be more legible if you replaced 0x43 by 'C'. They both mean the same thing (assuming an ASCII-based character set). Even better, don't repeat the size:
memset(evil, 'C', sizeof evil);
ptr = &evil[0];//set ptr variable equal to evil variable's memory address
This sets ptr to the address of the initial (0th) element of the array object evil. This is not the same as the address of the array object itself. They're both the same location in memory, but &evil[0] and &evil are of different types.
You could also write this as:
ptr = evil;
You can't assign arrays, but an array expression is, in most contexts, implicitly converted to a pointer to the array's initial element.
The relationship between arrays and pointers in C can be confusing.
Rule 1: Arrays are not pointers.
Rule 2: Read section 6 of the comp.lang.c FAQ.
printf(ptr);//prints 4 C's
The first argument to printf should (almost) always be a string literal, the format string. If you give it the name of a char array variable, and it happens to contain % characters, Bad Things Can Happen. If you're just printing a string, you can use "%s" as the format string:
printf("%s\n", ptr);
(Note that I've added a newline so the output is displayed properly.)
Except that ptr doesn't point to a string. A string, by definition, is terminated by a null ('\0') character. Your evil array isn't. (It's possible that there just happens to be a null byte just after the array in memory. Do not depend on that.)
You can use a field width to determine how many characters to print:
printf("%.4s\n", ptr);
Or, to avoid the error-prone practice of having to write the same number multiple times:
printf("%.*s\n", (int)sizeof evil, evil);
Find a good document for printf if you want to understand that.
(Or, depending on what you're doing, maybe you should arrange for evil to be null-terminated in the first place.)
printf(&evil);//prints 4 C's
Ah, now we have some serious undefined behavior. The first argument to printf is a pointer to a format string; it's of type const char*. &evil is of type char (*)[4], a pointer to an array of 4 char elements. Your compiler should have warned you about that (the format string has a known type; the following arguments do not, so getting their types correct is up to you). If it seems to work, it's because &evil points to the same memory location as &evil[0], and different pointer types probably have the same representation on your systems, and perhaps there happens to be a stray '\0' just after the array -- perhaps preceded by some non-printable characters that you're not seeing.
If you want to print the address of your array object, use the %p format. It requires an argument of the pointer type void*, so you'll need to cast it:
printf("%p\n", (void*)&evil);
return 0;
}
Putting this all together and adding some bells and whistles:
#include <stdio.h>
#include <string.h>
int main(void)
{
char *ptr;
char evil[4];
memset(evil, 'C', sizeof evil);
ptr = &evil[0];
printf("ptr points to the character sequence \"%.*s\"\n",
(int)sizeof evil, evil);
printf("The address of evil[0] is %p\n", (void*)ptr);
printf("The address of evil is also %p\n", (void*)&evil);
return 0;
}
The output on my system is:
ptr points to the character sequence "CCCC"
The address of evil[0] is 0x7ffc060dc650
The address of evil is also 0x7ffc060dc650
Using
printf(ptr);//prints 4 C's
causes undefined behavior since the first argument to printf needs to be a null terminated string. In your case it is not.
Could someone please nudge me in the right direction
To print an address, you need to use the %p format specifier in the call to printf.
printf("%p\n", ptr);
or
printf("%p\n", &evil);
or
printf("%p\n", &evil[0]);
See printf documentation for all the ways it can be used.
if you want to see the address of a char - without all the array / pointer / decay oddness
int main()
{
char *ptr;
char evil;
evil = 4;
ptr = &evil;
printf("%p\n",(void*)ptr);
printf("%p\n",(void*)&evil);
return 0;
}

Function in C with unknown input data type

I'm trying to write a simple function that will convert a number to a string with respect to a given format, something like this:
char * write_number(const char *format, void *number) {
char mystring[50];
sprintf(mystring, format, number);
return mystring;
}
The only problem is that this works with the integer data type, but not with the float/double data type. Is there any way to make this work regardless of the data type.
Thanks!
Addition to the original question:
Thank you for your suggestions. I didn't manage to make it work, here is the complete example of what am I trying to get.
#include <stdio.h>
void write_number(const char *format, void *number) {
char mystring[50];
sprintf(mystring, format, *number);
printf("%s\n", mystring);
}
int main(void) {
int a = 10;
float b = 20;
double c = 40;
write_number("int: %03d", &a);
write_number("float: %6.2f", &b);
write_number("double: %6.2f", &c);
return 0;
}
The format string for sprintf specifies the data type, eg "%i" prints and integer and "%f" prints a float - hence you would need to change the format string depending on the data type.
Note also that the function is not guarnetied to work for ints either, since its returning a pointer to a local variable mystring, this will go out of scope when the function returns and can be overwritten at any time by another value, so then the returned pointer will be pointing to some random value rather than the string.
For the calling code to use the address of the local variable mystring[] is undefined behavior.
"why does this work with integer data type?"
Undefined behavior does not mean code "won't work". It does does not mean if it works today (with integer) , it will work tomorrow. It means the behavior is not defined.
sprintf already takes care of the types of its arguments; you just need to make sure that the format string you pass to it matches it arguments. For example, "%f", 3.125f to format a float, "%d", 12 to format a signed integer, "%e", 3.125 to format a double precision float. Try reading this.
Your function won't work anyway, though, as compilation errors will tell you. mystring is declared as a local variable on the stack, so it will cease to exist when your function returns.
Also, you are passing the address of number to sprintf instead of its value. To pass the value, you need to dereference it like this: sprintf(mystring, format, *number);
You function may work with an integer type because pointers are integral, so if you called write_number("%d\n", 55), you would be passing a pointer to memory address 55, which sprintf would interpret as an integer because of the %d. If your ints and void *s weren't the same size this wouldn't work.
To actually pass a pointer instead of an int casted to a pointer do something like this:
char *str;
float num = 8.375f;
str = write_number("%f", &num);

Pointers to constants in C

I compiled the following code:
#include <stdio.h>
int main(void) {
// your code goes here
char *consta = "ABC";
printf("Use of just const: %c\n", consta );
printf("Use of const[1]: %c\n", consta[1]);
printf("Use of whole string: %s", consta);
return 0;
}
However, the output that I get is:
Use of just const: P
Use of const[1]: B
Use of whole string: ABC
The second printf and the third printf function calls work as expected however, I was expecting 'A' to be printed instead of 'P' in the first call to printf.
consta is a pointer to a character. The formatting specifier %c expects an argument of type char (character)†, not char* (pointer to character). Your code exhibits undefined behavior. Try to dereference consta instead:
printf("Use of just const: %c\n", *consta);
where *consta is incidentially equal to consta[0].
† Actually, the argument is of type int and is converted to unsigned char by printf(). This has to do with argument promotion rules that apply to functions with variable arguments; an argument to printf() of type char is promoted to int before passing it to printf(), which is why printf() has to promote it back. For most programs the difference does not matter.
consta is pointer containing the address of a string.
You're telling printf to treat that as a character, which is undefined behavior. Pointers are usually implemented as storing the address as a number, so it will typically print the ASCII value of that address.
You want to pass the value at that address (which the pointer points to) by writing *consta.
char *consta = "ABC";
is a pointer to char pointing to "ABC", to be exact to the 1st element of "ABC", that is 'A'.
"Pointing to" means consta contains an address, here the address of the 'A'.
To print a pointer, will say an address, use the conversion specifier %p:
printf("Use of just const: %p\n", (void*) consta);
I can spot three problems with your code:
The variable consta points to a constant string so you should to make it a constant:
const char *consta = "ABC";
The second argument to the first print statement should be the first character in the string rather than a pointer:
printf("Use of just const: %c\n", consta[0]);
In the last print statement there is no final newline. This implies that there may be no output from it.
If you want your code to be standard ANSI C conformant you also need to change the line-comment (//...) to a block comment (/*...*/).
It is also a good idea to enable all warnings in your compiler. With the popular GCC compiler I use the following options:
-ansi -fsanitize=address -g -pedantic -Wall -Wfatal-errors

C Programming Simple Pointers

I'm a beginner at learning pointers. Here is my code. (Note: I'm still trying to get my head around pointers so my code won't be clean.)
#include <stdio.h>
#include <stdlib.h>
int main (int argc, char *argv[]){
int a = 1;
char b = 's';
double c = 3.14;
int *ptra;
int *ptrb;
int *ptrc;
ptra = &a;
ptrb = &b;
ptrc = &c;
printf("I initialised int as %d and char as %c and double as %.2f\n", a, b, c);
printf("The address of A is %p and the contents of A is %d\n", ptra, *ptra);
printf("The address of B is %p and the contents of B is %c\n", ptrb, *ptrb);
printf("The address of C is %p and the contents of C is %.2f\n", ptrc, *ptrc);
I expected the following output:
I initialised int as 1 and char as s and double as 3.14
The address of A is 0xbf933094 and the contents of A is 1
The address of B is 0xbf933093 and the contents of B is s
The address of C is 0xbf933098 and the contents of C is 3.14
But instead I get this:
I initialised int as 1 and char as s and double as 3.14
The address of A is 0xbf933094 and the contents of A is 1
The address of B is 0xbf933093 and the contents of B is s
The address of C is 0xbf933098 and the contents of C is 427698.00000
Can someone help for the large number I got when printing the contents of C? Why don't I get 3.14? (The number is actually longer than that but it didn't fit into this textbox. :-))
You are declaring ptra, ptrb and ptrc as pointers to ints. But the type of a pointer is based on what it points to, so it really should be:
int *ptra;
char *ptrb;
double *ptrc;
In your specific case, your program is trying to interpret a double value through an int pointer. Since the sizes of these data types differ on your machine, some of the bits of the double get discarded and you end up with the strange number you're seeing.
This may not always happen the same way - the result of accessing something through the wrong type of pointer is not defined by the C language, but it still might compile. C programmers refer to this as undefined behaviour (a phrase you should really come to terms with if you want to learn C!).
There is also the fact that when you call printf, you need to give it variables of the type it expects from the format string. So if you give it a format string where the first placeholder is %.f, you must give it a first argument that's a double. If you don't, printf will also exhibit undefined behaviour and could do anything (the undefined behaviour may be strange output, crashing, or simply putting out the number you expect... until the worst possible moment).
Your pointers are all of type int. That is not correct. Replace those by
int *ptra;
char *ptrb;
double *ptrc;
Because your pointers are all int*. If you want it to dereference to a double, you need it to be double*. Your compiler should have warned you about incompatible pointer assignment.
You should declare pointers using the corresponding type.
int *ptra;
char *ptrb;
double *ptrc;
you need to change your pointer type to match your data type so the size will be set accordingly.
char *ptrb;
double *ptrc;
If I can just say a few words about typed pointers.
Pointers with a type (as opposed to void* pointers) know how many bytes to advance in memory. For example on 32 bit systems and integer pointer would typically advance four bytes in memory when iterating through an array containing integer values.
A char pointer (guaranteed by the C standard to be always 1 byte) would naturally advance 1 byte at a time.
Let me illustrate this with a small code snippet:
#include <stdio.h>
int main()
{
char array [] = "This is a char array.";
int* int_ptr;
char* char_ptr;
char_ptr = array; /* This is okay, we have a char array and we assign its address to a char pointer */
int_ptr = array; /* It will complain but let's go along with it */
printf("%p, %p, %p\n", array, char_ptr, int_ptr); /* They should all point to the same address in memory */
printf("%p\n", ++char_ptr); /* it will have advanced by one byte */
printf("%p\n", ++int_ptr); /* it will have advance by four bytes */
return 0;
}
I have the following output on my machine:
$ ./a.out
0xbf8b85d2, 0xbf8b85d2, 0xbf8b85d2
0xbf8b85d3
0xbf8b85d6
As you can see they have indeed advanced as we predicted. It is fairly obvious this can cause all sorts of problems when we start dereferencing our pointers and they don't match the underlying type.
Regarding void* pointers, arithmetic on them is illegal.
here the pointer ptrc is referring to the addressof varaible whose data type is integer but you are using it for double.

passing a character to char *

i have this piece of code
int main()
{
char a[100];
a[0]='a';
a[1]='b';
fun(a[0]);
}
void fun(char *a)
{
printf("%c",a);
}
but im passing a character to a pointer.will the pointer not be expecting an address???
a[0] holds the value 97 ('a' in ASCII). fun will receive the value 97 in a but interpret it as an address. However, since you're only passing it to printf, and happen to incorrectly be using the %c formatter which will interpret a as a char, you'll end up printing a anyway.
Of course, on most compilers you should receive warnings that:
You are converting an integer into a pointer (when you call fun) without casting it to a pointer.
The %c formatter in printf should take a char, not a char *.
You're calling a function which has no prototype at the point of the call. C89 says this is allowed, but it's your problem to ensure that the function is called with arguments that are correct for the parameters it is eventually defined with. Since they don't match in this case, undefined behavior.
Turn on more compiler warnings (-Wall in the case of gcc).
You're not passing a character to a pointer.
fun() is expecting a pointer to a character, but you're passing a character. This should generate a syntax error. Or, at the very least, not work right if you really haven't declared the function fun().
Characters are integer types that are copied on assignment.
Solution 1: Don't use a pointer.
void fun(char a);
Solution 2: If you need to use a pointer for some reason not obvious here, use & to get a pointer and * to dereference the pointer.
void fun(char *a);
int main()
{
char a[100];
a[0]='a';
a[1]='b';
fun(&a[0]);
}
void fun(char *a)
{
printf("%c",*a);
}

Resources