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

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

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

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

GCC

GCCはGNU Compiler Collectionの略です。LinuxのC言語コンパイラのデファクトスタンダードであり、数多くの他言語やプラットフォームサポートもします。

Linux

Linuxは、Unixをベースにして開発されたオペレーティングシステムです。日本では「リナックス」と呼ばれています。 主にWebサーバやDNSサーバ、イントラネットなどのサーバ用OSとして利用されています。 上位500のスーパーコンピュータの90%以上はLinuxを使用しています。 携帯端末用のプラットフォームAndroidは、Linuxカーネル上に構築されています。

ポインタ

ポインタはアドレスを用いてメモリに格納された値を"参照する"変数です。

関数

関数(ファンクション・メソッド・サブルーチンとも呼ばれる)は、はプログラムのコードの一部であり、ある特定のタスクを処理するように設計されたものです。

Q&A

6回答

2641閲覧

ポインタとSegmentation fault

tanukidayo

総合スコア4

C

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

GCC

GCCはGNU Compiler Collectionの略です。LinuxのC言語コンパイラのデファクトスタンダードであり、数多くの他言語やプラットフォームサポートもします。

Linux

Linuxは、Unixをベースにして開発されたオペレーティングシステムです。日本では「リナックス」と呼ばれています。 主にWebサーバやDNSサーバ、イントラネットなどのサーバ用OSとして利用されています。 上位500のスーパーコンピュータの90%以上はLinuxを使用しています。 携帯端末用のプラットフォームAndroidは、Linuxカーネル上に構築されています。

ポインタ

ポインタはアドレスを用いてメモリに格納された値を"参照する"変数です。

関数

関数(ファンクション・メソッド・サブルーチンとも呼ばれる)は、はプログラムのコードの一部であり、ある特定のタスクを処理するように設計されたものです。

0グッド

0クリップ

投稿2020/03/31 14:41

編集2020/03/31 16:00

前提・実現したいこと

C言語でパソコン屋さんの注文画面を作っています。
一度書き上手くいったコードを関数に書き直していたところ、エラーになってしまいました。
Order Summaryまでは表示され、そのあとにSegmentation faultと表示されてしまいます。
よって、printf(" Selected item: %s\n", laptops[(int)select - 1].item);の部分が間違っていると解釈しています。
構造体とポインタ部分が間違っていると予想していますが、ポインタを全く理解できておらず、なにもわかりません。教えて下さい。

発生している問題・エラーメッセージ

Segmentation fault

該当のソースコード

C

