web-dev-qa-db-ja.com

新しい文字列値を正しく割り当てる方法は?

Cでこの些細な問題を解決する方法を、最もクリーンで安全な方法で理解しようとしています。これが私の例です:

#include <stdio.h>

int main(int argc, char *argv[])
{
    typedef struct
    {
        char name[20];
        char surname[20];
        int unsigned age;
    } person;

    //Here i can pass strings as values...how does it works?
    person p = {"John", "Doe",30};

    printf("Name: %s; Age: %d\n",p.name,p.age);
    // This works as expected...
    p.age = 25;
    //...but the same approach doesn't work with a string
    p.name = "Jane";

    printf("Name: %s; Age: %d\n",p.name,p.age);

    return 1;
}

コンパイラのエラーは次のとおりです。

main.c:関数「main」内:main.c:18:エラー:タイプ「char *」からタイプ「char [20]」への割り当て時に互換性のないタイプ

C(C++ではない)にはString型がなく、代わりにcharの配列を使用することを理解しているため、これを行う別の方法は、charのポインターを保持するように構造体の例を変更することでした:

#include <stdio.h>

int main(int argc, char *argv[])
{
    typedef struct
    {
        char *name;
        char *surname;
        int unsigned age;
    } person;

    person p = {"John", "Doe",30};

    printf("Name: %s; Age: %d\n",p.name,p.age);

    p.age = 25;

    p.name = "Jane";

    printf("Name: %s; Age: %d\n",p.name,p.age);

    return 1;
}

これは期待どおりに機能しますが、これを行うためのより良い方法があるのだろうかと思います。ありがとう。

44

最初の例は、配列に値を割り当てることができないため機能しません。この点では、配列はconstポインターのように機能します。ただし、できることは、新しい値を配列にコピーすることです。

strcpy(p.name, "Jane");

文字配列の最大サイズが事前にわかっている場合は、Char配列を使用するのが適切です。最初の例では、名前が19文字に収まることを100%確信しています(20文字ではなく、終端のゼロ値を保存するために常に1文字が必要です)。

逆に、文字列の可能な最大サイズがわからない場合、および/またはメモリ使用量を最適化する場合は、ポインターが優れています。名前「John」に512文字を予約しないでください。ただし、ポインタを使用する場合は、メモリリークを回避するために、ポインタが指すバッファを動的に割り当て、不要になったら解放する必要があります。

更新:動的に割り当てられたバッファの例(2番目の例の構造体定義を使用):

char* firstName = "Johnnie";
char* surname = "B. Goode";
person p;

p.name = malloc(strlen(firstName) + 1);
p.surname = malloc(strlen(surname) + 1);

p.age = 25;
strcpy(p.name, firstName);
strcpy(p.surname, surname);

printf("Name: %s; Age: %d\n",p.name,p.age);

free(p.surname);
free(p.name);
40
Péter Török

文字列は抽象オブジェクト、文字配列はコンテナと考えてください。文字列は任意のサイズにすることができますが、コンテナは文字列の長さより少なくとも1大きくなければなりません(ヌルターミネータを保持するため)。

Cには、文字列の構文サポートがほとんどありません。文字列演算子はありません(char-arrayおよびchar-pointer演算子のみ)。文字列を割り当てることはできません。

ただし、関数を呼び出して、目的を達成することができます。

ここでstrncpy()関数を使用できます。安全性を最大限に高めるには、次のパターンに従うことをお勧めします。

_strncpy(p.name, "Jane", 19);
p.name[19] = '\0'; //add null terminator just in case
_

strncat()およびmemcpy()関数もご覧ください。

9
Artelius

2つの構造体は異なります。最初の構造体を初期化すると、約40バイトのメモリが割り当てられます。 2番目の構造体を初期化すると、約10バイトのメモリが割り当てられます。 (実際の量はアーキテクチャに依存します)

文字列リテラル(文字列定数)を使用して、文字配列を初期化できます。これが理由です

person p = {"John"、 "Doe"、30};

最初の例で動作します。

(従来の意味で)Cの文字列を割り当てることはできません。

所有する文字列リテラル( "John")は、コードの実行時にメモリにロードされます。これらのリテラルのいずれかで配列を初期化すると、文字列が新しいメモリの場所にコピーされます。 2番目の例では、単に文字列リテラル(の場所)へのポインターをコピーしています。次のようなことをする:

char* string = "Hello";
*string = 'C'

コンパイルまたはランタイムエラーが発生する可能性があります(わかりません)。たとえば、マイクロコントローラーで読み取り専用メモリにあるリテラル文字列 "Hello"を変更しているため、これは悪い考えです。

5
Gus