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_t
是sys_sem_t
的同义词,sys_sem_t
是sys_sem *
的同义词。我对吗?
我的sys_arch_sem_wait
签名接受struct sys_sem **s. However, I pass the address of
sys_mutex_twhich after all the
#defines 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_t
是 sys_sem_t
的同义词。
是的,sys_sem_t
是 struct 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 类型的值,它应该警告您=].看来这确实是你所观察到的。
总的来说,我没有发现您描述的任何编译器行为有任何问题。
我为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_t
是sys_sem_t
的同义词,sys_sem_t
是sys_sem *
的同义词。我对吗?
我的sys_arch_sem_wait
签名接受struct sys_sem **s. However, I pass the address of
sys_mutex_twhich after all the
#defines 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_t
是 sys_sem_t
的同义词。
是的,sys_sem_t
是 struct sys_sem *
的同义词。
My signature of
sys_arch_sem_wait
acceptsstruct sys_sem **s
. However, I pass the address ofsys_mutex_t
which after all the#defines
means the same as passing asys_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 类型的值,它应该警告您=].看来这确实是你所观察到的。
总的来说,我没有发现您描述的任何编译器行为有任何问题。