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

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

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

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

コンパイル

コンパイルとは、プログラミング言語のテキストソース(ソースコード)をコンピュータ上で実行可能な形式(オブジェクトコード)に変換することをいいます

プログラミング言語

プログラミング言語はパソコン上で実行することができるソースコードを記述する為に扱う言語の総称です。

ハッシュ

ハッシュは、高速にデータ検索を行うアルゴリズムのことです。

Q&A

1回答

5976閲覧

Segmentation fault (コアダンプ)の原因が知りたいです。

maguro_

総合スコア0

C

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

コンパイル

コンパイルとは、プログラミング言語のテキストソース(ソースコード)をコンピュータ上で実行可能な形式(オブジェクトコード)に変換することをいいます

プログラミング言語

プログラミング言語はパソコン上で実行することができるソースコードを記述する為に扱う言語の総称です。

ハッシュ

ハッシュは、高速にデータ検索を行うアルゴリズムのことです。

0グッド

0クリップ

投稿2021/11/21 02:19

前提・実現したいこと

CPU時間を計測するプログラムを作成しています。
普通にコンパイルして実行するとうまくいくのですが。
課題で指示された最適化オプション-O2をつけてコンパイルし、実行すると
Segmentation fault (コアダンプ)と表示されます。
どこに間違いが起きているのか分からず質問させてもらいました。
C言語でプログラムを作成しています。
以下のプログラムをリンクしてコンパイルしました。
#include <stdio.h>
#include <time.h>
#include "iata_db.h"

int main(void)
{
db_t db;
char key[KEY_LEN+1];
char *data;
int i;

db_init(&db);
db_hash_load(&db);

/* 空港コードを入力するとデータを出力; EOFまで繰り返し実行 */
for (;;) {
fprintf(stderr, "key = ");
if (scanf(KEY_FMT, key)==EOF) { break; }
clock_t clk_start, clk_end;
clk_start = clock();
for(i=0; i<=100000; i++)
{
data = db_hash_search(&db, key);
}
clk_end = clock();
fprintf(stderr, "cpu = %11.6f [sec]\n", (double) (clk_end-clk_start)/CLOCKS_PER_SEC);
if (data==NULL) { printf("NO RECORD\n"); }
else { printf("%s => %s\n", key, data); }
}

return 0;
}

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "iata_db.h"
/* c が改行記号かどうか判定するマクロ */
#define IS_EOL(c) ((c)=='\n' || (c)=='\r')

/* iata_db.c の内部だけで使用する関数 */
static void extract_data(char line[], char key[], char data[]);
static void record_set(record_t *r, char key[], char data[]);

void db_init(db_t db)
/
db_t 型変数の初期化 */
{
int i;
db->n = 0;
for (i=0; i<MAX_RECORDS; i++) {
strcpy(db->record[i].key, "");
db->record[i].data = NULL;
}
}

void db_load(db_t db)
/
db_t 型変数にファイル IATA_F からデータを読み込む */
{
FILE *fp;
char line[KEY_LEN+1+DATA_LEN+1];
char key[KEY_LEN+1];
char data[DATA_LEN+1];

/* ファイルのオープン */
if ((fp=fopen(IATA_F, "r"))==NULL) {
fprintf(stderr, "ファイル (%s) が開けません\n", IATA_F);
exit(1);
}

/* データの読み込み /
while (fgets(line, DATA_LEN, fp) != NULL) {
/
入力データ数が収容可能最大レコード数を超えたらエラー /
if (MAX_RECORDS<=db->n) {
fprintf(stderr, "MAX_RECORDS(%d) <= n(%d)\n", MAX_RECORDS, db->n);
exit(1);
}
/
line から key と data を切り出す /
extract_data(line, key, data);
/
db->n 番目のレコードに key と data を格納し, db->n を1増やす */
record_set(&db->record[db->n], key, data);
db->n++;
}

fclose(fp);
}

static void record_set(record_t r, char key[], char data[])
/
r の指すレコードに key と data を書き込む (data は動的割当て) /
{
assert(r!=NULL);
assert(key!=NULL);
assert(data!=NULL);
/
key /
assert(strlen(key)<=KEY_LEN);
strcpy(r->key, key);
/
data /
assert(strlen(data)<=DATA_LEN);
r->data = (char
) malloc(sizeof(char)*(strlen(data)+1));
strcpy(r->data, data);
}

static void extract_data(char line[], char key[], char data[])
/* line から key と data を抽出する*/
{
assert(line!=NULL);
assert(key!=NULL);
assert(data!=NULL);
int k;

/* ファイルの1行が長過ぎて line に収容できていないことがないかのチェック /
/
line の末尾が改行でない → 1行が長過ぎるのでエラー */
if (! IS_EOL( line[strlen(line)-1] ) ) {
fprintf(stderr, "line length exceeded line size\n");
exit(1);
}

/* 先頭の KEY_LEN 文字が key */
int p_key = 0;
for (k=0; k<KEY_LEN; k++) {
key[k] = line[p_key+k];
}
key[k] = '\0';

/* KEY_LEN+1 文字目以降改行記号までが data */
int p_data = KEY_LEN+1;
for (k=0; k<DATA_LEN; k++) {
if (IS_EOL(line[p_data+k])) { break; }
data[k] = line[p_data+k];
}
data[k] = '\0';
}

