0

セグを取得しています。char 型から 32 を減算しようとすると障害が発生します (C で tolower() を使用せずに小文字に変換しようとしています。関連する Q/A スレッドの前提条件の検索を実行しましたが、うまくいきませんでした。「a」-「A」も試しました)。変換値 '32' の場合、(char*) としてキャストし、他に考えられるものは何でも使用できます。

char* s1 = "Bob";

if (*s1 >= 97 && *s1 <= 122)
     *s1 -= 32;
}

何かアドバイス?

編集:

以下のヘルプに従っても、まだエラーが発生します。(この例では、名前の最初の文字を小文字に変更しようとしているだけです。) これが私が試みていることです:

 char* s1 = "Bob";
 printf("\n %s before", s1);

 // below I call my string length function to get actual size instead of 100
 char* temp = malloc(100);   
 temp = s1;

 if (*temp >= 'A' && *temp <= 'Z'){
    *temp -= 32;
 }

 printf("\n%s after", temp);
 free(temp);

また、すでにメモリ内にある文字列にメモリを割り当てる必要があるのはなぜですか?

4

6 に答える 6

7

そのようなリテラル文字列を変更することはできません-それらは(通常)読み取り専用メモリにあります。文字列リテラルの書き込み可能なコピーを作成する必要があります。

char* name = "Bob";
char* s1 = strdup(name);

...

free(s1);  // And you also need this to avoid a memory leak!
于 2013-11-11T16:15:39.700 に答える
5

コードには多くの問題があります。

char* s1 = "Bob";

char文字列リテラルは;の読み取り専用配列を作成します。この配列は静的であり、プログラムの存続期間全体にわたって存在することを意味します。歴史的な理由から、これはconstではないため、変更しようとしてもコンパイラは必ずしも警告を表示しませんが、慎重に変更を避ける必要があります。

s1その配列の最初の文字を指します。を変更することはできません*s1const安全のために、ポインタを次のように宣言する必要があります。

const char *s1 = "Bob";

変更可能な文字配列が必要な場合は、次のように作成できます。

char s1[] = "Bob";

残りのコードを見てみましょう。

if (*s1 >= 97 && *s1 <= 122)
     *s1 -= 32;
}

97とはと122の数値 ASCII コードです。は、小文字と対応する大文字の違いです -- 繰り返しますが、ASCII です。'a''z'32

C 言語は、文字が ASCII で表現されること、またはそれと互換性のある文字セットで表現されることを保証しません。たとえば、IBM メインフレームでは、文字は EBCDIC で表されます。この場合、文字のコードは連続しておらず (ギャップがあります)、対応する小文字と大文字の違いは 32 ではなく 64 です。

最近では EBCDIC システムはまれですが、それでも移植可能なコードは移植不可能なコードよりも明確である傾向があり、コードがすべてのシステムで機能するかどうかという実際的な問題は別としても同様です。

ご存じのとおり、これを行う最善の方法は次のtolower関数を使用することです。

*s1 = tolower((unsigned char)*s1);

へのキャストに注意してくださいunsigned char。で宣言されたto*()andis*()関数は、<ctype.h>歴史的な理由から奇妙な振る舞いをしています。charそれらは引数では機能しません。むしろ、intの範囲内にある引数で動作しますunsigned charEOF(通常は であるも受け入れ-1ます)。プレーンcharが署名されているchar場合、たまたま負の値を渡すと、未定義の動作が発生します。はい、それは迷惑です。

しかし、あなたは使いたくないと言いますtolower。(これは問題ありません。このようなことを自分で行うことを学ぶことは、良い練習になります。)

大文字が連続しており、小文字が連続していると仮定したい場合は、次のようにすることができます。

if (*s1 >= 'a' && *s1 <= 'z') {
    *s1 -= 'a' - 'A';
}

これはまだ非 ASCII システムには移植できませんが、たまたま ASCII テーブルを記憶していなければ、読みやすくなります。

また、ロジックが逆になっていることが少し明らかになります。小文字に変換したいと言いますが、コードは小文字から大文字に変換されます。

または、小文字を大文字にマップするルックアップ テーブルを使用できます。

char to_lower[CHAR_MAX] = { 0 }; /* sets all elements to 0 */
to_lower['A'] = 'a';
to_lower['B'] = 'b';
/* ... */
to_lower['Z'] = 'z';

または、コンパイラが複合リテラルをサポートしている場合:

const char to_lower[CHAR_MAX] = {
    ['A'] = 'a',
    ['B'] = 'b',
    /* ... */
};

残りはあなたに任せて、それを使用するコードを書きます。

tolowerこれで、 関数と関数が存在する理由がわかりました。toupperしたがって、これらすべてのものを処理する必要はありません (必要になる奇妙なunsigned charキャストは別として)。

アップデート :

あなたの質問の新しい部分に応えて:

char* temp = malloc(100);   
temp = s1;

その割り当てtemp = s1;は、割り当てられた文字列をコピーしません。ポインタをコピーするだけです。temp割り当てられたスペースの 100 バイトを指しますがtemp、(読み取り専用) 文字列リテラルを指すようにすると、割り当てられたスペースへの参照が失われ、メモリ リークが発生します。

C では文字列または配列を割り当てることはできません。文字列をコピーするには、次のstrcpy()関数を使用します。

char *temp = malloc(100);
if (temp == NULL) {     /* Don't assume the allocation was successful! */
    fprintf(stderr, "malloc failed\n");
    exit(EXIT_FAILURE);
}
strcpy(temp, s1);

また、すでにメモリ内にある文字列にメモリを割り当てる必要があるのはなぜですか?

それはメモリ内にありますが、変更が許可されていないのはメモリです。変更する場合は、変更可能な場所にコピーする必要があります。または、上で提案したように、最初に読み取り/書き込みメモリに配置できます。

char s[] = "Bob";

その初期化により、文字列が配列にコピーされますs

于 2013-11-11T16:42:37.860 に答える
2

char を初期化し、malloc を使用してメモリを割り当ててすべての文字列を格納し、for ループを使用して文字列全体を小文字に変換します。

于 2013-11-11T16:19:46.067 に答える
1

必要がある

  1. バッファを割り当てる
  2. 文字列「Bob」をバッファにコピーします
  3. 文字列をループしながら編集します。
于 2013-11-11T16:16:35.437 に答える
0

通常、文字列リテラルは読み取り専用メモリに格納されるため、これは失敗します。

最も簡単な修正は、リテラルを使用して配列を初期化することです。配列は変更可能になります (明示的に作成されていない限り、constそうしないでください)。

char s1[] = "Bob";

また、ASCII をハードコードするのは非常に悪い形式であり、このコードを適切にするためにislower()tolower()関数を使用します。<ctype.h>

于 2013-11-11T16:32:50.393 に答える