解決済み

配列を返す

  • すぐに回答を!
  • 質問No.5108906
  • 閲覧数241
  • ありがとう数2
  • 気になる数1
  • 回答数13
  • コメント数0

お礼率 13% (9/68)

ファイルから読み込んだ一行の文字列を読み込みカンマごとに区切って
返すというプログラムを関数化することで効率を図りたいと思います。

int main()
{
char buf[1000];
char *str;
char *bufG;
//ファイルを読み込む
 while(fgets(buf,1000,fp) != NULL){//一行ずつ読む
str = buf;//先頭アドレスを指す
    bufG = //文字列を返す関数
 ・
 ・
 ・
}
}

//文字列を返す関数
{
   for(i = 0; *str != ',' && *str != '\0'; i++){
if(*str == '\n'){
bufG[i] = '\0';
}
else{
bufG[i] = *str;
}
str++;
}
bufG[i] = '\0';
return bufG;
}


前の質問で自動変数でこの関数を抜けたら廃棄になるというのは
わかったんですが(そういう警告がでました)
ここからどのようにすれば求めるプログラムになりますか?
引数とかちょとわからないので関数定義を書きませんでした。

(1)ファイルをよみこむ
(2)一行ずつ読み込み文字列をbufにいれる
(3)ポインタstrをbufの先頭アドレスにする
(4)get_word関数にてポインタをずらしていき
カンマがあればそこまでの文字列を返す
(5)main関数に戻り変数に代入する
(6)終端文字があるまで(4)ー(5)を繰り返す。
(7)さらに行数分繰り返す

これらの一連の流れをやりたいのですが
わかりません。

質問者が選んだベストアンサー

  • 回答No.6

ベストアンサー率 52% (391/751)

bufGとstrをパラメータで渡す方法です。
get(char bufG[],char **p_str)の
bufGの定義は理解できるかと思います。
strは関数側でも、参照し、更新するために、strのアドレス(ポインタのポインタ)を渡す必要があります。
以下、実装例です。
------------------------------------
#include <ctype.h>
#include <string.h>
#include <stdio.h> //Linuxなので追加
#include <stdlib.h>
void swap(char p[], char q[]);
void get(char bufG[],char **p_str); //ここを変更

typedef struct {
int number;
char *class_type;
char* name;
char *subject;

} my;
my *data;
int main(int argc, char* argv[])
{
FILE *fp;
int field = 0, line = 0;
char buf[1000];
char bufG[1111]; //こちらに戻し
char *str; //同上

int m;
int line2 = 0;

if((fp=fopen("test3.csv","r"))==NULL){
printf("ファイルが開けません");
}

while(fgets(buf, 1000, fp) != NULL){
line2++;
}
fclose(fp);
printf("%d\n", line2);
if((fp=fopen("test3.csv","r"))==NULL){
printf("ファイルが開けません");
}

data = (my *)malloc(sizeof(my) * line2);

while(fgets(buf,1000,fp) != NULL){
str = buf;
while(*str != '\0'){
get(bufG,&str); //ここを変更
switch(field){
case 0:
data[line].number = atoi(bufG);

break;
case 1:
data[line].class_type = (char *)malloc(strlen(bufG) +1);
strcpy(data[line].class_type, bufG);
break;
case 2:
data[line].name = (char *)malloc(strlen(bufG) + 1);
strcpy(data[line].name, bufG);
break;
case 3:
data[line].subject =(char *)malloc(strlen(bufG) + 1);
strcpy(data[line].subject, bufG);
break;
}
str++;
field++;
}
line++;
field = 0;
}



fclose(fp);
for(m = 0; m < line; m++){ //ここを変更
printf("%d\n", data[m].number);
printf("%s\n", data[m].class_type);
printf("%s\n", data[m].name);
printf("%s\n", data[m].subject);
}
return 0;

}

void get(char bufG[],char **p_str) //ここを変更
{
int i;
char *str; //追加
str = *p_str; //追加

for(i = 0; *str != ',' && *str != '\0' ; i++){
if(*str == '\n'){
bufG[i] = '\0';
}
else{
bufG[i] = *str;
}
str++;
}
bufG[i] = '\0';
*p_str = str; //追加
return ;
}
--------------------------
centos5.3 gcc4.1.2で動作確認済みです。
不明点があれば、補足して下さい。
補足コメント
rooding

