结构 ** 与 C 中的结构 * .取消引用和地址运算符之间的混淆



我移植了 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 of sys_mutex_t which after all the #define s means the same as passing a sys_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 *的同义词。

我的sys_arch_sem_wait签名接受struct sys_sem **s。但是,我传递了sys_mutex_t的地址,毕竟#defines意味着与传递sys_sem相同,并且代码被编译。

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

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

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

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

结果是,

是的,编译器应该允许您将任何整数或指针参数传递给sys_mutex_lock(),但是如果您尝试传递任何不属于 struct sys_sem ** 类型的值,它应该警告您。 看来这确实是你观察到的。

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

最新更新