1#include <stdio.h> 2#include <stdbool.h> 3#include <string.h> 4#include <math.h> 5 6typedef struct laptop { 7 char item[10]; 8 float price; 9} laptop_t; 10 11void Order(float select, float qty, laptop_t *laptops); 12void Total(); 13void FlushScanner(); 14 15void main(){ 16 Total(); 17} 18 19void Order(float select, float qty, laptop_t *laptops) { 20 printf("List of available laptops:\n"); 21 for (int i = 0; i < 6; i++) { 22 printf(" %d. %s\tRM%.2f\n", i + 1, laptops[i].item, laptops[i].price); 23 }; 24 printf("--------------------------\n"); 25 printf("Select item> "); 26 scanf("%f", &select); 27 while ((select < 1 || select > 6) || (ceil(select) != floor(select))) { 28 printf(" Error: enter again\n"); 29 printf("Select item > "); 30 scanf("%f", &select); 31 } 32 printf("Quantity > "); 33 scanf("%f", &qty); 34 while (qty < 1 || ceil(qty) != floor(qty)) { 35 printf(" Error: enter again\n"); 36 printf("Quantity > "); 37 scanf("%f", &qty); 38 } 39 printf("\n\n"); 40} 41 42void Total() { 43 laptop_t laptops[6]; 44 strcpy(laptops[0].item, "Acer"); 45 laptops[0].price = 1999.00; 46 strcpy(laptops[1].item, "HP "); 47 laptops[1].price = 2399.00; 48 strcpy(laptops[2].item, "Dell"); 49 laptops[2].price = 4999.00; 50 strcpy(laptops[3].item, "Lenovo"); 51 laptops[3].price = 3949.00; 52 strcpy(laptops[4].item, "Toshiba"); 53 laptops[4].price = 3999.00; 54 strcpy(laptops[5].item, "Sony"); 55 laptops[5].price = 7880.00; 56 57 float select, qty; 58 Order(select, qty, laptops); 59 60 float discount, subtotal, gst, total; 61 printf("Order summary\n"); 62 printf("-------------------------------\n"); 63 printf(" Selected item: %s\n", laptops[(int)select - 1].item); 64 printf(" Item price: RM%.2f\n", laptops[(int)select - 1].price); 65 subtotal = laptops[(int)select - 1].price * qty; 66 printf(" Subtotal(%.f item(s)): RM%.2f\n", qty, subtotal); 67 if (select == 4) { 68 discount = subtotal * 0.1; 69 printf(" Discount 10%%: -RM%.2f\n", discount); 70 } else if (select == 2 || select == 5) { 71 discount = 0; 72 } else { 73 discount = subtotal * 0.05; 74 printf(" Discount 5%%: -RM%.2f\n", discount); 75 } 76 gst = (subtotal - discount) * 0.08; 77 printf(" GST(8%%): RM%.2f\n", gst); 78 total = subtotal - discount + gst; 79 printf("-------------------------------\n"); 80 printf("\t\tTOTAL: RM%.2f\n", total); 81 FlushScanner(); 82} 83 84void FlushScanner() { 85 while (getchar() != '\n'); 86}

試したこと

闇雲にポインタをつけてみるばかりです。

補足情報(FW/ツールのバージョンなど)

Linux / gcc

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

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

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

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

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

otn

2020/03/31 15:01 編集

> ポインタを全く理解できておらず、 プログラムを書くのは、まず理解してからですね。 回答がついても理解できないでしょうから。
KoichiSugiyama

2020/03/31 15:50

どういう操作をしたら該当するエラーになったか、もしくはどこを関数化したらエラーになったのかを追記してくださった方が問題点を指摘してもらいやすくなると思います。
tanukidayo

2020/03/31 15:59

Order Summaryまでは表示され、そのあとにSegmentation faultと表示されてしまいます。 よって、printf(" Selected item: %s\n", laptops[(int)select - 1].item);の部分が間違っていると解釈しています。
dodox86

2020/03/31 16:33

> ポインタを全く理解できておらず、なにもわかりません。教えて下さい。 > 闇雲にポインタをつけてみるばかりです。 ポインタのいちから全て教えて欲しいということでしょうか。回答に躊躇してしまう事案です。 float select, qty; Order(select, qty, laptops); で、関数における仮引数、実引数、というものは理解されていますでしょうか。
guest

回答6

0

上手くいったコードを関数に書き直していたところ、エラー

ポインタもさることながら、実引数と仮引数の関係もアヤシイ感じですね。
質問者は(見るだけでなく)次の最小限のコードから始めて、コンパイルして何が起こるか、動作を確かめてください。それだけでなく、途中、自分でコードを手直しして、自分の考えを確かめて見る姿勢が大事です。

C

1#include <stdio.h> 2void main(void) 3{ 4 int num = 12; 5 printf("before: num = %d\n", num); 6 num = 7; 7 printf(" after: num = %d\n", num); 8}

この中の num = 7; を関数にします。質問者のやり方だと次のようなコードになるでしょう。

C

1#include <stdio.h> 2void func(int num) { // ← ここの num は仮引数 3 num = 7; 4} 5void main(void) { 6 int num = 12; 7 printf("before: num = %d\n", num); 8 func(num); // ← ここの num は実引数 9 printf(" after: num = %d\n", num); 10}

関数 func() が変数 num の値を書き変えた(つもりな)のに、表示されたのは before も after も 12 です。

  • func() 関数は、main() 関数の num を書き変えない

なぜなら

  • func() 関数が見ている num と、main() 関数が見ている num は、別の変数だから

