web-dev-qa-db-ja.com

Cでcharの文字列を変更することは可能ですか?

ポインターに関連するあらゆる種類のCチュートリアルや書籍で数時間苦労してきましたが、作成したcharポインターを変更できるかどうかを本当に知りたいと思います。

これは私が試したものです:

char *a = "This is a string";
char *b = "new string";

a[2] = b[1]; // Causes a segment fault

*b[2] = b[1]; // This almost seems like it would work but the compiler throws an error.

ポインターのアドレスではなく、文字列内の値を変更する方法はありますか?

ありがとう

編集:

回答ありがとうございます。今ではもっと理にかなっています。なぜそれが時々うまく動作し、他の回は動作しなかったのか、特に理にかなっています。ときどき、charポインターとchar配列(char配列は正常に機能した)を渡すからです。

62
John Baker

いいえ、文字列は読み取り専用メモリに保存できるため、変更することはできません。変更したい場合は、代わりに配列を使用できます。

char a[] = "This is a string";

または、mallocを使用してメモリを割り当てることもできます。

char *a = malloc(100);
strcpy(a, "This is a string");
free(a); // deallocate memory once you've done
23

Cの文字列リテラルと組み合わせて、char *とchar []の違いについて多くの人が混乱します。

char *foo = "hello world";

...実際にはfooを一定のメモリブロックに向けています(実際、このインスタンスでコンパイラが「hello world」で行うことは実装依存です。)

代わりにchar []を使用すると、コンパイラに配列を作成し、その内容を「hello world」で埋めることを伝えます。 fooは、char配列の最初のインデックスへのポインターです。両方ともcharポインターですが、char []のみがローカルに割り当てられた可変メモリブロックを指します。

9
Jeff Ober

Aとbのメモリはユーザーが割り当てません。コンパイラは、文字を格納するための読み取り専用メモリの場所を自由に選択できます。そのため、変更しようとすると、セグエラーが発生する可能性があります。したがって、自分で文字配列を作成することをお勧めします。次のようなもの:char a[10]; strcpy(a, "Hello");

6
Naveen

あなたの質問には答えられたようですが、なぜchar * a = "String"が読み取り専用メモリに保存されているのか疑問に思うかもしれません。まあ、実際にはc99標準では未定義のままですが、ほとんどのコンパイラは次のようなインスタンスに対してこの方法を選択します。

printf("Hello, World\n");

c99 standard(pdf) [130ページ、セクション6.7.8]:

宣言:

char s[] = "abc", t[3] = "abc";

要素が文字列リテラルで初期化される「プレーンな」char配列オブジェクトsおよびtを定義します。この宣言はcharと同一です

s[] = { 'a', 'b', 'c', '\0' }, t[] = { 'a', 'b', 'c' };

配列の内容は変更可能です。一方、宣言

char *p = "abc";

p型を「charへのポインター」で定義し、初期化して、要素が文字列リテラルで初期化される、長さ4の「charの配列」型のオブジェクトを指すようにします。 pを使用して配列の内容を変更しようとした場合、動作は未定義です。

3
Sweeney

strdupを使用することもできます:

   The strdup() function returns a pointer to a new string which is a duplicate of the string  s.
   Memory for the new string is obtained with malloc(3), and can be freed with free(3).

あなたの例:

char *a = strdup("stack overflow");
2
Maxime Chéramy

これらはすべて、読み取り専用メモリに配置されているため、文字列リテラルを変更できない理由を説明する良い回答です。ただし、Pushが突き出た場合、これを行う方法があります。この例を確認してください。

#include <sys/mman.h>
#include <unistd.h>
#include <stddef.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

int take_me_back_to_DOS_times(const void *ptr, size_t len);

int main()
{
    const *data = "Bender is always sober.";
    printf("Before: %s\n", data);
    if (take_me_back_to_DOS_times(data, sizeof(data)) != 0)
        perror("Time machine appears to be broken!");
    memcpy((char *)data + 17, "drunk!", 6);
    printf("After: %s\n", data);

    return 0;
}

int take_me_back_to_DOS_times(const void *ptr, size_t len)
{
    int pagesize;
    unsigned long long pg_off;
    void *page;

    pagesize = sysconf(_SC_PAGE_SIZE);
    if (pagesize < 0)
        return -1;
    pg_off = (unsigned long long)ptr % (unsigned long long)pagesize;
    page = ((char *)ptr - pg_off);
    if (mprotect(page, len + pg_off, PROT_READ | PROT_WRITE | PROT_EXEC) == -1)
        return -1;
    return 0;
}

私はこれを const-correctnessについてのやや深い考え の一部として書いています。

それが役に立てば幸い。がんばろう!

1
user405725

文字列を読み取り専用メモリバッファではなく別のバッファにコピーして、そこで変更する必要があります。文字列のコピーにはstrncpy()、文字列の長さの検出にはstrlen()、新しい文字列にバッファを動的に割り当てるにはmalloc()およびfree()を使用します。

例(C++のような擬似コード):

int stringLength = strlen( sourceString );
char* newBuffer = malloc( stringLength + 1 );

// you should check if newBuffer is 0 here to test for memory allocaton failure - omitted

strncpy( newBuffer, sourceString, stringLength );
newBuffer[stringLength] = 0;

// you can now modify the contents of newBuffer freely

free( newBuffer );
newBuffer = 0;
0
sharptooth
char *a = "stack overflow";
char *b = "new string, it's real";
int d = strlen(a);

b = malloc(d * sizeof(char));
b = strcpy(b,a);
printf("%s %s\n", a, b);
0
luca