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;
}
これは期待どおりに機能しますが、これを行うためのより良い方法があるのだろうかと思います。ありがとう。
最初の例は、配列に値を割り当てることができないため機能しません。この点では、配列は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);
文字列は抽象オブジェクト、文字配列はコンテナと考えてください。文字列は任意のサイズにすることができますが、コンテナは文字列の長さより少なくとも1大きくなければなりません(ヌルターミネータを保持するため)。
Cには、文字列の構文サポートがほとんどありません。文字列演算子はありません(char-arrayおよびchar-pointer演算子のみ)。文字列を割り当てることはできません。
ただし、関数を呼び出して、目的を達成することができます。
ここでstrncpy()
関数を使用できます。安全性を最大限に高めるには、次のパターンに従うことをお勧めします。
_strncpy(p.name, "Jane", 19);
p.name[19] = '\0'; //add null terminator just in case
_
strncat()
およびmemcpy()
関数もご覧ください。
2つの構造体は異なります。最初の構造体を初期化すると、約40バイトのメモリが割り当てられます。 2番目の構造体を初期化すると、約10バイトのメモリが割り当てられます。 (実際の量はアーキテクチャに依存します)
文字列リテラル(文字列定数)を使用して、文字配列を初期化できます。これが理由です
person p = {"John"、 "Doe"、30};
最初の例で動作します。
(従来の意味で)Cの文字列を割り当てることはできません。
所有する文字列リテラル( "John")は、コードの実行時にメモリにロードされます。これらのリテラルのいずれかで配列を初期化すると、文字列が新しいメモリの場所にコピーされます。 2番目の例では、単に文字列リテラル(の場所)へのポインターをコピーしています。次のようなことをする:
char* string = "Hello";
*string = 'C'
コンパイルまたはランタイムエラーが発生する可能性があります(わかりません)。たとえば、マイクロコントローラーで読み取り専用メモリにあるリテラル文字列 "Hello"を変更しているため、これは悪い考えです。