同姓同名の人だとか、全国各地に「銀座」がある・・・ように、名前は(たまたま?)同じだけど実体は別、名前が同じでもモノが同じとは限りません。

重要なキーワードは変数のスコープです。

実引数と仮引数の名前がなまじ同じだと混同しがちなので、あえて実引数・仮引数を別の名前にしたらどうでしょうか。例えば func() 関数を次のように書き変えても実質的な違いは皆無です。

C

1void func(int numarg) { 2 numarg = 7; 3}

では関数呼出しの際の引数は、何がどのように関数に渡るのか。その際

  • 実引数の値が、仮引数の変数に代入されて、関数呼出しが行われる

という仕組みが隠れている事を意識すると良いです。たとえば上のコードなら numarg = num という代入が隠れています。
仮引数 numarg に**代入される値は、変数 num の値(=12)**です。
繰り返しますが、このやり方で、func() は main() の num を書き換えることはできません。


では、func() が main() の中の num を書き換えたい場合はどうするか?
そうしたい事はあるので次のコードを示します。上のコートと何が違うか、よく見比べて、動作を確認してください。

C

1#include <stdio.h> 2void func(int *nptr) { // 仮引数 nptr はポインタ変数 3 *nptr = 7; 4} 5void main(void) { 6 int num = 12; 7 printf("before: num = %d\n", num); 8 func(&num); // 実引数は num のアドレス 9 printf(" after: num = %d\n", num); 10}

今度は呼出された関数 func() が、呼出した側である main() の中の変数 num を書き換えました。
「*nptr = 7」は nptr 変数が指す(ポイントする)メモリに 7 を代入します。

「*」は間接演算子です(闇雲につけてはダメw)。
ポインタ変数 nptr の値はメモリアドレスです。代入する場所は、nptr そのものではなく、nptr が指す(ポイントする)先のメモリ、なので「間接」です。変数のありか(アドレス)が分かれば、その値を知ることも、書き換えることもできます。

「&num」の「&」はアドレス演算子です。
普通、変数はメモリ上に割り当てられます。メモリには1バイト毎にアドレスが振られており、変数にはそれぞれアドレス(メモリアドレス)があります。
アドレスは変数の「ありか」です。
「&num」はアドレス演算子「&」を使って、num のアドレス(ありか)を得ます。その値を関数 func() に渡しています。この関数呼出しには「nptr = &num」という代入が隠れています。
仮引数 nptr に代入されたのは、num の値ではなく、num のアドレスです。


次はキーボードから数値を入力するコードです。

C

1 int num; 2 scanf("%d", &num);

ここにも &num があります。scanf() の第一引数は文字列ですが、第二引数は変数のアドレスです。そのココロは「num のありかを教えるから、ここに値を書き込め」。main() 関数自身が scanf() を呼出して値を得るならこのままですが、
func() 関数が scanf() を呼出し、main() の中の num 変数に値を入力するにはどうするか。

C

