質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Q&A

解決済

3回答

5207閲覧

C言語:構造体メンバの文字配列長さ

so9999

総合スコア18

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

0グッド

1クリップ

投稿2017/05/09 07:23

編集2017/05/09 07:24

###質問したいこと
0. C言語の構造体定義において、メンバが文字列である場合、文字配列の長さは「必要な文字の長さ+1」で正しいでしょうか。
0. また、文字列の定義において、以下の"s1"のような定義が正しい場合はあり得るでしょうか。

c

1char s1[1]; /* 終端文字が入らないので、この記述はあり得ない? */ 2char s2[2]; /* 文字列として、長さ1の場合、この書き方が正しい? */ 3char s3; /* 文字であればこれでもOKのはず */

###質問の背景と詳細
C言語については完全な初心者ですが、次のプロジェクトでC言語を使用することになったので、設計書等を読みつつ、勉強中の身です。
プロジェクト自体は、詳細設計が終わり、来週から実装に取り掛かる、という状況ですが、構造体定義の設計書がほぼプログラムそのままでした。

その設計書を読んでいると、以下のような記述が散見されました。

c

1struct person { 2 char s_date[8]; /* yyyyMMdd */ 3 char s_sex[1]; /* M or F */ 4};

まず、s_dateについてですが、この日付は「yyyyMMdd」の形式とします。
8桁なので一見合っているのですが、C言語の場合、終端文字を考慮して、「必要な文字の長さ+1」にする必要があると思っています。
また、s_sexについてですが、これもs_dateの場合と同じです。そもそも、終端文字を考慮するなら、「文字配列を定義しているのに長さが1」という状況は絶対にありえない、と考えています。

よって、正しくは以下のような記述になると思っています。

c

1struct person { 2 char s_date[9]; /* yyyyMMdd */ 3 char s_sex[2]; /* M or F */ 4};

かなり初歩的な内容ですが、私の考えは正しいのでしょうか…。
設計書には上記の誤り(?)が非常に多く、また、プロジェクトにはC言語に精通した人が多く居るらしいので、私の考えの方が間違っている可能性が高いと思い、こちらで質問させて頂きました。

ちなみに、次のプロジェクトのメンバとは距離的にも心理的にも遠く、質問がし辛い状況です。

よろしくお願いします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

ベストアンサー

まず、C に文字列、というものは本質的には存在しません。あるポインタから、\0(null)にたどり着くまでのメモリ空間を文字列とみなしているだけです。
また char[] の形は、「文字の配列」であって「文字列」ではありません。

逆に言えば、\0 がないことを考慮して固定サイズのメモリ空間を扱うのであれば、問題はないのです。

C

1struct person { 2 char s_date[8]; /* yyyyMMdd */ 3 char s_sex[1]; /* M or F */ 4}

この場合、s_date が 8byte であることを理解して、文字列として扱わなければ問題がありません。
strcpy じゃなくて strncpy を使うとか、strlen を使わないとか、そういう「\0で終わっていることを期待しない」使い方をする限りはです。

また、 char val[1]; のような書き方は、他との整合性をとるために使うことはあります。

  • 固定長の長さであることが保証できる
  • 組み込み機器などでメモリ容量に余裕がない

などの場合はこの形になるのが普通ですね。逆に長さが可変で分からない場合は、

c

1struct person 2 char* name; // 実際に使うときは malloc して割り当ててから使う 3 char* address; 4}

のように、文字へのポインタ(≒文字列)として定義して、使うときに最低必要量のメモリを割り当て・開放して処理しますかね。

投稿2017/05/09 07:39

tacsheaven

総合スコア13703

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

so9999

2017/05/09 08:26

なるほど、C言語に文字列が無いのは分かっていたつもりですが、ちゃんと理解できていませんでした。 ネット上で入門向けの資料は読んだのですが、入門レベルだと printf を良く使うので、必ず文字列長+1だと勘違いしていました。 単にデータとして持ち、終端文字を期待する関数を使わないのであれば、+1する必要はないわけですね。 回答ありがとうございました!
yohhoy

2017/05/09 08:46 編集

FYI: strncpy関数はまさにこのような「構造体中にある固定長フィールド」を扱うために導入されました。現在ではあまり意図通りの使われ方をされていない気がしますが... http://www.open-std.org/jtc1/sc22/wg14/www/C99RationaleV5.10.pdf > 7.21.2.4 The strncpy function > > strncpy was initially introduced into the C library to deal with fixed-length name fields in structures such as directory entries. Such fields are not used in the same way as strings: the trailing null is unnecessary for a maximum-length field, and setting trailing bytes for shorter names to null assures efficient field-wise comparisons. strncpy is not by origin a “bounded strcpy,” and the Committee preferred to recognize existing practice rather than alter the function to better suit it to such use.
guest

0

C言語における文字列の認識としては正しいですが、NULL終端である必要はありません。
NULL終端にする必要があるのは、たいてい以下の理由です。
・文字数が可変の文字列を扱いたい
・何も考えずにprintf等で出力したい
・string系の関数を使いたい

例えば質問にあるs_dateなんかは日付なので8文字と決まっています。
これを敢えてNULLも考慮して9バイト用意する必要はありません。
但し、用途によってはNULL終端にしておいたほうが便利な場合もあるので、どちらが良いかは実装方法により異なります。

いくつか例を書いておきますが、業務でprintfを使うことはあまりないと思うのでそこらへんは参考程度に。

C

1// NULL終端の場合 2char s_date[9]; 3strcpy(s_date, "20170509"); 4printf("%s\n", s_date); 5 6char s_sex[2]; 7s_sex[0] = 'M'; 8s_sex[1] = 0x00; 9if(strcmp(s_sex, "M") == 0){ 10} 11 12// NULL終端ではない場合 13char s_date[8]; 14strncpy(s_date, "20170509", 8); 15printf("%.8s\n", s_date); 16 17char s_sex[1]; 18s_sex[0] = 'M'; 19if(s_sex == 'M'){ 20}

それから質問タイトルに構造体メンバとありますが、文字列自体の考え方は構造体云々は関係ありません。

投稿2017/05/09 07:42

ttyp03

総合スコア16998

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

so9999

2017/05/09 08:31

実例まで挙げてくださり、助かります! やはり業務では printf はまず使いませんよね…。入門向けのサンプルしか勉強していないので、勘違いしていました。 構造体は関係ないとは思ったのですが、C言語が分からな過ぎて…。すいません。 回答ありがとうございました!
guest

0

1. C言語の構造体定義において、メンバが文字列である場合、文字配列の長さは「必要な文字の長さ+1」で正しいでしょうか。

文字列は、
・固定長文字列(文字列の長さ(文字数)はいつも同じ文字列(例えば、YYYYMMDDのように常に8文字の数字から成るとか、性別を表す文字列(MもしくはFの1文字))と、
・可変長文字列(文字数は変わるので、どこが終わりなのかを示す記号(null)が必要)
に分けられます。

固定長文字列を表す文字の配列であれば、その文字数分の配列で十分ですから、次のようなコードは、有り得ます。

C

1struct person{ 2 char birthdate[8]; 3 char sex[1]; 4}

終端のnulが無いので、printf("%s\n",Ann.birthdate);のような使い方はできません。
でも、printf("性別:%c",Ann.sex);のように使うのであれば、何の問題もありません。

質問者は「固定長文字列は有り得ない」と思っていらっしゃるようですが、固定長文字列というのも存在するのだと考えれば、みんな「有り得る」ことに納得できるのではないでしょうか。

投稿2017/05/09 08:41

coco_bauer

総合スコア6915

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問