-3

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;
}
  • 2
    The C++ tag! It VANISHED! – user4581301 Jan 05 '17 at 00:53
  • 1
    `printf()` of a non-NULL-terminated string is never going to do anything useful. – Ken Y-N Jan 05 '17 at 00:54
  • Arrays are a bit inconsistent in the language: `evil` and `&evil` mean the same thing--this is only true of arrays. Also, you're getting lucky that `printf` works without having explicit 0-terminators in your character array. – Lee Daniel Crocker Jan 05 '17 at 00:54
  • Regarding `printf(&evil);` `evil` is an array and [arrays decay to pointers.](http://stackoverflow.com/questions/1461432/what-is-array-decaying) The `&` is detrimental to what you want. – user4581301 Jan 05 '17 at 00:55
  • @KenY-N My apologies! I forgot to declare `ptr` variable in my example. – user7376830 Jan 05 '17 at 00:56
  • 3
    @LeeDanielCrocker: No, `evil` and `&evil` do *not* mean the same thing. `evil` is the name of an array object; it's implicitly converted to the address of its initial member in most, but not all, contexts. `&evil` is the address of the entire array, not of its initial element; it's of type `char (*)[4]`, not `char*`. Suggested reading: Section 6 of the [comp.lang.c FAQ](http://www.c-faq.com/). – Keith Thompson Jan 05 '17 at 00:59
  • Yes, I understand, but I think that's a language design flaw. – Lee Daniel Crocker Jan 05 '17 at 01:17
  • 1
    @LeeDanielCrocker: thinking it is a language design flaw really isn't going to help anyone — the language is as it is, and that is incredibly unlikely to change, and it is not clear what the meaning (of `&evil`, presumably) should be changed to even if it was changed. – Jonathan Leffler Jan 05 '17 at 01:33

4 Answers4

3

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.
Community
  • 1
  • 1
A.S.H
  • 29,101
  • 5
  • 23
  • 50
  • 1
    `printf("%p", (void*)ptr);` – Keith Thompson Jan 05 '17 at 01:01
  • 1
    Your use of the phrase "perfect code" suggests that it's OK to omit the cast. In my opinion, it isn't. You should *always* cast a pointer value to `void*` when using it with `%p` (unless it's already of that type); there's no reason not to. – Keith Thompson Jan 05 '17 at 01:06
  • 1
    @KeithThompson I know that the standard says that %p should be used with a void pointer. Still there is no reason at all why a C compiler wouldn't be able to silently convert any pointer-to-object to a void pointer when passed to printf through a va_list, when in all other cases, it is perfectly able and required to do so (according to 6.3.2.3/1). If the cast was required, then that implies that pointers take some mysterious and magical format when passed to printf, but only then. That doesn't make any sense. – Lundin Jan 05 '17 at 07:48
  • 1
    @Lundin: The compiler doesn't know the expected type for an argument to a variadic function. Some default promotions are done, but they don't apply to pointers -- and there's no guarantee that all pointers have the same representation. There is no conversion, just a reinterpretation, and even that's not guaranteed. Without the cast, it will probably work. With the cast, it *will* work. – Keith Thompson Jan 05 '17 at 08:35
  • @KeithThompson My point is: has there ever existed a system where void pointers would not have the same binary format as pointer to object? I think not, because then it would have been very problematic to handle the requirement that any object pointer must be convertible to/from a void pointer. If no such system have ever existed, and is not going to ever exist, there is no need for us to design our code for compatibility with such fictional, non-existing systems. – Lundin Jan 05 '17 at 08:55
  • @Lundin: I've worked on systems where `void*` and `char*` pointers store a byte offset in the high-order 3 bits. That wouldn't cause problems with argument passing, but systems where `void*` is bigger than `int*` are legal and plausible, and I believe they've existed, though I don't have an example. That wouldn't cause a problem for implicit `void*` conversion, since the compiler knows when it needs to generate code to do the non-trivial conversion. If you're satisfied being 99% safe, that's up to you. I'll take 100%. – Keith Thompson Jan 05 '17 at 09:01
3

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
Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
0

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;
}
pm100
  • 48,078
  • 23
  • 82
  • 145
  • 1
    `%p` requires an argument of type `void*`. If you want to print a pointer value of a different type, cast it: `printf("%p\n", (void*)*evil);` – Keith Thompson Jan 05 '17 at 01:00
0

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.

R Sahu
  • 204,454
  • 14
  • 159
  • 270