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

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

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

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

Q&A

解決済

1回答

884閲覧

自作した構造体のオブジェクトを格納するメモリ領域をcallocを使って取得しようとしたが、segmentation faultのエラーが出た

shocyu

総合スコア17

C

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

0グッド

0クリップ

投稿2023/03/20 09:15

編集2023/03/20 13:43

実現したいこと

C言語を使って
(1).すでにcsv形式で保存した構造体データのファイルを読み込む
(2).読み込んだデータを構造体データとして取得したメモリ領域に格納
(3).ファイルより取得した構造体データを特定のメンバをキーにしてソート
((3)はソース上にはない)

前提

macのデフォルトのgccコマンドを使ってコンパイル。コンパイルは成功
(実際のコンパイル環境は Clang)

macの環境でc言語のプログラムを書いています。
このコードの狙いとしては、先に「実現したいこと」に書いた通り、構造体のデータを一時的にcsv形式で保存し、編集する際には再度保存したcsvを読み込み、構造体として加工できるようにすることです(簡易的なデータベースを構造体を使って作成したい)。

コンパイルは成功しましたが、実行時に以下のエラーメッセージが発生しました。

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

コンパイルは成功するが、実行の際に
zsh: segmentation fault ./fileGet_andSave
とエラーが出る

(ターミナルに表示) zsh: segmentation fault ./fileGet_andSave (lldbを実行した際のエラー表示) Process 33450 stopped * thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x0) frame #0: 0x000000019cc67f64 libsystem_platform.dylib`_platform_strlen + 4

該当のソースコード

C

1#include <stdio.h> 2#include <stdlib.h> 3#include <string.h> 4 5#define NUM_LETTERS 16 6#define NUM_BUFF 256 7#define ONE 1 8#define COMMA "," 9 10/* definition of struct People */ 11typedef struct{ 12 /* data */ 13 unsigned int id; 14 char first_name[NUM_LETTERS]; 15 char last_name[NUM_LETTERS]; 16 unsigned short age; 17} People; 18 19int main(){ 20 unsigned int cnt, numData; 21 FILE *fp = NULL; 22 //ファイルの文字列を一時的に格納する配列 23 char strBuff[NUM_BUFF]; 24 //構造体の配列へのポインタ 25 People *p_PeopleGet; 26 size_t sizePeople; 27 28 numData = 0; 29 //区切り文字(デリミタ)をカンマとする 30 char comma[] = COMMA; 31 //構造体 Peopleのオブジェクト1個分のサイズを求める 32 sizePeople = sizeof(People); 33 34 //すでに保存されているcsvファイルを読み込む 35 fp = fopen("People.csv", "r"); 36 37 //fopenの結果に対するエラー処理 38 if(fp == NULL){ 39 printf("failed to open file\n"); 40 exit(EXIT_FAILURE); 41 } 42 else{ 43 printf("file open succeed.\n"); 44 } 45 46 //次に fgets関数で文字列を取得できるか確認 47 if(fgets(strBuff, NUM_BUFF, fp) == NULL){ 48 printf("string in file could not be got.\n"); 49 exit(EXIT_FAILURE); 50 } 51 52 //開いたcsvファイルの行数(データ数)を求める 53 while(fgets(strBuff, NUM_BUFF, fp) != NULL){ 54 numData++; 55 } 56 57 //データ数分の構造体データを格納する領域を確保 58 p_PeopleGet = calloc(numData, sizePeople); 59 //デバッグソース(この表示が出る前に segmentation faultが出る) 60 //printf("address of p_PeopleGet: %p", p_PeopleGet); 61 62 //1行分のデータをカンマで区切り、 63 //区切ったデータを順次確保した構造体の領域に格納 64 //id, age(年齢)については文字データを数値データ(int)に変換 65 for(cnt = 0; cnt < numData; cnt++){ 66 fgets(strBuff, NUM_BUFF, fp); 67 p_PeopleGet[cnt].id = atoi(strtok(strBuff, comma)); 68 strcpy(p_PeopleGet[cnt].last_name, strtok(NULL, comma)); 69 strcpy(p_PeopleGet[cnt].first_name, strtok(NULL, comma)); 70 p_PeopleGet[cnt].age = atoi(strtok(NULL, comma)); 71 } 72 73 //取得した構造体データを出力 74 for(cnt = 0; cnt < numData; cnt++){ 75 printf("id:%3u, ", p_PeopleGet[cnt].id); 76 printf("last_name: %-16s, ",p_PeopleGet[cnt].last_name); 77 printf("first_name: %-16s, ",p_PeopleGet[cnt].first_name); 78 printf("age:%d\n", p_PeopleGet[cnt].age); 79 } 80 81 free(p_PeopleGet); 82 fclose(fp); 83 84 return 0; 85}

