C言語プログラムの製作

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
dorian

C言語プログラムの製作

#1

投稿記事 by dorian » 11年前

こんにちは
C言語を始めたばかりのものです。
下のプログラムは最大100人で(実際は101人)まで少ない人数で5人か10人くらい抽選で選ぶプログラムの製作途中の段階です。
Visual C++2010で作っています。
とりあえずinfor関数を使ってscanfで入力した文字で配列の中身を表示しようとするプログラムを作っていますが、コンパイルして適当な文字を入力してEnterを押したところ
抽選.exe の 0x53a813af (msvcr100d.dll) でハンドルされていない例外が発生しました: 0xC0000005: 場所 0x00000073 を読み込み中にアクセス違反が発生しました。
と出て出来ません。もしかしたら初歩的な間違いかも知れませんが回答お願いします。

#include<stdio.h>

void infor(char name[]){
int j=0;
for(j=0;j<=100;j++){
printf("%s ,",name[j]);
}
}

int main(void){
int count,i;
char name[100];
count=1;
i=0;
while(i<=100){
printf("%d人目:",count);
scanf("%s",&name);
infor(name);
i++;
}
return 0;
}

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: C言語プログラムの製作

#2

投稿記事 by みけCAT » 11年前

コードを提示するときはBBcodeを有効にした状態でcodeタグで囲み、
かつ適切なインデントをしていただけると、見やすくて助かります。

コード:

#include<stdio.h>

/* どれくらいのテキストが入力されるかわからないので、バッファを多めに取っておく */
#define BUFFER_MAX 100000

void infor(char name[][BUFFER_MAX]){ /* main関数のname変数の型に合わせて修正 */
	int j=0;
	for(j=0;j<=100;j++){
		/* 元のコードではここでアドレスとして不正な値が渡されるので、アクセス違反になる */
		printf("%s ,",name[j]);
	}
}

int main(void){
	int count,i;
	/* 101人分のデータを読み込むなら、要素は101個確保しないと領域外アクセスを起こす */
	/* 大きい配列はstaticまたはグローバル変数(またはmallocなどで動的確保)にしないと、
	 * スタックオーバーフローを起こす可能性が高くなる */
	static char name[101][BUFFER_MAX];
	count=1;
	i=0;
	while(i<=100){
		printf("%d人目:",count);
		scanf("%s",name[i]); /* %sは文字列を読み込む領域の先頭を指すchar *型データを要求する */
		infor(name);
		i++;
		count++; /* せっかくcount変数があるのに、更新しないのは不自然(致命的ではない) */
	}
	return 0;
}
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

dorian

Re: C言語プログラムの製作

#3

投稿記事 by dorian » 11年前

ありがとうございます。
おかげで情報が表示出来、制作を進めてみたところあともうちょっとなんですが、抽選を行うと重複して結果が出てしまうことがあり、重複を出さないようにするためにはどうしたらよいですか?

コード:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define BUFFER_MAX 10000000

void infor(char name[][BUFFER_MAX]){//名簿の中身を表示
	int j;
	for(j=0;j<=99;j++){
		printf("%s,",name[j]);
	}
	printf("\n");
}

void sel(char name[][BUFFER_MAX],int sele){//抽選するための関数
	int a,k;
	int selectbox[50];
		for(a=0;a<50;a++){
		if(strcmp(name[a],"")==0){//nameで配列データがなくなるまでaをカウント
			a=a-1;//aのままではデータがない配列まで差しているので引いておく
			break;
			}
		}
		puts("結果");
	for(k=0;k<sele;k++){//抽選を行う。ここが間違いだと思うが、重複して出てしまうことがあり、重複を出さない方法がわからない
		selectbox[k]=rand()%a;
	printf("%s\n",name[selectbox[k]]);
	}
}


int main(void){
	int count;//登録するごとに人数をカウント
	int i;//名簿の配列リストのカウント
	char select[10];//gets関数を使って数字以外の文字を入力したときにエラーを出すための文字列変数
	int sele;//select変数で数字が入力されたときに格納
	static char name[100][BUFFER_MAX];//名簿:配列ごとに名前の登録
	count=1;
	i=0;

	while(1){
	printf("何人抽選しますか?:");
	gets(select);
	sele=atoi(select);//atoi関数でchar型からint型に変換
	if (1 <= sele && sele <= 50)
	break;
	else
	printf("抽選人数は1~50人でお願いします。\n");//1~50以外の数字、文字を入力したときにエラー
	}
	
	puts("抽選希望の登録を始めます。");
	puts("抽選を開始するなら「抽選」と入力してください");

	while(i<=99){
	printf("%d人目:",count);
    scanf("%s",name[i]);

	if(strcmp(name[i],"抽選")==0&&i-1<sele){//抽選人数<抽選希望人数にするためにエラーを出す
	printf("抽選希望人数は%d人以上でなくてはいけません。\n",sele+1);
	continue;
	}

	if(strcmp(name[i],"抽選")==0){//抽選人数<抽選希望人数のときにsel関数で抽選開始
	sel(name,sele);
	continue;
	}

	if(i==99){//登録した人数が100人に達したとき
	puts("これ以上登録できません。抽選を開始します。");
	sel(name,sele);
	continue;
	}

	infor(name);//人を登録していくと毎にname配列の状態を確認
	i++;
	count++;
	}
	return 0;
}

アバター
ookami
記事: 214
登録日時: 14年前
住所: 東京都

Re: C言語プログラムの製作

#4

投稿記事 by ookami » 11年前

とりあえず以下の方法を思いつきました。
案1.抽選のたびに重複をチェックして、重複していれば再度抽選する
案2.selectbox[0]=0,selectbox[1]=1,...selectbox[a]=a を用意してランダムに何回か並べ替えた後、selectbox[0]~selectbox[sele-1]までを当選、とする

「puts("結果");」の後を以下のように変更します。

案1

コード:

for(k=0;k<sele;k++){
    selectbox[k]=rand()%a;
    
    for(int l=0;l<k;l++) {
		// 重複が見つかったら一つ戻ってもう一度抽選
		if(selectbox[k]==selectbox[l]) {
			k--;
			continue;
		}
	}
    
    printf("%s\n",name[selectbox[k]]);
}
案2

コード:

for(k=0;k<=a;k++){ // selectbox[0]=0,selectbox[1]=1,...selectbox[a]=a を用意
    selectbox[k]=k;
}
for(k=0;k<99;k++) { // 何回かランダムに並べ替え
	int i,j,tmp;
	i=rand()%a;
	j=rand()%a;
    tmp=selectbox[i];
    selectbox[i]=selectbox[j];
    selectbox[j]=tmp;
}
for(k=0;k<sele;k++){ // selectbox[0]~selectbox[sele-1]までを当選
    printf("%s\n",name[selectbox[k]]);
}
こんな感じかなと。
他にも方法はあるとは思いますが(当選した人をname配列から除外するとか)、案ということで。

なお、rand()%a の結果は 0~(a-1)なので、元のプログラムだと最後の人が当選しないかなと思いました。
今回の回答ではその辺を考慮しませんでしたが、ご確認いただければと思います。

dorian

Re: C言語プログラムの製作

#5

投稿記事 by dorian » 11年前

ありがとうございます。とても参考になりました。

閉鎖

“C言語何でも質問掲示板” へ戻る