-1

I know there's tons of these and it's probably a simple question, but I can't seem to figure it out :(

char * char_buffer = (char *) malloc(64);
printf("%x\n", char_buffer);
memset(char_buffer,
       0,
       64);

printf("%x\n", char_buffer);

Output :

50000910
50000910

Why isn't char_buffer zero'd? Can someone explain what's happening?

Spencer Woo
  • 21
  • 1
  • 6

4 Answers4

2

You are confused about the difference between a pointer and space being pointed to.

Your code doesn't zero the pointer. It zeroes the space being pointed to. The memset function behaves that way. In fact you would not want to zero the pointer, as then it would no longer point to the memory you allocated.

Your printf statement attempts to print the pointer's value, which is the address of the space being pointed to. Not the contents of the space being pointed to.

Actually the printf statement causes undefined behaviour, because you mismatched format specifiers. Your compiler should have warned about this.

Here is some correct code:

printf("The buffer's address is %p\n", char_buffer);
printf("The buffer contains: %02X %02X %02X ...\n", 
    (unsigned char)char_buffer[0],
    (unsigned char)char_buffer[1],
    (unsigned char)char_buffer[2]);

To use the %X specifier you must pass non-negative values in, which is why the cast is necessary. You could declare the buffer as unsigned char * instead, in which case the cast would not be necessary.

M.M
  • 138,810
  • 21
  • 208
  • 365
  • I think you should use %d with unsigned char, http://stackoverflow.com/questions/27547377/format-specifier-for-unsigned-char – Giorgi Moniava Oct 26 '15 at 21:06
  • @Giorgi sure, if you want to print in base 10... see the answer by "mafso" explaining that it is safe to use `%d`, `%u`, or `%x`. The C standard is unclear in defining the behaviour of these specifiers. – M.M Oct 26 '15 at 21:08
  • about the `%x` ... well, a pointer *is* convertible to an `int`, so I'd say it's not *ub* but *ib* (implementation-defined behavior). All that could happen is you get an integer overflow.... –  Oct 26 '15 at 21:12
  • 1
    @FelixPalmen you have to actually perform a conversion yourself, e.g. `printf("%x\n", (unsigned int)p);`. The printf function doesn't convert the arguments to match the specifiers. Imagine you are the implementor of printf, how do you know whether an argument of the correct type was given? – M.M Oct 26 '15 at 21:20
  • @M.M [I didn't implement `%p` so far](https://github.com/Zirias/clang-libdos/blob/master/src/stdio/stdio_printf.c#L92). But I see your point, if the pointer *is* larger than `int`, `printf()` will probably mess up the `va_list`. –  Oct 26 '15 at 21:26
  • 1
    @FelixPalmen on x64 it's common for pointers to be passed in separate registers to integers even if the same size – M.M Oct 26 '15 at 21:26
  • @M.M still `%p` isn't always satisfying as the output format isn't specified. Converting the pointer to `long` and printing that is a fairly safe workaround. –  Oct 26 '15 at 21:29
1

You're printing the address of the buffer, not its contents. The contents are zero after the memset(). Just try:

printf("%d", char_buffer[0]);

and see the zero ;)

  • You must cast the argument of `%hhu` to `(unsigned char)` – M.M Oct 26 '15 at 21:02
  • not strictly necessary, but would be cleaner. It really doesn't matter for printing zeros. And of course, you could avoid all of that using `%d`, after all, it's passed as an `int` anyways.... –  Oct 26 '15 at 21:09
  • 1
    It is necessary because the `u` specifier expects an unsigned value but `char_buffer[0]` may be a negative value – M.M Oct 26 '15 at 21:10
  • @M.M no, not strictly. Aliasing is allowed (for several reasons, the simplest being it is a `char`). Representation of integers is guaranteed to have bits represent powers of two and to have some *sign bit* set for negative values. There's no padding bits in `char`. So a zero *is* a zero, no matter how you interpret it (except: negative zeroes, but that's not what `memset()` would write). –  Oct 26 '15 at 21:19
  • The printf function doesn't do aliasing, this is just undefined behaviour – M.M Oct 26 '15 at 21:22
  • no it doesn't, but it will fetch the size of an `int` from the parameters list and interpret the least significant `char` as `unsigned`. This isn't undefined. –  Oct 26 '15 at 21:27
  • Actually it is undefined. There's no reinterpretation. The standard says "If any argument is not the correct type for the corresponding conversion specification, the behavior is undefined." – M.M Oct 26 '15 at 21:30
  • @M.M it's not, at least not for zero and any positive value. See e.g. 6.2.5p9 –  Oct 26 '15 at 21:37
  • I agree that it's OK for non-negative values but it is undefined for negative values, which `char` may hold – M.M Oct 26 '15 at 21:37
  • @M.M that's correct. But here I *know* it's all zeroes :) Well, I should probably just change this to `%d` -- the conversion to `int` is "free" with a variadic function. See, I hate cast-riddled code ;) (edit: done.) –  Oct 26 '15 at 21:39
  • @M.M I _think_ there is an exception to the " "If any argument is not the correct ..." such that `%x` and `int` is OK. But its buried deep in the C standard. I'll look later. – chux - Reinstate Monica Oct 26 '15 at 21:52
  • @chux I think the standard is badly specified. On a strict reading there is no exception, but we usually assume they meant that there is an exception otherwise it's lame. – M.M Oct 26 '15 at 21:58
  • @M.M It is in §6.5.2.2 6 Function calls: which in part "...one promoted type is a signed integer type, the other promoted type is the corresponding unsigned integer type, and the value is representable in both types ..". These are the weasel words that allow a positive `int` to be correctly printf with `"%x"` and `unsigned` with `"%d"` if the value <= `INT_MAX`. – chux - Reinstate Monica Oct 26 '15 at 22:07
  • Some remark: The C standard always tried and still tries to describe a language as abstract and generic as possible that evolved from being just something like "assembler brought to a higher level". The technical and the abstract view might conflict sometimes. For portable code, you need the latter, but it's not easy at all ... –  Oct 26 '15 at 22:19
  • @chux Your quoted text is describing matching of arguments to parameters (not `...`). For example `int f(x) int x; {}` can be called with `f(0u);` – M.M Oct 26 '15 at 22:49
0

You're printing out the pointer, not the buffer values.

memset(p,v,n) writes the byte v to the byte pointed at by p and n-1 bytes after it. It doesn't touch the pointer p at all (it's passed in by value, how can it?)

Russ Schultz
  • 2,545
  • 20
  • 22
0

To print out the buffer, you'd have to loop through it. I use something like this:

void printbuf(char *buf, int size)
{
    int i = 0;
    char b = 0;
    for (i = 0; i < size; i++) {
        if (i % 8 == 0) {
            printf("\n%3d: ", i);
        }
        b = buf[i];
        printf("%2hhX ", b);
    }
}
bruceg
  • 2,433
  • 1
  • 22
  • 29