2

少し戸惑いました。以下は、動作する非常に単純な例です。

#include <stdlib.h>

typedef struct 
{
  unsigned char one: 1;
  unsigned char two:1;
  unsigned char three: 1;
  unsigned char four: 1;
} nibble_bits;

typedef union
{
  unsigned char all : 4;
  nibble_bits bits;
} nibble;

void initArr(nibble ** arrLoc, unsigned int size)
{
  nibble * start = arrLoc[0];
  int i =0;
  for (i=0; i<size; i++)
    {
      start[i].all = 0;
    }
}

int main()
{
  nibble * fourNibbles = (nibble *) malloc(4 * sizeof(nibble));
  initArr(&fourNibbles,4);
}

これは、警告なしで正常にコンパイルされます。ただし、メインの最初の行を変更すると:

nibble * fourNibbles = (nibble *) malloc(4 * sizeof(nibble));

に:

nibble fourNibbles[4];

私は以下を取得します:

警告: main.c: 関数 'main' 内: main.c:150: 警告: 互換性のないポインター型から 'initArr' の引数 1 を渡しています

実行すると、「バス エラー 10」が発生します。

mallocがヒープ上の配列にスペースを割り当て、配列宣言がスタック上にあることを除いて、行は同じことをしているように思えます。しかし(私は思った)どちらの方法でも「fourNibbles」は「ニブルへのポインター」タイプであるため、「fourNibbles」のアドレスはニブルへのポインターへのポインター(ニブル**)になります。

ここで何が欠けていますか?

4

2 に答える 2

3

これらはリモートでさえ同じではありません。これ

nibble * fourNibbles = (nibble *) malloc(4 * sizeof(nibble));

ポインタを宣言しますfourNibblesが、これは

nibble fourNibbles[4];

配列を宣言します。配列とポインタは 2 つの完全に異なるものであり、(オブジェクト レベルでは) 共通点はありません。オブジェクトコンテキスト (&演算子など) でそれらを交換可能に使用しようとすると、災害につながるだけです。このトピックに関する多くの情報は、SO (「配列ポインターの違い」を検索) と、この [事実上の標準] C FAQ: http://c-faq.com/aryptr/index.htmlにあります。

ただし、コードで注意を引くことがもう 1 つあります。あなたの機能

void initArr(nibble ** arrLoc, unsigned int size)

最初の引数としてポインターへのポインターが必要なため、最初のバリアントに合わせて特別に調整されています。配列へのポインターを最初の引数に強制しようとしても機能しません (すでに直接観察する機会がありました)。

ただし、ここでの本当の問題は、initArr関数がそのような奇妙な方法で記述されている理由です。このシーケンス

void initArr(nibble ** arrLoc, unsigned int size)
{
  ...
  nibble * start = arrLoc[0];
  ...
    start[i].all = 0;

かなり珍しいように見えます。通常の単一レベルのポインターではなく、ポインターをポインターに渡すのはなぜですか? たとえば、単に行うことができます

void initArr(nibble *start, unsigned size)
{
  unsigned i;
  for (i = 0; i < size; ++i)
    start[i].all = 0;
}

このバージョンは次のように呼ばれます

initArr(fourNibbles,4); /* note: no `&` operator */

mallocまた、 -ed 配列と明示的に宣言された配列の両方と互換性があります。

PS C言語では、より良いイディオムmalloc

nibble * fourNibbles = malloc(4 * sizeof *fourNibbles);

このバリアント タイプ名nibbleでは、1 回だけ言及されていることに注意してください。

于 2013-09-06T17:24:16.567 に答える
2

配列のアドレスが、式で使用されたときにプレーンな配列名になるポインターとは異なる型を持つことを見逃しています。

あれは:

int *a1 = ...;
int a2[] = { ... };

some_func(&a1);
some_func(&a2);

some_func()を期待しない限り、 は正しくありませんvoid *。最初の呼び出しは、int **— へのポインタへのポインタを渡しintます。2 番目の呼び出しは、int (*)[]— の配列へのポインタを渡しますint&を配列からドロップします。

ただし、コードでは、問題はより複雑です。関数は を想定しているため、nibble **問題があります。あなたがすべきことは、次を渡すことnibble *です:

void initArr(nibble *arrLoc, unsigned int size)
{
    for (unsigned int i = 0; i < size; i++)
        start[i].all = 0;
}

int main(void)
{
    nibble *fourNibbles_1 = (nibble *) malloc(4 * sizeof(nibble));
    nibble fourNibbles_2[4];
    initArr(fourNibbles_1, 4);
    initArr(fourNubbles_2, 4);
    initArr(&fourNubbles_2[0], 4);
}

あなたの実際のコードは、かなり奇妙なことをしています。それがどれだけのダメージを与えているかは、ポインタがnibble.

于 2013-09-06T17:19:02.247 に答える