お礼率 13% (9/68)

現在、関数はvoid型ですが
char*型で文字列を返すようにはできませんか?
投稿日時 - 2009-07-09 10:34:12

その他の回答 (全12件)

  • 回答No.13

4度目ですが失礼します。char*型で文字列を返す、ですね?それなら
1.サブルーチンでstatic(プログラム終了まで生きる)をつけたchar配列を宣言
2.戻り値の型は char *
3.受け取る変数の方もchar *

《ソ-ス》
#include <stdio.h>

char* test(){
static char hai[20]={"あいうえお"};
return hai;
}

void main(){
printf("%s",test());
}

《結果》
あいうえお

こんな感じです。詳しい情報は↓のサイトにあります。
  • 回答No.12

前回の回答ですが、ソースコードの最後のreturnがretuenになっていました。修正してください。
  • 回答No.11

前の回答の続きです。
読解力ないのであってるかわかりませんが、

1.main関数内でbuf[1000]を宣言。サブル-チンにポインタとして渡す。
2.ファイルの内容を調べていき、 ',' があるまで文字列をbufに入れていき、 ',' があったらbufに '\0' を追加して終了。ファイルの文字が "\0"(終端) だった場合はもう自分(サブル-チン)を呼び出さないようにする。
3.main関数でbufの情報を使い(例としてprintfに出力します)、またサブル-チンを呼び出す。サブル-チンは前回あった ',' の次からファイルを調べ、2.を繰り返す。

このプログラムだとファイルの内容から ',' を抜いたものが画面に出力されます。

《ソ-ス》

#include <stdio.h>

int FileRead(FILE *fp,char *bufS){
int i=0; //カウンタ

while( (bufS[i]=fgetc(fp))!=','){
if(bufS[i++]==EOF){ //ファイルの終端なら
bufS[--i]='\0';
return 1;
}
}
bufS[i]='\0';
return 0;

}


int main(){
char buf[1000]={0}; //0はFileRead()内のル-プの為
FILE *fp=fopen("rrr.txt","r");

while(1){
if(1==FileRead(fp,buf)){ //配列名の指定でbufの先頭ア
//ドレスが渡される(配列はただのポインタ)
printf("%s",buf);
return 0;
}

else{ //ファイルの終端なら↑のif実行、それ以外ならelse
printf("%s",buf);
}
}
retuen 0;
}

《ソ-スここまで》
fgetcを使いました。main関数にある配列bufの先頭アドレスをサブル-チンFileReadに渡してFileRead内でポインタ参照しています。何度も言います配列はただのポインタ、正確には「アドレス定数」というものです。要するに、値の変えられない(定数)ポインタです。下の配列を例にします。

char buf[10];

これが内部ではこうなります。

1.char型かける10のメモリが確保される。
2.const char *buf=[1.]で確保したメモリの先頭アドレス; ←このようなアドレス定数(constのついたポインタそれがアドレス定数であり配列である)が用意される。

関数にbufを渡すとbufの中身(buf[]の先頭アドレス)が渡されます。受け取る側でポインタ型(char *型)で受け取れば、もう自由にFileRead関数内からbufを扱えます。ちなみに配列についてる[]はアドレス計算の演算子です。配列はアドレス定数なので(*hairetsu)+2といった参照の仕方や確保している要素以上の要素数を指定してもコンパイラは何も言いません(少なくともVC++では)。詳しくはポインタについて解説しているサイトを見てみてください。
説明へたでゴメンネ。
  • 回答No.10

配列を戻り値として利用する方法を教えるのでいいですか?まず結論から言います、構造体に配列を入れてその構造体を戻り地にします。
配列を戻り値にできない理由を説明します。構造体を参照する時に構造体名のみを示すと、その構造体全体があるメモリを示していることになります。ここが配列との違いです。配列は要素の型が同じなので、要素1つの型の情報を持っていれば個々の要素にアクセスできます。なので配列はポインタで実装され、配列全体の大きさが分かる情報は持っていません。しかし、構造体は個々の要素の型の情報を持っていないと要素に正しくアクセスできません。なのでポインタでは現せないので、個々の要素の情報を持っています。その情報を使えば構造体全体がどれだけの大きさなのかが分かります。よって構造体には構造体全体を参照できる機能が付いているのです。また最近のコンパイラは最適化が行われるので,変数より構造体のほうが遅いということは少なくなっています。構造体の使用ををためらうことはありません。配列を以下のように

