-4

So I've been experimenting with pointers for a while and a few questions have come up. In the code below, first of all, when I print the memory addresses for each of:
pHours++ - pHours - pHours + sizeof(char), I get the following results: 1989086632 - 1989086636 - 1989086636.
Shouldn't the value of pHours++ be by 4 units (Btw, doesn't C normally allocate 1 byte instead of 4 for storing a char?) larger, instead of smaller, than pHours's value?
Moreover, why is pHours + sizeof(char)'s value the same as pHours's? In the code I've written below, it becomes clear that the addition of sizeof(char) does change the address at which the pointer points, since when I omit it in the function's last printing statement, I get a segmentation fault (*nHours holds no value).
Also, why do I get a segmentation fault when I write nHours++, but not when I do the same for skata (skata++) or pHours (pHours++)?
Oh, two more things. Visual Studio's compiler gives me an error for not initializing *nHours and *skata before using them, but using an online compiler I'm allowed to do so. Why is this a mandatory practice in VS?
Also, why is it preferrable to use the format string %p instead of %d for referring to memory addresses?
That's all for now. Thank you in advance! :)

#include <stdio.h>

#define MinutesPerHour 60

void ConvertTimeToHM(int time, int *pHours, int *pMinutes);

int main()
{
    int time, hours, minutes;
    printf("Enter a time duration in minutes: ");
    scanf("%d", &time);
    ConvertTimeToHM(time, &hours, &minutes);
    printf("HH:MM format: %d:%02d\n", hours, minutes);
    return 0;
}

void ConvertTimeToHM(int time, int *pHours, int *pMinutes)
{
    *pHours = time / MinutesPerHour;
    int *nHours, *skata, *kHours;
    *pMinutes = time % MinutesPerHour;
    //nHours = pHours;
    //nHours++;
    skata++;
    printf("%d %d %d\n", pHours++, pHours, pHours + sizeof(char));
    skata = nHours + 3 * sizeof(char);
    *skata = 19;
    printf("%d\n", *(nHours + 3 * sizeof(char)));
}
Yunnosch
  • 26,130
  • 9
  • 42
  • 54
Leet
  • 83
  • 8
  • 1
    These days, the words "experimenting with" and "pointers" in the same sentence always give me a chill... – Steve Summit Aug 11 '18 at 13:49
  • Break up that printf: `printf("%d ", pHours++); printf("%d ", pHours); printf("%d\n", pHours + sizeof(char));`. Now it's well-defined, and your results will make sense. – Steve Summit Aug 11 '18 at 13:50
  • You should probably break the question up into smaller discrete questions and then research each smaller part. Your first question has been asked and answered many times before. For example, [Pointer addition and element size](https://stackoverflow.com/q/4772932/608639). – jww Aug 11 '18 at 13:51
  • `skata` isn't initialized to point anywhere, so its results are just about guaranteed not to make sense. Same for `nHours` and `kHours`. But you don't need those extra pointers anyway. – Steve Summit Aug 11 '18 at 13:52
  • Bottom line, as always: When using pointers, always know where they point, always initialize them to point somewhere well-defined. Uninitialized pointers don't work. – Steve Summit Aug 11 '18 at 13:54
  • It's preferable to use `%p` to print pointers because `%p` works, while `%d` doesn't. Simple as that. :-) (On many platforms these days, pointers are bigger than ints, so `%d` will only print part of your pointer, and the rest of the printf call tends to get screwed up, too.) – Steve Summit Aug 11 '18 at 13:55
  • Possible duplicate of [Why are these constructs (using ++) undefined behavior in C?](https://stackoverflow.com/questions/949433/why-are-these-constructs-using-undefined-behavior-in-c) – Osiris Aug 11 '18 at 13:56
  • Please what is the idea behind this: "*`skata = nHours + 3 * sizeof(char);`*"? – alk Aug 11 '18 at 14:08
  • 1
    Properly initializing variables is *always* a "mandatory practice". As you've discovered, though, different compilers have different degrees of fussiness when it comes to explicitly reminding you to follow the rules. – Steve Summit Aug 11 '18 at 14:12
  • "*print the memory addresses for each of: pHours++ - pHours - pHours + sizeof(char), I get the following results: 1989086632 - 1989086636 - 1989086636. Shouldn't the value of pHours++ be*" the order in which arguments to a function call are evaluated is not defined. It seems that the 4th is evaluated 1st then the 2nd and finally the 3rd argument. At least move printing `pHours++` to a separate call. – alk Aug 11 '18 at 14:14
  • Using variables before initialising them invokes the infamous Undefined Behaviour. Anything can happen form then on. – alk Aug 11 '18 at 14:17
  • @SteveSummit it's much safer to experiment with pointers these days than it was back before memory protection was common, when a data-write via an errant pointer could hose your filesystem driver (and thus your filesystem, destroying your source code along with it) :) – Jeremy Friesner Aug 11 '18 at 22:34
  • 1
    @JeremyFriesner Well, yes, although there's some irony there. The machine and OS where C was invented (and where, in fact, I first learned it) had full memory protection. It was a significantly later generation of "personal" computers that thought they could get away without an MMU. That choice (among others) may have helped advance the computing industry in terms of popularity, but handicapped it badly in terms of robustness and security. (In some ways we've never recovered.) – Steve Summit Aug 12 '18 at 15:04

