C指针“;类型**名称";对;键入*名称[]”;作为论据

C指针“;类型**名称";对;键入*名称[]”;作为论据,c,arrays,pointers,types,malloc,C,Arrays,Pointers,Types,Malloc,我有点困惑。下面是一个非常简单的例子: #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;

我有点困惑。下面是一个非常简单的例子:

#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);
}
致:

我得到以下信息:

警告:main.c:在函数“main”中: main.c:150:警告:从不兼容的指针类型传递'initArr'的参数1

运行时,我得到一个“总线错误10”

在我看来,这些行做的是相同的事情,除了malloc为堆上的数组分配空间,数组声明在堆栈上。但是(我认为)无论哪种方式,“fourNibbles”都是“指向nibble的指针”,因此“fourNibbles”的地址将是指向nibble的指针(nibble**)


这里缺少什么?

您缺少的是数组地址的类型与普通数组名在表达式中使用时变成的指针的类型不同

即:

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

some_func(&a1);
some_func(&a2);
除非
some_func()
需要
void*
,否则不能正确。第一个调用传递一个
int**
——指向
int
的指针;第二个调用传递一个
int(*)[]
——指向
int
数组的指针。从阵列中删除
&

但是,在您的代码中,问题更加复杂。因为函数需要一个
半字节**
,所以您有问题。您应该做的是传递一个
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);
}
void initArr(半字节*arrLoc,无符号整数大小)
{
for(无符号整数i=0;i

您的实际代码正在做一些非常奇怪的事情。它造成的破坏程度可能取决于指针与一个小字节相比有多大,而这两个指针甚至根本不一样。这个

nibble * fourNibbles = (nibble *) malloc(4 * sizeof(nibble));
声明一个指针
fourNibbles
,而

nibble fourNibbles[4];
声明一个数组。数组和指针是两个完全不同的东西,它们(在对象级别)没有任何共同点。试图在对象上下文中互换使用它们(如
&
操作符)只会导致灾难。关于这个主题,这里有很多信息(搜索“数组指针差异”)以及[Fact standard]C常见问题解答:

不过,代码中还有另一个值得注意的地方。你的职能

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;
}
它将与
malloc
-ed数组和显式声明的数组兼容

在C语言中,
malloc
的一个更好的习惯用法是

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

请注意,在此变体类型中,仅提及一次名称
nibble

不要强制转换
malloc
的结果。这是需要在C++中,但它是坏的实践在C(它可能隐藏问题在您的代码)@ @ LorenzoDonati有关的S.Q&A:@ H2CO3谢谢指点这个!我忘了链接到那个有用的信息@洛伦佐多纳蒂,不客气。我喜欢这个答案。为什么数组地址的类型必须与“普通数组名在表达式中使用时变成的指针”不同?数组的地址和指针的地址都是指向相同大小/类型的数据段的指针,并且两个指针本身在内存中的大小应该相同。这是必要的,因为C标准规定数组的地址类型不同于数组的普通名称所变成的指针。区别在于给定的
intx[4]={0,1,2,3};int(*a)[4]=&x;int*b=a;int*c=&a[0]
,使用
b[1]
c[1]
x
的元素进行值为1的寻址,但
a[1]
x
之后的数组进行寻址(该数组未定义,因此不好)。也就是说,
a
指向的对象的大小是
sizeof(int[4])
,但是
b
c
指向的对象的大小是
sizeof(int)
@heisenBug:绝对不是。数组不是指针。当您将
&
应用于数组时,您会得到一个指向数组本身开头的指针(即,该指针在数字上指向第一个元素的位置)。您不会像第一种情况那样获得“指向指针的指针”。因此,您认为“两个指针指向相同类型/大小的数据”的假设是完全错误的。@heisenBug用另一种说法来说明Jonathan所说的:问题不在于这两个指针不指向相同的“内存地址”,而是指针运算需要精确的类型,指针运算也用于索引数组(因为[i]相当于*(a+i))。“+i”表示“按i元素前进”,因此您必须知道元素的大小(这是从类型推断出来的);我试图提供一个简单的例子来说明这个问题。实际应用程序是一个具有多维数组的多线程可怕怪物,但问题仍然是一样的。也许这里的动机不太清楚,但我认为问题是显而易见的。@heisenBug:在这种情况下,也就是说,如果您确实需要一个指向指针的指针参数,您将无法直接将数组作为参数传递。这是不可能的。在这种情况下,如果必须传递一个声明为
nibblefournibbles[4]的数组,您必须声明一个附加指针
nibble*temp=fo
void initArr(nibble *start, unsigned size)
{
  unsigned i;
  for (i = 0; i < size; ++i)
    start[i].all = 0;
}
initArr(fourNibbles,4); /* note: no `&` operator */
nibble * fourNibbles = malloc(4 * sizeof *fourNibbles);