struct A{
char buf[1000];
},bufS

とサブルーチンで宣言し、関数の戻り値の型を struct A にし、返す時は return bufS; として返すとができます。受け取るほうの変数はstruct A型の構造体でもいいし、char bufmain[1000]でもいいと思います。後者は検証してないので分かりませんが。つまり配列1つだけがメンバの構造体は、配列全体の大きさの情報を持っている配列、といえます。でもこんなことをするより配列名(配列名はただのポインタ)を渡して配列の先頭アドレスを渡し、ポインタとしてサブルーチンからmainにあるbuf[1000]を遠隔操作したほうが高速だと多分思いますよ。わからんけど。その方法は次の回答で
  • 回答No.9

ベストアンサー率 55% (772/1382)

ちょっと間違ってました。以下2ケ所を修正します。
p = (char **) malloc(sizeof(char*) * (n + 2)); // +1 → +2
p[n] = NULL; // *p[n] → p[n]

メインのほうね・・・
while(fgets(buf,1000,fp) != NULL){//一行ずつ読む
  char *p[] = bufG(buf);
  for ( int i = 0 ; p[i] ; i++ ) { //1ブロックずつの処理
    //p[i]←ブロックへのポインタ
    printf("%d\n",p[i]); //表示例
  }
  free((void *)p);//ポインタ領域を解放
}
ポインタのポインタなんて、日常茶飯事ですよ。
  • 回答No.8

ベストアンサー率 23% (3656/15482)

え~と,
「現在、関数はvoid型ですが char*型で文字列を返すようにはできませんか?」
「つまりはchar*型で関数を宣言してフィールドの値を返すようにすることですね」
と書かれてますけど, もっときちんと仕様を出してくれないことにはこの辺のリクエストには答えようがありません.
・関数の引数は何ですか?
・関数の返り値は何ですか?
・この関数はどのような機能を持っているのですか?
でも「フィールド」ってなんだろう.
補足コメント
rooding

お礼率 13% (9/68)

いや引数や返り値がわからないので関数を作れないんですよ。
今まで作っていたプログラムの一部をvoid型の関数に押し込むってのは
できます。フィールドはCSVファイルのことを知らない人はわからないかもしれません。関数の機能はさんざんでているとおもいます。
投稿日時 - 2009-07-09 13:06:25
  • 回答No.7

ベストアンサー率 52% (391/751)

>現在、関数はvoid型ですが
>char*型で文字列を返すようにはできませんか?
void get(char bufG[],char **p_str) の
char bufG[]のパラメータが、その主旨になっています。
つまり、このバッファに文字列が返ります。
どうしても、バッファのアドレスをreturnで戻したいのであれば
以下のようにします。(そうする意味がないのでやりたくないのですが・・・)
char *get(char **p_str)として、
static bufG[1111];をこの関数内に確保します。
戻るときに、このバッファのアドレスで戻ります。
return bufG;とします。
呼び出し側は、
char *bufG;として
bufG = get(&str);
とします。
お礼コメント
rooding

お礼率 13% (9/68)

やりたくないことさせてしまって申し訳ないです。
でもありがとう。
投稿日時 - 2009-07-09 11:15:36
  • 回答No.5

ベストアンサー率 52% (391/751)

