6

I'd like to refactor some old C code of mine, and I was curious if I can replace all ptr++ with ptr += 1 where ptris some pointer, without changing any behavior. Here's an example of what I mean, from K&R Section 5.3:

/* strlen: return length of string s*/
int strlen(char *s)
{
    int n;
    for (n = 0; *s != '\0'; s++)
        n++;
    return n;
}

When I replace the s++ with s += 1, I get the same results, but I'm wondering if this will be the case for all types. I also did a test for ints:

int size = 10;
int *int_array = (int*)calloc(size, sizeof(int));
for (int i = 0; i < size; i++)
    int_array[i] = i;

for (int i = 0; i < size; i++) {
    printf("*int_array = %d\n", i, *int_array);
    int_array++;
}

If I replace the line int_array++; with int_array += 1;, I get the same result.

After thinking about this some more, I realize there could be a problem if the value is used in an expression. Would it be safer I just moved the increment onto another line like so:

int a = 5;
int b = a++;

would become:

int a = 5;
int b = a;
a += 1;

Conclusion

What I thought could be a problem, incrementing pointers of different types, is not a problem. See @bdonlan's response for the reason why.

This doesn't mean that you can replace all x++ with x += 1 and expect the same behavior. You can, however, replace ++x with (x += 1) safely, since they are equivalent.

Mike Nichols
  • 119
  • 2
  • 10
  • 3
    Why? `a += 1` is not an improvement on `a++`. How is this refactoring? – user229044 Dec 07 '11 at 19:12
  • @CanSpice Because in a lot of cases, adding `x` to a pointer actually adds `x*sizeof(*ptr)`. – Toomai Dec 07 '11 at 19:13
  • 2
    @Toomai, in all cases, actually. – bdonlan Dec 07 '11 at 19:14
  • @meagar I use the term refactoring in a broad sense that also includes code formatting clean up, variable name clarification, and changing directory structure among other things. I believe that `a += 1` is an improvement over `a++` because it's got a tricky flavor to it. I think it just crosses the line between concise and terse. – Mike Nichols Dec 07 '11 at 20:51

3 Answers3

11

a += 1 is equivalent to ++a (C99 §6.5.3.1/2). In a line like int b = a++; this means it is not equivalent to a++; a++ would return the old value of a, while a += 1 returns the new value.

Note that if you don't use the result of a++ (ie, you have a statement containing just a++;), then they are effectively identical.

Also, note that _all pointer arithmetic is done in increments of the pointed-to type's size (§6.5.6/8). This means that:

ptr = ptr + x;

is equivalent to:

ptr = (ptr_type *)( (char *)ptr + x * sizeof(*ptr) );

This is the same whether you use +, ++, +=, or [] (p[x] is exactly equivalent to *(p + x); you can even do things like 4["Hello"] because of this).

bdonlan
  • 224,562
  • 31
  • 268
  • 324
  • Are you sure it's equivalent to casting to `char*` and not invoking UB? Also, I hate to be pedantic, but pretty sure `*(p + x)` should be `*(p + (x))` – Pubby Dec 07 '11 at 19:20
  • 2
    @Pubby, see C99 §6.2.5/20, §6.2.6.1/4, §6.3.2.3/7. C requires that arrays are contiguously allocated, and that their constituent objects have fixed size of some multiple of CHAR_BIT bits. We arrive at a character pointer to the array element through arithmetic that is equivalent to what C requires; thus `(char *)p + sizeof(*p)*x == (char *)&p[x]`. Further 6.3.2.3/7 requires that we can convert the pointer back to the object type (since it is properly aligned) and thus we arrive back at `&p[x]`. It's not directly stated, but the constraints on the implementation effectively require this, IOW. – bdonlan Dec 07 '11 at 19:29
  • Thanks for the references! Makes sense. – Pubby Dec 07 '11 at 19:32
  • Wow, very nice, thank you. I didn't think to actually look at the language standard. – Mike Nichols Dec 07 '11 at 20:53
1

++ and -- is defined in terms of arithmetic for built-in types. The behavior will be the same, other than postfix returning the old value.

Pubby
  • 51,882
  • 13
  • 139
  • 180
1

It's a good question. The answer is yes, you can do that -- no matter how you do it, incrementing a pointer adds sizeof(the type that the pointer points to) to the pointer. As other answers indicate, you do need to be careful that you don't depend on when the increment happens, i.e. a++ and ++a have different effect, but a eventually ends up with the same value.

A question is: why do you want to change all your code from a++ to a+=1? Using the post-increment operator with pointers is something that should be easily understood by any C programmer, so it's hard to understand why you'd make that change.

Caleb
  • 124,013
  • 19
  • 183
  • 272
  • I guess it's mostly a style thing. Also, Crockford said he doesn't allow it for his guys to use in JavaScript. I'm working in C, Java, and VB.NET at work, JavaScript, Ruby and others at home, so I want to be consistent across different languages. I understand the importance of writing idiomatic code, but I feel ++ and -- represent a kind of cleverness or C only-ness that I don't want in my code in any language. – Mike Nichols Dec 07 '11 at 20:17