試したこと

デバッガ lldbを使って、segmentation fault と表示される原因を探った
callocが成功した場合には、取得した先頭のアドレスが確保されるはずなので、そのアドレスを表示させるデバッグソースを記述してテストした

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

(ターミナル上での出力)
$clang -v
Apple clang version 13.0.0 (clang-1300.0.29.3)
Target: arm64-apple-darwin20.6.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin
$lldb -v
lldb-1300.0.32.2
Swift version 5.5-dev

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

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

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

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

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

jimbe

2023/03/20 09:29 編集

printf は内部のバッファによりその行を実行しても即表示されるとは限りません。 fflash(stdout); を入れる等して、どこで止まっているのかを確実に確認してください。 そして、ファイル内の行数をカウントした後(カウントもこれでは間違っている気がしますが)、そのままファイルを読もうとしても fp は既にファイルの最後に達していて fgets で読めるデータは残っていないのではありませんか。 読めるデータが無かった場合どうなるでしょうか。
ozwk

2023/03/20 09:30

NUM_LETTERSが定義されていないので動きません
shocyu

2023/03/20 13:44

ozwk様、ご指摘ありがとうございます。ただいまコードの先頭に #define NUM_LETTERS 16 の1行を加えました
shocyu

2023/03/20 14:14

jimbe様、ただいま解決しました。 ご指摘の通り、ファイル内の行数をカウントするためにfgets関数を使いましたが、そこでfpがファイルの最後に達していました。それをrewind関数でfpをファイルの最初に位置に戻す操作を入れたところ解決しました。 行数をカウントするループも、この状態では1行少ない結果となりました。そちらも修正を加えました。 コメントありがとうございました
shocyu

2023/03/20 14:20

jimbe様 fflush関数の使い方を教えて下さり感謝します。これからデバッグの際に使っていきたいと思います。
guest

回答1

0

ベストアンサー

気づいたおかしいところとしては、
・ファイルを1行読み捨ててから行数を数えているので、numDataは本当の行数より1少なくなる
・ファイルがEOFになってからさらに読んでいる
strncpyでなくstrcpyを使っているのでコピー元がコピー先より長いと次の領域を壊す

fgetsの機能ですが、「前回読んだ続きから読むのか、再度最初から読み直すのか、特に指示しなくても、プログラマの意図を汲んでよしなにしてくれる」という機能はありません。
常に「前回読んだ続きから」です(もちろん初回なら先頭から)。
再度最初から読み直したければ、rewind関数を使います。
https://linuxjm.osdn.jp/html/LDP_man-pages/man3/fseek.3.html

投稿2023/03/20 10:09

otn

総合スコア84555

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

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

shocyu

2023/03/20 14:11

ご回答ありがとうございます。 ご指摘の通り、まずファイルの行数(データ数)を取得する方法に問題がありました。fgets関数は最後に読み込んだ時にNULLを返すので、numDataにカウントされた値が1つ少ない状態でwhileループを抜けてしまっていました。 次にrewind関数を使ってファイルの位置を示すポインタを最初に戻したところ上手く行きました。 csvファイルから構造体にデータを格納することが出来ましたので、データの追加や削除などといった操作を実装していきたいと思います。ありがとうございました
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問