関数化する時に、最も簡単なのは、パラメータを持たせず、呼び出し側(main)と関数側(get)の両方から参照/更新できる領域を使用する方法です。
以下にその方法で実装したコードを書きます。(前の質問のソースを使用)
----------------------------------
#include <ctype.h>
#include <string.h>
#include <stdio.h> //Linuxなので追加
#include <stdlib.h>
void swap(char p[], char q[]);
void get(void ); //ここを変更
typedef struct {
int number;
char *class_type;
char* name;
char *subject;

} my;
my *data;
char bufG[1111]; //ここで定義する(mainとgetの両方で参照/更新可能となる)
char *str; //ここで定義する(同上)
int main(int argc, char* argv[])
{
FILE *fp;
int field = 0, line = 0;
char buf[1000]; //ここを変更
//char *bufG; //削除
int m; //ここを追加
int line2 = 0;

if((fp=fopen("test3.csv","r"))==NULL){
printf("ファイルが開けません");
}

while(fgets(buf, 1000, fp) != NULL){
line2++;
}
fclose(fp);
printf("%d\n", line2);
if((fp=fopen("test3.csv","r"))==NULL){
printf("ファイルが開けません");
}

data = (my *)malloc(sizeof(my) * line2);

while(fgets(buf,1000,fp) != NULL){
str = buf;
while(*str != '\0'){
get(); //ここを変更
switch(field){
case 0:
data[line].number = atoi(bufG);

break;
case 1:
data[line].class_type = (char *)malloc(strlen(bufG) +1);
strcpy(data[line].class_type, bufG);
break;
case 2:
data[line].name = (char *)malloc(strlen(bufG) + 1);
strcpy(data[line].name, bufG);
break;
case 3:
data[line].subject =(char *)malloc(strlen(bufG) + 1);
strcpy(data[line].subject, bufG);
break;
}
str++;
field++;
}
line++;
field = 0;
}



fclose(fp);
for(m = 0; m < line; m++){ //ここを変更
printf("%d\n", data[m].number);
printf("%s\n", data[m].class_type);
printf("%s\n", data[m].name);
printf("%s\n", data[m].subject);
}
return 0;

}

void get(void) //ここを変更
{
int i;


for(i = 0; *str != ',' && *str != '\0' ; i++){
if(*str == '\n'){
bufG[i] = '\0';
}
else{
bufG[i] = *str;
}
str++;
}
bufG[i] = '\0';
return ; //ここを変更
}
------------------------------------------
linux(centos5.3 gcc4.1.2)で動作確認済みです。
但し、この方法は、今回の質問の趣旨に沿わないかも知れませんが、このようなやり方もあるということを覚えておいて損はありません。
実際に、関数の引数の数が多すぎて、関数の呼び出しのオーバーヘッドが大きい場合は、処理の高速化の為に、パラメータを一切渡さず、両方で、参照更新できる変数を設ける方法をとります。
しかしながら、そうはいっても、パラメータを使用する方法が知りたいと言うのが、質問の趣旨かと思いますので、次の回答で、その方法を書きます。(要はstrとbufG)がパラメータになれば良いわけです。
補足コメント
rooding

お礼率 13% (9/68)

目的は他の人にもこの関数を応用できるようにするということなので
ファイルから読み込んだ一行の文字列の各フィールドを格納する関数にしたいのです。つまりはchar*型で関数を宣言してフィールドの値を返すようにすることですね。
投稿日時 - 2009-07-09 09:33:28
  • 回答No.4

ベストアンサー率 23% (3656/15482)

あなたは「なぜそこでエラーになるのか」を考えたのですか?
ヒント: *p[n] の型は?
  • 回答No.3

ベストアンサー率 31% (1589/5031)

こんなソースコードを書いたところ、これこれこういうエラーが出ました。どこがおかしいのでしょうか?

という質問とともにソースコード全文を載せてくださると、
もしかしたらお力になれたかもしれません。

しかし、今回はそういう状況ではなく単に答えをほしがっているだけのようです。
その場合、残念ですが私は力不足です。お役に立てそうもありません。
大変申し訳ありません。
12件中 1~10件目を表示
AIエージェント「あい」

こんにちは。AIエージェントの「あい」です。
あなたの悩みに、OKWAVE 3,500万件のQ&Aを分析して最適な回答をご提案します。

こんな書き方もあるよ!この情報は知ってる?あなたの知識を教えて!
このQ&Aにはまだコメントがありません。
あなたの思ったこと、知っていることをここにコメントしてみましょう。

その他の関連するQ&A、テーマをキーワードで探す

キーワードでQ&A、テーマを検索する

特集

ピックアップ

ページ先頭へ