0

Cの初心者によくあることですが、配列、ポインター、配列のポインターを理解するのに問題があります。残念ながら、ここで提供される情報は、すべて「より簡単な」問題を扱っているため、あまり役に立ちませんでした。これは私のコードです:

/* random.c */
#include <stdio.h>
#include <time.h>
#include <stdlib.h>

int main(){
    double particles[4][22];
    int seed,i,x,y;
    double px_buf, py_buf, pz_buf;

    seed=time(NULL);
    srand(seed);

    /* The random numbers are generated between 1E-12 and 10E-12 */
    /*Double precision floats support up to 15 decimal places*/
    for(i=0;i<20;i++){
        px_buf=((double)rand()/RAND_MAX)*9001E-15;
        py_buf=((double)rand()/RAND_MAX)*9001E-15;
        pz_buf=((double)rand()/RAND_MAX)*9001E-15;
        particles[0][i]=px_buf;
        particles[1][i]=py_buf;
        particles[2][i]=pz_buf;
        printf("(step: %i) The following noise momentum was generated: (%.15E,%.15E,%.15E)\n",i,px_buf,py_buf,pz_buf);
    }

    sscanf("p[20] = nullvector(45.0000000000106,33.03951484238976,14.97124733712793,26.6317895033428)", \
    "p[20] = nullvector(%lf,%lf,%lf,%lf)",&particles[3][20],&particles[0][20],&particles[1][20],&particles[2][20]);

    for(y=0;y<22;y++){
        for(x=0;x<3;x++){
            printf("%.15E \t", particles[x][y]);
        }
        printf("\n");
    }
    return 0;
}

このコードは正常に機能しますが、ご覧のとおり、最後の4つの(y = 21)配列エントリは「空」であり、sscanf行で行ったのと同じ方法で入力したいと思います。

sscanfの部分を適切なパーサー関数に置き換えたいのですが、ポインターを正しく渡す方法、特にsscanfのアドレス(&)単項演算子を使用する方法がまったくわかりません。これは私の予備的なパーサー関数です:

/* parse.c */
void parser(char *input, double **particles){
    sscanf(input, "p[20] = nullvector(%lf,%lf,%lf,%lf)", \
    &particles[3][20],&particles[0][20],&particles[1][20],&particles[2][20]);
    printf("energy: %E, p: (%E, %E, %E)\n",particles[3][20], \
    particles[0][20],particles[1][20],particles[2][20]);
}

ご覧のとおり、私は主に「nullvector(」が前に付いた4つのdoubleに関心があり、文字列からそれらの値を取り出して、マルチアレイ「パーティクル」の21番目の「行」に書き込みます。

しかし、私が追加すると

#include "parse.c"
(...)
parse("p[20] = nullvector(45.0000000000106,33.03951484238976, \
14.97124733712793,26.6317895033428)",particles);

main関数に対して、次のエラーが発生します。

[darillian@quantumbox rng]$ gcc random.c -Wall -pedantic -o ../../bin/random 
random.c: In function ‘main’:
random.c:29:2: warning: passing argument 2 of ‘parse’ from incompatible pointer type [enabled by default]
In file included from random.c:4:0:
parse.c:1:6: note: expected ‘double **’ but argument is of type ‘double (*)[22]’

私は何が間違っているのですか?; D

4

1 に答える 1

1

パーティクルを表すベクトルのサイズを事前に知っているので、配列のインデックスを逆にする方が理にかなっています。

#define NUM_PARTICLES 22
/* ... */
double particles[NUM_PARTICLES][4];

これにより、解析関数が書きやすくなります。

void parser(char *input, double particles[][4]){
  sscanf(
    input, 
    "p[20] = nullvector(%lf,%lf,%lf,%lf)", 
    &particles[20][3], &particles[20][0],
    &particles[20][1], &particles[20][2]
  );
}

これにより、コンパイルして必要な処理を実行できます。追加のボーナスとして、パーサーはフィールドにある粒子の数を知る必要はありません。代わりに、パーティクルがどのように表現されているかを知る必要があるだけです(そうあるべきです)。

パーサーがフィールドにある粒子の数を知る必要があるという事実は、何かがおかしいという大きなコードの臭いでした。プログラムをよりわかりやすく変更することで、エラーも処理されます。

ヒントとして、次のような2次元配列を考えてみてください。

double arr[rows][columns];

新しいファイルは、GCC4.6を搭載したLinuxでエラーなしでコンパイルされます。

明確化

パーサー関数に列数を指定する必要があるのはなぜですか?

そうしないと、関数には、指定された値へのオフセットを計算するのに十分な情報がないためです。

1次元配列は次のようになります。

|value0|value1|value2|...

の幅を指定するには、これが配列valueであることをコンパイラに通知します。double

したがって、このアレイにアクセスするときarr[17]、コンピュータが実際にこれを行うと言います。

address of value (bytes) = 17 * width of double (bytes)

ここで、多次元配列について考えてみましょうdouble arr[17][2]

|va0|vb0|va1|vb1|va2|vb2|...

たとえばarr[3][1]、値にアクセスしようとすると、コンピュータは次の計算を実行します。

address of value (bytes) = (3 * width of inner array) + (1 * size of element)

どこ

width of inner array = no of elements * size of elements. 

上記の場合、内部配列の要素の数は2であり、それらのサイズはdoubleのサイズです。

したがって、値の実際のアドレスを見つけるには、内部配列(「列」)の幅が必要です。そうしないと、インデックス作成時にオフセットを計算できません。

これは、Cが行優先の順序を使用するためです。

于 2013-03-26T17:28:47.823 に答える