ページ 11

関数にしたい

Posted: 2016年10月29日(土) 00:46
by no name
文字列をn個入力して、出力を 0, n/2, 1, n/2+1, ....とするプログラムです。
mainと関数で分割したいのですが、出力部分を関数にする方法を思いつきませんでした。
申し訳ありませんがご教授お願いします。


入力

1 Homecoming King
2 We Don't Have to Dance
3 Ribcage
4 Stay Alive
5 Love was Made to Break
6 Beautiful Pain
7 Put the Gun Down
8 Drown Me Out
9 Paint it Black
10 Break Your Halo
11 Louder Than Your Love
12 Broken Pieces
13 The Void


期待する結果

1 Homecoming King
7 Put the Gun Down
2 We Don't Have to Dance
8 Drown Me Out
3 Ribcage
9 Paint it Black
4 Stay Alive
10 Break Your Halo
5 Love was Made to Break
11 Louder Than Your Love
6 Beautiful Pain
12 Broken Pieces
13 The Void


プログラム本体

コード:

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

#define LINELEN 128

char *Scan_line( char s[] );

int main(void)
{
	int count=0, i=0, j=0;
	
	char s[LINELEN+1];
	char *P_line[LINELEN];
	char *line[LINELEN];
	char *scan_line = NULL;
	char *tmp = NULL;
	
	scan_line = malloc( sizeof(int)*LINELEN );
	tmp = malloc( sizeof(int)*LINELEN );
	
	while (fgets(s, LINELEN, stdin) != NULL) {
		scan_line = Scan_line( s );
		P_line[ count ] = scan_line; 
		line[ count ] = scan_line;
		count++;
	}

/* ここから */

	for( i=0; i<count; i++ ){ line[i] = P_line[i]; }
	for( i=0; i<count; i++ ){
		if( (i+1) % 2 == 0 ){
			line[i] = P_line[count/2 + j];
			j++;
		}
		else{
			line[i] = P_line[i/2];
		}
	}
	for( i=0; i<count; i++){ printf( "%s", line[i]); }

/* ここまで */

	free(scan_line);
	
	return 0;
}
char *Scan_line( char s[] ){
	int i;
	char *line = NULL;
	
	line = malloc( sizeof(char)*(strlen(s)+1) );
	for( i = 0; s[i]!='\0'; i++ ){
		line[i] = s[i];
	}
	line[i] = '\0';
	
	return line;
}

Re: 関数にしたい

Posted: 2016年10月29日(土) 05:41
by あんどーなつ
最初のうちは、「メモリは呼び出し側が用意する」というルールに従ったほうがいいです。
オープンソースのコードでもメモリリークすることが前提で書いているものをたまに見かけますが、
ルール的にはNGです。

そんでもって、コードを直してみようかと思いましたが、no nameさんが自身で考えたほうが
有意義だと思いますので、かわりに私流のSTLのコードを書かせていただきます。

ちなみに動的メモリを確保するならば、
#define MAX_LINE 128
#define MAX_STRLEN 128
char *text = malloc( sizeof(char) * MAX_LINE * MAX_STRLEN );
とか

コード:

char *lines[MAX_LINE];
for (i = 0; i < MAX_LINE; i++)
  lines[i] = malloc( sizeof(char) * MAX_STRLEN );
とかでしょうか。あとは、ファイル入力の場合は文字の総数をカウントするために1回ファイルを
読み込んでしまうという手もあります。今回は標準入力なのでできなさそうですね。あとは、

コード:

char *lines[MAX_LINE];
char s[MAX_STRLEN+1];
for (linecnt = 0; linecnt < MAX_LINE; linecnt++) {
  if (fgets(s, MAX_LINE, stdin) == NULL) break;
  s[MAX_STRLEN] = '\0';
  lines[linecnt] = malloc( sizeof(char) * (strlen(s)+1));
  strcpy(lines[linecnt], s);
}
こんなかんじでしょうか。あまりやりたくないですね。

Re: 関数にしたい

Posted: 2016年10月29日(土) 06:21
by あんどーなつ
約束してしまったので、STLバージョンです。

コード:

#include <iostream>
#include <string>
#include <vector>

using namespace std;

int main() {
  vector<string> v;
  string line;
  while (getline(cin, line))
    v.push_back(line);
  auto i1 = v.cbegin();
  auto i2 = v.cbegin() + v.size()/2;
  auto ie = v.cend() - v.size()%2;
  for (; i2 != ie; ++i1, ++i2)
    cout << *i1 << "\n" << *i2 << endl;
  if (ie != v.cend())
    cout << *i2 << endl;
  return 0;
}


Re: 関数にしたい

Posted: 2016年10月29日(土) 09:57
by かずま
no name さんが書きました:mainと関数で分割したいのですが、出力部分を関数にする方法を思いつきませんでした。
出力部分を関数にするだけなら、簡単です。

コード:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
#define LINELEN 128
 
char *Scan_line( char s[] );
void print(char *line[], char *P_line[], int count, int j);
 
int main(void)
{
    int count=0, i=0, j=0;
    
    char s[LINELEN+1];
    char *P_line[LINELEN];
    char *line[LINELEN];
    char *scan_line = NULL;
    char *tmp = NULL;
    
    scan_line = malloc( sizeof(int)*LINELEN );
    tmp = malloc( sizeof(int)*LINELEN );
    
    while (fgets(s, LINELEN, stdin) != NULL) {
        scan_line = Scan_line( s );
        P_line[ count ] = scan_line; 
        line[ count ] = scan_line;
        count++;
    }
 
/* ここから */
    print(line, P_line, count, j);
/* ここまで */
 
    free(scan_line);
    
    return 0;
}
char *Scan_line( char s[] ){
    int i;
    char *line = NULL;
    
    line = malloc( sizeof(char)*(strlen(s)+1) );
    for( i = 0; s[i]!='\0'; i++ ){
        line[i] = s[i];
    }
    line[i] = '\0';
    
    return line;
}

void print(char *line[], char *P_line[], int count, int j)
{
    int i;
    for( i=0; i<count; i++ ){ line[i] = P_line[i]; }
    for( i=0; i<count; i++ ){
        if( (i+1) % 2 == 0 ){
            line[i] = P_line[count/2 + j];
            j++;
        }
        else{
            line[i] = P_line[i/2];
        }
    }
    for( i=0; i<count; i++){ printf( "%s", line[i]); }
}
でも、元のプログラムにはたくさんの問題があります。
13行目が出力されないとか、tmp が使用されていないとか、
free が malloc に対応していないとか。

私なら次のように書きます。

コード:

#include <stdio.h>   // fgets, fputs
#include <stdlib.h>  // malloc
#include <string.h>  // strlen
 
#define LINELEN 128  // 1つの文字列の長さ(1行の長さ)
#define N_LINES 128  // 文字列の個数(行数)
 
void print(const char *lines[], int count)
{
    int i, j = count / 2, k = j;
    for (i = 0; i < k; i++, j++) {
        fputs(lines[i], stdout);
        fputs(lines[j], stdout);
    }
    if (j < count) fputs(lines[j], stdout);
}

int main(void)
{
    int count, i;
    char s[LINELEN], *lines[N_LINES];
    for (count = 0; count < N_LINES && fgets(s, LINELEN, stdin); count++) {
        lines[count] = malloc(strlen(s) + 1);
        strcpy(lines[count], s);
    }
    print(lines, count);
    for (i = 0; i < count; i++) free(lines[i]);
    return 0;
}

Re: 関数にしたい

Posted: 2016年10月29日(土) 11:03
by あんどーなつ
古き良きCのコードって感じで、美しいです。勉強になります。