C 中的 struct ** 与 struct *。解引用和寻址运算符之间的混淆

struct ** vs struct * in C. Confusion between the dereference and address-of operators

我为Android移植了lwIP,并从移植的Unix中提取了一些代码,现在我不明白为什么可以编译出以下逻辑。

有一个函数,一个#define,一个typedef,它们在Unix 的端口中使用。 我把代码写在func.h

#ifndef FUNC_H
#define FUNC_H
#include <stdint.h>

struct sys_sem {
    unsigned int c;
};

#define sys_mutex_t                   sys_sem_t;
typedef struct sys_sem * sys_sem_t;

void sys_arch_sem_wait(struct sys_sem **s, int timeout);

#endif

我还复制了他们的 #define 并粘贴到 sys.h

#ifndef SYS_H
#define SYS_H

#define sys_mutex_lock(mutex)         sys_sem_wait(mutex)
#define sys_sem_wait(sem)                  sys_arch_sem_wait(sem, 0)

#endif

还有我在 Android 中从 Java 调用的函数;它在文件 com_sample_MainActivity.c

#include "func.h"
#include "sys.h"

static sys_mutex_t
mem_mutex;

JNIEXPORT jstring JNICALL Java_com_sample_MainActivity_messageFromNativeCode(
        JNIEnv *env, jobject thisObj) {
    sys_mutex_lock(&mem_mutex);
    sys_mutex_lock(121); // Even this can be compiled
    return (*env)->NewStringUTF(env, "Hello from native code!");
}

sys_mutex_tsys_sem_t的同义词,sys_sem_tsys_sem *的同义词。我对吗? 我的sys_arch_sem_wait签名接受struct sys_sem **s. However, I pass the address ofsys_mutex_twhich after all the#defines means the same as passing asys_sem`,代码编译成功

连这个都能编译

sys_mutex_lock(121); // Even this can be compiled. Why?

所以我可以将任何东西传递给 sys_mutex_lock,不是吗?

我的测试。

#include "func.h"
#include <android/log.h>

void sys_arch_sem_wait(struct sys_sem **s, int timeout) {
    if (s == NULL) {
        __android_log_print(ANDROID_LOG_DEBUG, "Sample", "s == NULL");
    } else {
        __android_log_print(ANDROID_LOG_DEBUG, "Sample", "s != NULL");
    }
    if (*s == NULL) {
        __android_log_print(ANDROID_LOG_DEBUG, "Sample", "*s == NULL"); // It causes the failure
    } else {
        __android_log_print(ANDROID_LOG_DEBUG, "Sample", "*s != NULL");
    }
}

这是 LogCat 的输出:

01-07 14:46:40.695: D/Sample(22086): s != NULL
01-07 14:46:40.695: D/Sample(22086): *s == NULL

预期行为。

我预计

void sys_arch_sem_wait(struct sys_sem **s, int timeout)

应该有不同的签名

void sys_arch_sem_wait(struct sys_sem *s, int timeout)

鉴于提供的宏,这...

sys_mutex_lock(<arg>);

...等同于...

sys_arch_sem_wait(<arg>, 0);

... sys_arch_sem_wait() 的第一个参数被声明为类型 struct sys_sem **.

是的,sys_mutex_tsys_sem_t 的同义词。

是的,sys_sem_tstruct sys_sem * 的同义词。

My signature of sys_arch_sem_wait accepts struct sys_sem **s. However, I pass the address of sys_mutex_t which after all the #defines means the same as passing a sys_sem, and the code is compiled.

没有。您将寻址运算符 (&) 与取消引用运算符 (*) 混淆了。 sys_mutex_t 的地址类型归结为 struct sys_sem **,这是调用的正确类型。

So I can pass anything to sys_mutex_lock, can't I?

C 明确允许将任何整数类型的值转换为任何指针类型,反之亦然。 C 还允许将指向任何对象类型的指针转​​换为指向任何其他对象类型的指针。兼容的 C 编译器 必须 能够编译这样的语句,因为 C 允许它们,我希望任何编译器默认接受它们。

另一方面,这种转换的结果是否有意义或合适是一个完全不同的问题。许多此类转换会产生未定义的行为,而许多其他转换会产生已定义但不需要的行为。因此,任何称职的 C 编译器都应该发出有关此类转换的警告,至少在它们是隐式转换时,前提是此类警告未被手动禁用。

结果是,编译器应该允许您将任何整数或指针参数传递给 sys_mutex_lock(),但如果您尝试传递任何非 [=13 类型的值,它应该警告您=].看来这确实是你所观察到的。

总的来说,我没有发现您描述的任何编译器行为有任何问题。