5

Here is an implementation of strCopy

void strcopy2(char *dst, char const *src){
    while ((*dst++ = *src++))
        ;
}

Our professor asked us to reproduce this code without using pointers, so I came up with the following function:

void strcopy(char dst[], char const src[]){
    size_t i = 0;
    while (dst[i] = src[i++])
        ;
}

It works well, but I realised, that under the hood the function must still be using pointers, as we nowhere return any value. In other words, I though the last function would use pass by value but this is obviously not the case. So what is happening under water, and is there actually any difference between the two methods?

nobody
  • 19,814
  • 17
  • 56
  • 77
Slugger
  • 665
  • 1
  • 5
  • 17
  • Here's some background reading on why a pointer and an array are not the same thing http://www.cplusplus.com/forum/articles/9/ – UKMonkey Oct 18 '16 at 10:30
  • 3
    This code ` while (dst[i] = src[i++]) ;` has undefined behavior: two increments of `i` without an intervening sequence point. Change it to ` while (dst[i] = src[i]) i++;`. – Pete Becker Oct 18 '16 at 11:56

3 Answers3

14

Not just under the hood… both dst and src, despite appearances, actually are pointers! The [] syntax in parameter lists is syntactic sugar (or syntactic pepper really) but it's lying to you; these are char* dst and char const* src for reals.

8.3.5/5 [dcl.fct] Functions:

After determining the type of each parameter, any parameter of type “array of T” or of function type T is adjusted to be “pointer to T”.

