So I am learning C and to get practice I do some "LeetCode" challenges. For a palindrome checker I have tried two approaches, one using indices and one using pointers.
- Where does the large performance difference come from?
And as a follow up question:
- What method is usually preferred and for what reasons?
For reference here are the code blocks.
Pointers:
bool isPalindrome(char *s) {
size_t length = strlen(s);
char *left = s;
char *right = s + length - 1;
while (left < right) {
while (left < right && !isalnum(*left))
++left;
if (left == right)
return true;
while (right > left && !isalnum(*right))
--right;
if (right == left)
return true;
if (tolower(*left) != tolower(*right))
return false;
++left;
--right;
}
return true;
}
Indices:
bool isPalindrome(char *s) {
size_t length = strlen(s);
size_t left = 0;
size_t right = length - 1;
while (left < right) {
while (left < right && !isalnum(s[left]))
++left;
if (s[left] == '\0')
return true;
while (right > left && !isalnum(s[right]))
--right;
if (right == 0)
return true;
if (tolower(s[left]) != tolower(s[right]))
return false;
++left;
--right;
}
return true;
}


There are many potential reasons for a performance difference, but it would be helpful to understand how you compile the code, how you measure performance and with what input. Here are a few hints:
the 2 functions posted do not implement the same semantics: in the pointer version, you exit the loop
if (left == right)whereas the equivalent test in the index version isif (s[left] == '\0')which is not true in most cases whereif (left == right)is true. If the functions behave differently, no surprise they perform differently.did you enable optimisations? without optimisations,
s[left]may generate more code than*left, but with optimisations enabled, most compilers will generate code with similar performance, either converting the index approach to a pointer approach, or using addressing modes that use multiple registers, with or without scaling, without a penalty.Both methods are equivalent if implemented properly, and compile to equivalent performance with modern compilers.
Here are reasons to use pointers:
Reasons to use index variables with type
size_t:Before you focus on performance, focus on correctness!
isalnum()andtolower()from<ctype.h>are only defined for a restricted range ofintvalues: all values of typeunsigned charand the special negative valueEOFexpands to. Passing negativecharvalues has undefined behavior on platforms where typecharis signed by default. You should use a cast to(unsigned char).Here are modified versions: