使用地址运算符在 c 函数中传递数组会发出警告

Passing array in a c function using address operator gives a warning

我试图将一个数组传递给一个方法。我尝试了以下方法:

  1. func2(ARRAY_NAME, length) => 有效,无警告
  2. func2(&ARRAY_NAME[0], length) => 有效,无警告
  3. func2(&ARRAY_NAME, length) => 有效,但有警告

我不明白为什么最后一个(#3)给出警告。 &ARRAY_NAME 在 memset、memcpy 等中工作时没有警告。为什么在自定义方法中出现问题?

警告信息:

functionTest.c:35:11: warning: passing argument 1 of ‘func2’ from incompatible pointer type [-Wincompatible-pointer-types]
   35 |     func2(&temp, ARRAY_SIZE);
      |           ^~~~~
      |           |
      |           unsigned char (*)[200]
functionTest.c:8:27: note: expected ‘unsigned char *’ but argument is of type ‘unsigned char (*)[200]’
    8 | void func2(unsigned char* buf, int length)

代码

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


#define ARRAY_SIZE 200

void func2(unsigned char* buf, int length) 
{
    // Change data of any index
    buf[0] = 100;
    buf[10] = 200;
}

void func1()
{
    unsigned char temp[ARRAY_SIZE]; 
    // Initialize
    memset(temp, 0, sizeof(temp));

    for(int i = 0; i < ARRAY_SIZE; i++)
    {
        printf("\t%d", temp[i]);
    }

    printf("\n-----------------------------------------------\n");

    printf("Address : %p\n", &temp);
    printf("Address of 0th index : %p\n", &temp[0]);
    
    printf("\n-----------------------------------------------\n");

    // Pass array in func2  

    func2(&temp, ARRAY_SIZE); // WARNING

    func2(&temp[0], ARRAY_SIZE); // NO WARNING

    for(int i = 0; i < ARRAY_SIZE; i++)
    {
        printf("\t%d", temp[i]);
    }

    printf("\n-----------------------------------------------\n");
}

int main()
{
    func1();
    return 0;
}

错误信息中写得很清楚

functionTest.c:8:27: note: expected ‘unsigned char *’ but argument is of type ‘unsigned char (*)[200]’
    8 | void func2(unsigned char* buf, int length)

该函数需要一个类型为 unsigned char * 的指针,但参数表达式 &temp 的类型为 unsigned char ( * )[200] 并且没有从一种指针类型到另一种指针类型的隐式转换,尽管值为指针相同:数组占用内存范围的地址。

至于函数memsetmemcpy则它们处理void *类型的指针。例如,函数 memset 具有以下声明

void *memset(void *s, int c, size_t n);

指向其他类型对象的指针可以隐式转换为void *类型的指针。

来自 C 标准(6.3.2.3 指针)

1 A pointer to void may be converted to or from a pointer to any object type. A pointer to any object type may be converted to a pointer to void and back again; the result shall compare equal to the original pointer.

func2 被声明为具有 unsigned char * 类型的第一个参数,因此应将其传递给与 unsigned char 兼容的类型的指针或指向 [=13= 的指针], 将自动转换为 unsigned char *.

使用 func2(temp, ARRAY_SIZE),数组 temp 自动转换为指向其第一个元素的指针。这个指针是一个unsigned char *,满足要求

对于func2(&temp[0], ARRAY_SIZE)temp[0]是一个unsigned char,所以&temp[0]是一个指向unsigned char的指针。这样就满足要求了。

func2(&temp, ARRAY_SIZE)一样,&temp是指向unsigned char的200个元素的数组的指针。它指向与&temp[0]相同的位置,只是它的类型不同。它是一个指向数组的指针,而不是指向 unsigned char 或兼容类型的指针,也不是指向 void 的指针。所以不满足要求,编译器报错

指向unsigned char的指针和指向数组的指针是不同的。如果pu的类型是unsigned char *pa的类型是unsigned char (*)[200](指向200个数组的指针unsigned char),则[=1加1 =30=],与 pu + 1 一样,在 pu 之后生成指向下一个 unsigned char 的指针,但是向 pa 添加 1 会生成指向 [= 之后的下一个数组的指针32=]。换句话说,pu + 1 指向内存中的下一个字节,但 pa + 1 指向内存中更远的 200 个字节。

C 类型系统的一个目的是帮助避免错误。当 non-matching 类型被传递给一个函数时,程序员可能会期望一些不同于语言定义的行为。因此编译器发出诊断消息。

memset,它的第一个参数声明为void *void 是一个不完整的类型;它充当其他类型的占位符。 memset 旨在处理任何对象的字节,因此它接受指向任何对象类型的指针。当为 void * 参数传递任何指向对象类型的指针时,它会自动转换为 void *,没有任何诊断消息。