代码:
char *s = "hello ppl.";
给你一个指向它可能只读内存的指针。这是因为 C 中的字符串常量是不可修改的。当您尝试写入该内存时,您很可能会遇到分段违规。标准的相关部分(C99 6.4.5/6 on String literals)指出:
如果这些数组的元素具有适当的值,则未指定这些数组是否不同。如果程序试图修改这样的数组,则行为未定义。
所以,虽然内存必须是只读的,但你仍然在尝试修改它来破坏规则。
试试这个:
char s[] = "hello ppl.";
在概念上与:
char s[11]; // for the whole string plus null terminator
strcpy (s, "hello ppl.");
换句话说,它把你想改变的字符串放到可写内存中。以下代码:
#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;
}
根据需要为您提供"jgnnq rrn."。
我想指出的其他几件事不是致命的:
- 使用像
97 和122 这样的“神奇”数字通常不是一个好主意。使用“a”和“z”同样简单,意图更清晰。
- 如果真的要旋转,不能一味的给'y'和'z'加2。您必须对它们进行特殊处理(减去 24),以便它们正确映射到“a”和“b”。
- C 标准不保证字母字符是连续的。如果你知道你使用的是 ASCII,你可能没问题,但我想我只是提一下。顺便说一句,它确实保证对于数字字符。
话虽如此,我宁愿使用如下映射表:
#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;
}
这解决了我上面提到的所有问题,如果您处于国际化环境中(而不仅仅是纯 ASCII 或 EDCDIC),您可以在必要时添加更多映射。
在我看来,这应该足够快,除了最苛刻的要求(我在我的 PC 上以每秒超过 300 万个字符的速度运行)。如果您对性能的需求几乎无法满足,但又不想选择针对您的特定 CPU 的手工组装,您可以尝试类似以下的方法。 p>
它仍然完全符合 C 标准,但可以提供更好的性能,因为所有繁重的计算工作都是在开始时完成的。它创建一个包含所有可能字符值的表,对其进行初始化,以便每个字符默认转换为自身,然后更改您感兴趣的特定字符。
这会从翻译本身中删除任何字符检查。
#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;
}