void db_dump(db_t db)
/
db_t 型変数の全データを出力する */
{
int i;
for(i=0; i<MAX_RECORDS; i++)
{
printf("[ %d]", i);
if(db->record[i].data != NULL)
{
printf("%s %s", db->record[i].key, db->record[i].data);
}
printf("\n");
}
}

char *db_linear_search(db_t *db, char key[])
{
int i;
for(i=0; i<MAX_RECORDS; i++)
{
if(strcmp(key,db->record[i].key)==0)
{
return db->record[i].data;
break;
}
}
return NULL;
}

char *db_binary_search(db_t *db, char key[KEY_LEN+1])
{
int low=0;
int high=db->n-1;
int mid;
for(;;)
{
mid=(high+low)/2;
if(strcmp(key,db->record[mid].key)==0)
{
return db->record[mid].data;
}
else if(strcmp(key,db->record[mid].key)> 0)
{
low = mid + 1;
}
else if(strcmp(key,db->record[mid].key)< 0)
{
high = mid - 1;
}
if(high < low){break;}
}
return NULL;
}

int db_hash_f(char key[])
{
int i;
int v = 0;
for (i=0; key[i]!='\0'; i++) {
v = (v<<5) + (key[i]-'A');
}
return v % MAX_RECORDS;
}

void db_hash_load(db_t *db)
{
FILE *fp;
char line[KEY_LEN+1+DATA_LEN+1];
char key[KEY_LEN+1];
char data[DATA_LEN+1];
int h;
int m = MAX_RECORDS;

/* ファイルのオープン */
if ((fp=fopen(IATA_F, "r"))==NULL) {
fprintf(stderr, "ファイル (%s) が開けません\n", IATA_F);
exit(1);
}

/* データの読み込み /
while (fgets(line, DATA_LEN, fp) != NULL) {
/
入力データ数が収容可能最大レコード数を超えたらエラー /
if (MAX_RECORDS<=db->n) {
fprintf(stderr, "MAX_RECORDS(%d) <= n(%d)\n", MAX_RECORDS, db->n);
exit(1);
}
/
line から key と data を切り出す /
extract_data(line, key, data);
/
db->n 番目のレコードに key と data を格納し, db->n を1増やす */
h = db_hash_f(key);
while(strcmp(db->record[h].key, "")!= 0)
{
h++;
if(h == m){h = h - m;}
}
record_set(&db->record[h], key, data);

}

fclose(fp);
}

char *db_hash_search(db_t *db, char key[KEY_LEN+1])
{
int h;
int i;
int m = MAX_RECORDS;
for(i=0; i<= MAX_RECORDS; i++)
{
if(strcmp(db->record[h].key, key)==0)
{
return db->record[h].data;
break;
}
h++;
if(h == m){h = h - m;}
}
return NULL;
}

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

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

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

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

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

jimbe

2021/11/21 03:56

まず、コードはマークダウン記法を用いてご提示ください。 マークダウンに関しましてはヘルプをご参照ください。 ツール等を利用しないのであれば、printf と fflush のセットを埋め込んでトレースする方法もあります。 [C言語の「fflush関数」を解説!知っておくとデバッグにも役立つよ! - 落ちるプログラムのデバッグ](https://daeudaeu.com/fflush/#i-3 )
guest

回答1

0

コンパイルを実行する時点で原因が判明しますね。。。

bash

1$ gcc -O2 -fsanitize=address -std=gnu18 -Wall -Wextra -g main.c iata_db.c -o main 2iata_db.c: In function ‘db_hash_search’: 3iata_db.c:212:27: warning: ‘h’ may be used uninitialized in this function [-Wmaybe-uninitialized] 4 212 | return db->record[h].data; 5 | ~~~~~~~~~~~~~^~~~~

一応、sanitizer 付きで実行してみると。。。

bash

1$ ./main 2key = ANB 3AddressSanitizer:DEADLYSIGNAL 4================================================================= 5==562958==ERROR: AddressSanitizer: SEGV on unknown address 0x7ffe48acc3e8 (pc 0x7f4e3fa0a448 bp 0x7ffe48945940 sp 0x7ffe489450c0 T0) 6==562958==The signal is caused by a READ memory access. 7 #0 0x7f4e3fa0a448 in __interceptor_strcmp ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:461 8 #1 0x55b35db7f5fe in db_hash_search iata_db.c:210 9 #2 0x55b35db7e5ea in main main.c:24 10 #3 0x7f4e3f7b4564 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x28564) 11 #4 0x55b35db7e77d in _start (main+0x277d) 12 13AddressSanitizer can not provide additional info. 14SUMMARY: AddressSanitizer: SEGV ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:461 in __interceptor_strcmp 15==562958==ABORTING

iata_db.c:210 は以下になります。

c

1char *db_hash_search(db_t *db, char key[KEY_LEN+1]) 2{ 3 int h; 4 int i; 5 int m = MAX_RECORDS; 6 for(i=0; i<= MAX_RECORDS; i++) 7 { 8 if(strcmp(db->record[h].key, key)==0) // <== 210行目 9 { 10 return db->record[h].data; 11 break; 12 } 13 : 14

投稿2021/11/21 05:54

melian

総合スコア19618

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

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

melian

2021/11/21 06:08

使ったことがないので確かなことは言えませんが、VSCode などのモダンな IDE であれば、db_hash_search 関数を表示した時点で同じ様なワーニングメッセージ('h’ may be used uninitialized 〜)が表示されるのではないでしょうか。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

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

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

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問