2 Answers2

0

Moreover, why is pHours + sizeof(char)'s value the same as pHours's?

Because you invoked undefined behavior in the printf statement by trying to update pHours and use its value in a computation without an intervening sequence point (also for using the wrong conversion specifier). Function arguments are not guaranteed to be evaluated left to right, and the result of ++ is not guaranteed to be evaluated immediately.

Break that up into multiple statements and you should see a different result.

Visual Studio's compiler gives me an error for not initializing *nHours and *skata before using them, but using an online compiler I'm allowed to do so. Why is this a mandatory practice in VS?

Compilers are required to issue diagnostics for syntax errors and constraint violations - beyond that, it’s up to the individual vendor.

Also, why is it preferrable to use the format string %p instead of %d for referring to memory addresses?

For the same reason we use %f for doubles and %s for strings and %c for individual characters - because the language specification says so. Pointers are not integers; though they may have an integral representation on many systems, that’s not guaranteed.

John Bode
  • 119,563
  • 19
  • 122
  • 198
0

Shouldn't the value of pHours++ be by 4 units

I'm not sure which book you're reading, but whatever it is, it's not working for you...

Given int *p;, p+0 is equivalent to &p[0], p+1 is equivalent to &p[1], and so on. (p + 1) - (p + 0) must be 1, logically, but that 1 does not denote a char; it denotes an int, since the that's the type you're pointing at.

On the other hand, if you were to cast pHours to char * (or void *) before and after pHours, you might see something more along the lines of what you expect... but it might also be 1, if CHAR_BIT is 32 for example.

(Btw, doesn't C normally allocate 1 byte instead of 4 for storing a char?)

Yes, in C, a char always occupies exactly one byte, thus sizeof (char) is always 1... however the bytes needn't be octets; you can obtain the number of bits in a byte using CHAR_BIT, which is required (by the C standard) to be at least 8, and has been values such as 16 and 32 for some systems, so...

larger, instead of smaller, than pHours's value?

The value of p++ is p before incrementation. The value of ++p is p after incrementation.

Moreover, why is pHours + sizeof(char)'s value the same as pHours's?

In the following code you invoke undefined behaviour, so you shouldn't read too much into it.

printf("%d %d %d\n", pHours++, pHours, pHours + sizeof(char));

You need to be aware that the order of evaluation of arguments is unspecified, and so pHours++ followed by pHours in the next argument is undefined behaviour (i.e. could cause unpredictable results including crashes, security compromise, etc)... also by telling printf to print an int value and then passing an int * value: that's also UB. You probably meant to write: printf("%p", (void *) pHours++); printf("%p %p\n", pHours, pHours + sizeof (char));.

Also, why do I get a segmentation fault when I write nHours++, but not when I do the same for skata (skata++) or pHours (pHours++)?

Undefined behaviour doesn't mean will produce a segmentation fault; that would be a very much defined behaviour. Undefined behaviour means... okay let's start with undefined... lacking in definition... right? That means it has no meaning. What I'm trying to say here is that the behaviour of your code has no meaning. It's allowed to segfault, but not really required to.

This explains buffer overflows, which don't necessarily cause segfaults; they might go unnoticed for many years as the product functions routinely and nobody probes it, then a hacker notices a segfault that looks suspiciously buffer overflowish... and manages to penetrate your codebase... No, a segfault is certainly not required here... but neither is any logical behaviour.

Where are you getting your expectations from? I fear that you might not actually be reading, but guessing... Please tell me you're not guessing... It's not safe to learn C that way! Trust me; I've been there and done that, and you've picked up a few of the same misunderstandings I had. If I'm correct, and you're not reading a book, consider going out and finding yourself a copy of K&R2E. Remember to do the exercises.


Visual Studio's compiler gives me an error for not initializing *nHours and *skata before using them, but using an online compiler I'm allowed to do so. Why is this a mandatory practice in VS?

Visual Studio is detecting the undefined behaviour which is use of an uninitialised value... and at that point, there are no requirements upon Visual Studio to follow the C standard, as you've stepped outside of the realms of C, so it's allowed to cease compilation at that point.


Also, why is it preferrable to use the format string %p instead of %d for referring to memory addresses?

%p tells printff to expect a void * argument, which is important because printff needs to know that it's operating upon a void * argument... %d tells printff to expect an int argument... The two types have different usecases. Don't mix them. It's undefined behaviour if you pass the wrong type of argument.

autistic
  • 1
  • 3
  • 35
  • 80
  • "... so it's allowed to cease compilation at that point." It would also be perfectly valid to produce a program which moos at you through your sound card, for example... Undefined behaviour is behaviour that is undefined. – autistic Aug 11 '18 at 14:45