The code:
char *s = "hello ppl.";
gives you a pointer to what it almost certainly read-only memory. That's because string constants in C are const char *
. When you try to write to that memory, you'll most likely get a segmentation violation. Try this instead:
char s[] = "hello ppl.";
which is conceptually the same as:
char s[11]; // for the whole string plus null terminator
strcpy (s, "hello ppl.");
In other words, it puts the string you want to change into writable memory. The following code:
#include <stdio.h>
#include <string.h>
int main(void) {
int i;
char s[] = "hello ppl.";
for (i = 0; i < strlen(s); i++) {
char c = s[i];
if (c >= 97 && c <= 122) {
c += 2;
s[i] = c;
}
}
printf("%s\n",s);
return 0;
}
gives you "jgnnq rrn."
.
A few other things I'd like to point out which are not fatal:
- It's not usually a good idea to use 'magic' numbers like
97
and 122
. It's just as easy, and clearer in intent, to use 'a' and 'z'.
- If you really want to rotate, you can't blindly add 2 to 'y' and 'z'. You have to treat them specially (subtract 24) so that they map correctly to 'a' and 'b'.
- The C standard doesn't guarantee that alpha characters are contiguous. If you know you're using ASCII, you're probably okay but I thought I'd just mention that. As an aside, it does guarantee that for the numeric characters.
Having said that, I'd rather use a mapping table as follows:
#include <stdio.h>
#include <string.h>
int main (void) {
char *lkupPtr, *strPtr;
char str[] = "hello ppl.";
const char * const from = "abcdefghijklmnopqrstuvwzyz";
const char * const to = "cdefghijklmnopqrstuvwzyzab";
for (strPtr = str; *strPtr != '\0'; strPtr++)
if (lkupPtr = strchr (from, *strPtr)) != NULL)
*strPtr = to[(int)(lkupPtr - from)];
printf("%s\n",str);
return 0;
}
This takes care of all the points I raised above and you can, if necessary, add more mappings if you're in an internationalized environment (rather than just plain ASCII or EDCDIC).
This should be, in my opinion, fast enough for all but the most demanding of requirements (I clocked it at over 3 million characters per second on my PC). If you have a near-insatiable need for performance over and above that, yet don't want to opt for hand-crafted assembly targeted to your specific CPU, you could try something like the following.
It's still fully compliant with the C standard but may deliver better performance by virtue of the fact all heavy calculation work is done once at the start. It creates a table holding all possible character values, initializes it so that every character translates to itself by default, then changes the specific characters you're interested in.
That removes any checking for characters from the translation itself.
#include <stdio.h>
#include <string.h>
#include <limits.h>
static char table[CHAR_MAX + 1];
static void xlatInit (void) {
int i;
char * from = "abcdefghijklmnopqrstuvwzyz";
char * to = "cdefghijklmnopqrstuvwzyzab";
for (i = 0; i <= CHAR_MAX; i++) table[i] = i;
while (*from != '\0') table[*from++] = *to++;
}
int main (void) {
char *strPtr;
char str[] = "hello ppl.";
xlatInit(); // Do this once only, amortize the cost.
for (strPtr = str; *strPtr != '\0'; strPtr++)
*strPtr = table[*strPtr];
printf("%s\n",str);
return 0;
}