user703016
  • 37,307
  • 8
  • 87
  • 112
Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • that is what I thought, thanks. But is pass by value really not possible then? – Slugger Oct 18 '16 at 10:30
  • 1
    -1 Saying they are the same is oversimplified: http://stackoverflow.com/questions/11555997/c-c-int-vs-int-pointers-vs-array-notation-what-is-the-difference – Trevir Oct 18 '16 at 10:30
  • 9
    @Trevir They are the same in this very context. – user703016 Oct 18 '16 at 10:30
  • @Slugger: Indeed. Arrays can't be passed by value unless you encapsulate them in a `std::array`. You could pass by ref with a template. – Lightness Races in Orbit Oct 18 '16 at 10:30
  • 4
    @Trevir: It's not. They are literally the same. I'm not saying arrays are pointers (they're not); but in this parameter list they are the same. The Q&A you link to is something different. – Lightness Races in Orbit Oct 18 '16 at 10:30
  • 4
    @UKMonkey: You are mistaken. – Lightness Races in Orbit Oct 18 '16 at 10:33
  • 6
    @UKMonkey Please see the comment by @Lightness. The claim is not that "arrays are pointers", because they are not. The claim is "`dst` and `src` are pointers", which they are. I have added a standard quote. – user703016 Oct 18 '16 at 10:33
  • @PatrickM'Bongo: Just compile and look at the assembly. They ARE different, pointer implementation takes more time and instructions ... The function syntax may be the same. But what happens INSIDE the function is different! – Trevir Oct 18 '16 at 10:34
  • 5
    No, @Trevir. That is incorrect. The OP's two functions are (marginally) different, but not for any reason to do with their parameters. – Lightness Races in Orbit Oct 18 '16 at 10:36
  • 5
    For the avoidance of doubt, everybody, this is a C++ quirk (well, a C quirk actually), that if you write `void foo(int array[])` this is _literally_ translated to be _identical_ to `void foo(int* array)` in every way. Yes, it is silly and confusing. Thanks to Patrick for adding the relevant standardese to this answer. – Lightness Races in Orbit Oct 18 '16 at 10:39
  • So this: https://godbolt.org/g/zNRxxP and this: https://godbolt.org/g/0jHNCM is the same?! – Trevir Oct 18 '16 at 10:39
  • @Trevir: One more time: _"The OP's two functions are (marginally) different, but not for any reason to do with their parameters."_ – Lightness Races in Orbit Oct 18 '16 at 10:40
  • He asked, wheather, "is there actually any difference between the two methods". And there is a difference. But nvm ... – Trevir Oct 18 '16 at 10:41
  • 1
    @Trevir: The relevant point is that the professor wanted the OP to avoid using pointers; the important takeaway here is that the OP failed to do so. :) – Lightness Races in Orbit Oct 18 '16 at 10:41
  • 2
    @Trevir the implementations are obviously different, because... they are different (i.e. the specific expressions have slightly different side effects inside the function. That doesn't change the fact that in function signatures, `T[]` and `T*` are literally the same type. – Griwes Oct 18 '16 at 10:42
  • @LightnessRacesInOrbit: We agree on that. But I still think it is not possible to say both methods are the same, if they compile differently. – Trevir Oct 18 '16 at 10:42
  • 5
    @Trevir: I never claimed that both [functions] were the same. I've actually said the opposite twice now. – Lightness Races in Orbit Oct 18 '16 at 10:43
  • @PatrickM'Bongo ahh - my misreading; good point - although I'd still tread carefully when saying they're the same because it's only true in the case of function arguments. – UKMonkey Oct 18 '16 at 10:53
  • 1
    @Trevir "Saying they are the same is oversimplified" Saying *what* are the same? Also, the post you like is largely irrelevant. – juanchopanza Oct 18 '16 at 10:54
  • 1
    @Trevir IMHO you're comparing different things, taking your links and changing only the signature, not the implementation the assembly is the same. There you go https://godbolt.org/g/6zqUru and https://godbolt.org/g/r08vNK – CGR Oct 18 '16 at 12:34
  • OP here: Thanks for all the comments I could just now read them. It is become abundantly clear that my implementation is actually using pointers. But I am becoming increasingly convinced that because pass by value is bad practice, it is nigh on impossible. The reason I want to use pass by value is that (I believe) references wont work. – Slugger Oct 18 '16 at 16:42
  • @Slugger: _"I believe references wont work"_ Yeah to be fair that's quite likely for a function like this (basically cos it'd require compile-time bounds). For what it's worth, this is more or less the conventional way of doing it. It's difficult for us to know to the extent to which your professor wants you to "avoid pointers" (after all, at a certain level of abstraction, they are completely unavoidable) so, sadly, I'm not sure I can be of more help to you... – Lightness Races in Orbit Oct 18 '16 at 16:59
  • 2
    @Slugger: My gut instinct is that he is imprecisely describing what you have done - that is, using array index notation rather than pointer increment notation. Hopefully he won't care that you're using array index notation directly on pointers ;) (although in my experience with professors, frankly he may not even be aware!) Maybe you can get extra credit (and hedge your bets at the same time) by describing/explaining all of this below your solution. Also, you do need to correct that usage of `i` (per Pete's comment below the Q). – Lightness Races in Orbit Oct 18 '16 at 17:07
  • Thanks so much for all the helpful feedback! I Know now what must be done! – Slugger Oct 18 '16 at 18:54
3

is there actually any difference between the two methods?

Yes, the second one is just wrong:

void strcopy(char dst[], char const src[]){
    size_t i = 0;
    while (dst[i] = src[i++]) // (*)
        ;
}

The line marked (*) is undefined behavior for most of C++'s history. There is no specified ordering of the two reads of i with respect to the increment. It's possible that this will be ordered correctly on your compiler on your platform. It's possible the increment of i will happen before it's read as the index of dst, and your copy function will be off by one. In C++17, the right hand side will be evaluated first so the copy function will definitely be off by one.

You will want to make the increment its own expression:

void strcopy(char dst[], char const src[]){
    size_t i = 0;
    while (dst[i] = src[i]) {
        ++i;
    }
}
Barry
  • 286,269
  • 29
  • 621
  • 977
  • @KonradRudolph Yes, that's true, and already covered by [Lightness](http://stackoverflow.com/a/40105806/2069064). I'm just taking the other part of the question. – Barry Oct 18 '16 at 12:53
  • Thanks for the answer! Tbh, I kinda fiddled around with the `++` until the copy worked as intended. Good to note that this is indeed undefined behaviour! – Slugger Oct 18 '16 at 15:54
-1

This is the process of decaying.

for the compiler, the name of the array becomes the pointer of the first element.

do notice that arrays and pointers are not the same. it the the name, the symbol which is treated like a pointer to the first element of the array.

so in your example, char dest[] is treated by the compiler as &dest[0], or in other words, char* dest.

according the language rules, if p is a pointer, then p[x] is treated as *(p+x), this is why the generated machine code in both example is almost identical.

David Haim
  • 25,446
  • 3
  • 44
  • 78
  • 2
    Decaying is 1/2 of the story. The more important half is explained in the other answer. In fact, there isn't much decay going on in OP's code (except indirectly via e.g. `dst[i]`.) – juanchopanza Oct 18 '16 at 10:44