String [x] vs * string ++

Which of the two methods is theoretically faster and why? (The pointer to the string must be constant.)

What is the difference between the destination [count] and * destination ++? Does destination [count] support a number from 0 to each call? Is * destination ++ just adding 1 for each call?

char *const string = "Hello world!"; char *destination = malloc(strlen(string) + 1); int count = 0; while(string[count] != '\0') { destination[count] = string[count]; count++; } char *const string = "Hello world!"; char *destination = malloc(strlen(string) + 1); char *ptr = string; while(*ptr != '\0') { *destination++ = *ptr++; } 
+7
c
source share
8 answers

Why should we speculate? We can try and find out. I compiled the code with gcc -O3 -g (on x86) and parsed the result. There were more changes than I expected, so I will focus on the bit in the middle, where we expect that most of the differences between them will be. The core of the loop in the first case:

 0x00000030 <foo+48>: mov %dl,(%edi,%esi,1) 0x00000033 <foo+51>: movzbl 0x1(%ecx),%edx 0x00000037 <foo+55>: inc %eax 0x00000038 <foo+56>: inc %ecx 0x00000039 <foo+57>: mov %eax,%esi 0x0000003b <foo+59>: test %dl,%dl 0x0000003d <foo+61>: jne 0x30 <foo+48> 

The core of the loop in the second case:

 0x00000080 <foo2+48>: mov %dl,(%eax) 0x00000082 <foo2+50>: movzbl 0x1(%ecx),%edx 0x00000086 <foo2+54>: inc %eax 0x00000087 <foo2+55>: inc %ecx 0x00000088 <foo2+56>: test %dl,%dl 0x0000008a <foo2+58>: jne 0x80 <foo2+48> 

On this basis, the second, perhaps a little faster. But in reality it will not be of much importance in practice. In L1 cache, both loops are just perfect, and the target memory is not available, so the differences will be debatable. Good luck, ever actually measuring the difference between the two.

+5
source share

It depends on the compiler. With modern compilers, it is really impossible to predict anything about optimization at the level of a single instruction without looking at the generated code. Both are probably compiled with equivalent instructions.

However, destination[count] does not loop from 0 to count for each call. It takes the location of destination[0] , adds count to the size of *destination and looks there. (Of course, this naively says - the compiler probably does something faster.)

On the other hand, *destination++ takes the destination[0] cell (which is no longer the beginning of the array since you changed destination ), it looks and adds a size of the type *destination , so destination[0] refers to what used to be destination[1] .

+6
source share

In the good old days, the second method is faster. But with modern compiler optimization, they are the same .

This is because:

 destination[count] = string[count]; 

will do

 *(destination+count) = *(string+count); 

In each loop, two more add operations are performed. A modern compiler will just do it for you.

+5
source share

CPU dependent. On x86, the first version is slightly faster (or at least this will result in a slightly shorter code), since only one command is required to load from the [count] line and one instruction to write to the [count] destination. Thus, the pseudo-code will look something like this (each line = one assembly instruction)

 register = *(string+count) *(destination+count) = register count += 1 compare register, 0 jump to line 1 if nonzero 

and the second version will be

 register = *ptr *destination = register ptr += 1 destination += 1 compare register, 0 jump to line 1 if nonzero 

In practice, any optimizing compiler optimizes the output from this code and, depending on its complexity, the compiler may even be able to turn the first version into the second or vice versa. Thus, it is not reported which one will be faster.

+2
source share

*destination++ actually adds sizeof(*destination) to each call (depending on the size of the pointer), and destination[count] should do *(destination + count * sizeof(*destination)) , but is probably optimized by the compiler.

0
source share

destination[count] is the count-th element (based on 0) in destination[] . count++ guarantees that at each moment of entering the loop, destination[count] passes by the last element available at the previous iteration (except, of course, the first entry).

*destination++ = *ptr++; has the following form:

 *destination = *ptr; destination++; ptr++; 

Note that destination++ increases the value of the pointer to sizeof(*destination) , which will point to the next element in the array, and not to the data indicated by the destination (or *destination ) symbol.

For modern compilers, both will be optimized for almost the same binary, so it doesn't matter which one you choose with respect to speed.

0
source share

If "string" appears as an argument in a function, you can use this as an iterator instead of "ptr". Then you will have one smaller variable that will be held on the stack. This may mean that the compiler can store all local variables in registers, which will improve performance.

This, of course, depends, if the compiler succeeds in "counting", you will not get any performance increments.

0
source share

Looking at it from a high level, both operations are related to dereferencing and adding. The expression destination[count] evaluates to *(destination + count) . The expression *destination++ (most likely) evaluates to *destination; destination += 1 *destination; destination += 1 (taking into account that the side effect should be applied only to the next point in the sequence, which is not necessary immediately after evaluating the expression).

Thus, theoretically there should not be such a big difference between the two versions. The only way to find out your specific platform is to code both versions and compare their performance.

0
source share

All Articles