1void func(int *nptr) { 2 int temp; // 一時変数を作る 3 scanf("%d", &temp); // いったん一時変数に値を入力し、 4 *nptr = temp; // main() の num に代入する 5}

でも可能ですが、nptr の値は格納すべき変数のありか・アドレスなのだから、
それをそのまま scanf() に渡せば一時変数は要らなくなります。つまりこうです。

C

1#include <stdio.h> 2void func(int *nptr) { 3 scanf("%d", nptr); // 書込むべき所に書き込め 4} 5void main(void) { 6 int num; 7 func(&num); // 「num に値を入力したい」 8}

func() から呼び出された scanf() は、main() の num に値を直接書込む、そういう動作をします。


配列の場合。

C

1void func(int *nptr) { // narray[0] のアドレスが渡る 2 nptr[1] = 5; // narray[1] に 5 を代入 3} 4void main(void) { 5 int narray[] = { 1, 2, 3 }; 6 func(narray); // 配列の先頭アドレスを渡す 7}

** 配列の名前「narray」は配列の先頭アドレス(== &narray[0])**という約束があります。繰返しますが、先頭「アドレス」です。
次のように書くこともできます。ココロは「仮引数 nptr が指す先は配列だよ」。実質的な違いは皆無です。

C

1void func(int nptr[]) { // 配列の先頭アドレスが渡る

ここまでで他の方の回答も参考書も、理解の手がかりになると思います。


気づいたこと。

  • float select, qty; としていますが、ここで必要なのは整数値なので int 型で良いと思うのですが。ceil(), floor() を誤解しているのでは?

  • strcpy(laptops[1].item, "HP "); は "HP" であって "HP " ではないと思います。表示する側の %s を %-10s に変更してください。

C

1 printf("List of available laptops:\n"); 2 for (int i = 0; i < 6; i++) { 3 printf(" %d. %-10s\tRM%.2f\n", i + 1, laptops[i].item, laptops[i].price); 4 };
  • 構造体配列 laptop_t laptops[6]; は次で初期化できます。

C

1 laptop_t laptops[6] = { 2 { "Acer" , 1999.00 }, 3 { "HP" , 2399.00 }, 4 { "Dell" , 4999.00 }, 5 { "Lenovo" , 3949.00 }, 6 { "Toshiba" , 3999.00 }, 7 { "Sony" , 7880.00 }, 8 };
  • 動作中に item 名を変更する必要は無さそうなので、次のようにした方がメモリ効率が良くなる場合があります。上のように初期化し、他のコードはそのまま使えるはずです。

C

1typedef struct laptop { 2 char *item; // ここを配列ではなくポインタにできる 3 float price; 4} laptop_t;

追加

Order Summaryまでは表示され、そのあとにSegmentation faultと表示されてしまいます。
よって、printf(" Selected item: %s\n", laptops[(int)select - 1].item);の部分が間違っている

この printf()の前に
printf("select = %d\n", (int)select); を追加し、どんな値が表示されるか確認してください。おそらくとんでもない値・キーボードから入力した値と大きく異なる値が表示されると予想します。ちなみに、こういう方法を「printfデバッグ」と呼びます。デバッガを起動するまでもなく、動作状況を調べることができます。

疑問点はコメントしてください。
Enjoy !

投稿2020/04/02 02:16

編集2020/04/02 04:28
rubato6809

総合スコア1380

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

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

0

C

1void Order(float select, float qty, laptop_t *laptops);

select,qtyが値渡しのため、呼び出し元には関数内で設定した値は返りません。
そのためTotal()では不定値になっています。

投稿2020/03/31 23:37

SHOMI

総合スコア4079

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

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

0

printf(" Selected item: %s\n", laptops[(int)select - 1].item);

select という変数は初期化されないまま使用されています
ローカル変数は初期化されなければ、デタラメな値が入ってます

投稿2020/03/31 23:02

y_waiwai

総合スコア87749

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

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

0

原因は、他の回答者に任せるとして
違う観点から
ある程度、箇所が特定できているなら
「select」の内容をprintfで出力して
期待値かどうか確認してみてください。
期待値では、ないなら初期値や代入している値を確認してください。

また、ポインタを全く理解できていないなら
理解するように勉強してください。
もし理解できないならC言語はあきらめて
違う言語で開発することおすすめします。

投稿2020/04/02 12:14

ai_2013_dev

総合スコア338

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

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

0

とりあえず、修正しておきました。
selectの型が何故int型でなくfloat型なのかがよくわかりませんが、以下のようにしてください。
不明点があれば、補足してください。

C

1#include <stdio.h> 2#include <stdbool.h> 3#include <string.h> 4#include <math.h> 5 6typedef struct laptop { 7 char item[10]; 8 float price; 9} laptop_t; 10 11void Order(float *select, float *qty, laptop_t *laptops); 12void Total(); 13void FlushScanner(); 14 15void main(){ 16 Total(); 17} 18 19void Order(float *select, float *qty, laptop_t *laptops) { 20 printf("List of available laptops:\n"); 21 for (int i = 0; i < 6; i++) { 22 printf(" %d. %s\tRM%.2f\n", i + 1, laptops[i].item, laptops[i].price); 23 }; 24 printf("--------------------------\n"); 25 printf("Select item> "); 26 scanf("%f", select); 27 while ((*select < 1 || *select > 6) || (ceil(*select) != floor(*select))) { 28 printf(" Error: enter again\n"); 29 printf("Select item > "); 30 scanf("%f", select); 31 } 32 printf("Quantity > "); 33 scanf("%f", qty); 34 while (*qty < 1 || ceil(*qty) != floor(*qty)) { 35 printf(" Error: enter again\n"); 36 printf("Quantity > "); 37 scanf("%f", qty); 38 } 39 printf("\n\n"); 40} 41 42void Total() { 43 laptop_t laptops[6]; 44 strcpy(laptops[0].item, "Acer"); 45 laptops[0].price = 1999.00; 46 strcpy(laptops[1].item, "HP "); 47 laptops[1].price = 2399.00; 48 strcpy(laptops[2].item, "Dell"); 49 laptops[2].price = 4999.00; 50 strcpy(laptops[3].item, "Lenovo"); 51 laptops[3].price = 3949.00; 52 strcpy(laptops[4].item, "Toshiba"); 53 laptops[4].price = 3999.00; 54 strcpy(laptops[5].item, "Sony"); 55 laptops[5].price = 7880.00; 56 57 float select, qty; 58 Order(&select, &qty, laptops); //修正 59 60 float discount, subtotal, gst, total; 61 printf("Order summary\n"); 62 printf("-------------------------------\n"); 63 printf(" Selected item: %s\n", laptops[(int)select - 1].item); 64 printf(" Item price: RM%.2f\n", laptops[(int)select - 1].price); 65 subtotal = laptops[(int)select - 1].price * qty; 66 printf(" Subtotal(%.f item(s)): RM%.2f\n", qty, subtotal); 67 if (select == 4) { 68 discount = subtotal * 0.1; 69 printf(" Discount 10%%: -RM%.2f\n", discount); 70 } else if (select == 2 || select == 5) { 71 discount = 0; 72 } else { 73 discount = subtotal * 0.05; 74 printf(" Discount 5%%: -RM%.2f\n", discount); 75 } 76 gst = (subtotal - discount) * 0.08; 77 printf(" GST(8%%): RM%.2f\n", gst); 78 total = subtotal - discount + gst; 79 printf("-------------------------------\n"); 80 printf("\t\tTOTAL: RM%.2f\n", total); 81 FlushScanner(); 82} 83 84void FlushScanner() { 85 while (getchar() != '\n'); 86} 87

投稿2020/04/01 06:32

tatsu99

総合スコア5438

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

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

0

SegVの原因は、y_waiwaiさん、SHOMIさんの説明通りです。
ポインタを理解せぬまま商用物を作成されることが非常に心配ですが、、、
とりあえずSegVに関して添削してみます。
(diffっぽく表現します)
< で始まる行は修正前
> で始まる行は修正後
としてお読みください。

C

1< void Order(float select, float qty, laptop_t *laptops); 2> void Order(float *select, float *qty, laptop_t *laptops); 3 4< void Order(float select, float qty, laptop_t *laptops) { 5> void Order(float *select, float *qty, laptop_t *laptops) { 6 7< scanf("%f", &select); 8< while ((select < 1 || select > 6) || (ceil(select) != floor(select))) { 9> scanf("%f", select); 10> while ((*select < 1 || *select > 6) || (ceil(*select) != floor(*select))) { 11 12< scanf("%f", &select); 13> scanf("%f", select); 14 15< scanf("%f", &qty); 16< while (qty < 1 || ceil(qty) != floor(qty)) { 17> scanf("%f", qty); 18> while (*qty < 1 || ceil(*qty) != floor(*qty)) { 19 20< scanf("%f", &qty); 21> scanf("%f", qty); 22 23< Order(select, qty, laptops); 24> Order(&select, &qty, laptops);

試していませんが、とりあえずSegVは回避できる筈です。

投稿2020/04/01 05:57

DreamTheater

総合スコア1095

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問