抱歉,我无法发布任何可重现的代码,因为它是大型项目代码的一部分。
片段是
struct res_data *getRes(int id) {
struct res_data *ret = malloc(sizeof(*ret));
/*
* res.aa = getAA(), etc...
*/
// print ret got: 0x55ffb23ce000
fprintf(stderr, "return: ret: %p\n", ret);
return ret;
}
Run Code Online (Sandbox Code Playgroud)
然后
struct res_data *data = getRes(id);
fprintf(stderr, "got: %p\n", data);
// print data got: 0xffffffffb23ce000
Run Code Online (Sandbox Code Playgroud)
这是为什么?有什么可能的原因吗?
在回答中转移评论。
我打赌你没有getRes()在定义它的地方和使用它的地方都使用的头文件中正确声明,所以使用它的地方认为它int来自32 位getRes(),然后它签名 -扩展以创建保存在data.
两个值的最后 8 个十六进制数字 (B23CE000) 相同,但原始指针getRes()在前 4 个字节中有一些零位,但在调用代码中的所有位均为 1,表示符号扩展。
道德:确保你有准确的原型。让你的编译器坚持他们。注意它的警告!
这是一些重现问题的代码 - 但对我来说却是毛骨悚然。我通常不会发布这样的代码。
main31.c#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
// Incorrect declaration, but what the compiler infers, more or less
// extern int getRes(int id);
int main(void)
{
struct res_data *data = (struct res_data *)getRes(31);
fprintf(stderr, "%6s: 0x%.16" PRIXPTR "\n", __func__, (uintptr_t)data);
//free(data); // free fails because the pointer is incorrect
return 0;
}
Run Code Online (Sandbox Code Playgroud)
getres31.c#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
struct res_data
{
int id;
char part2[32];
double part3;
};
extern struct res_data *getRes(int id);
struct res_data *getRes(int id)
{
struct res_data *ret = malloc(sizeof(*ret));
ret->id = id;
ret->part2[0] = '\0';
ret->part3 = 0.0;
fprintf(stderr, "%6s: 0x%.16" PRIXPTR "\n", __func__, (uintptr_t)ret);
return ret;
}
Run Code Online (Sandbox Code Playgroud)
这段代码本质上是干净的,除了 的声明getRes()应该在getres31.c和 中包含的标头中main31.c。
通常,我使用-Werrorset 进行编译,因此警告会导致编译失败。
$ make so-6051-9209-a
gcc -O3 -g -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -c -o main31.o main31.c
main31.c: In function ‘main’:
main31.c:10:48: warning: implicit declaration of function ‘getRes’; did you mean ‘gets’? [-Wimplicit-function-declaration]
10 | struct res_data *data = (struct res_data *)getRes(31);
| ^~~~~~
| gets
main31.c:10:29: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
10 | struct res_data *data = (struct res_data *)getRes(31);
| ^
gcc -O3 -g -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -c -o getres31.o getres31.c
gcc -o so-6051-9219-a -O3 -g -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes main31.o getres31.o
$
Run Code Online (Sandbox Code Playgroud)
运行时,输出会有所不同:
$ ./so-6051-9219-a
getRes: 0x00007FDF41D02620
main: 0x0000000041D02620
$ ./so-6051-9219-a
getRes: 0x00007F868AC02AA0
main: 0xFFFFFFFF8AC02AA0
$
Run Code Online (Sandbox Code Playgroud)
有时,接收到的值main()被解释为正数,有时被解释为负数,但该值是符号扩展的。
解决方法是将 的声明getRes()放入头文件中,并将该头文件包含在两个源文件中。
main37.c#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include "getres37.h"
int main(void)
{
struct res_data *data = getRes(31);
fprintf(stderr, "%6s: 0x%.16" PRIXPTR "\n", __func__, (uintptr_t)data);
free(data);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
请注意,这次释放数据是安全的。
getres37.c#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include "getres37.h"
struct res_data
{
int id;
char part2[32];
double part3;
};
struct res_data *getRes(int id)
{
struct res_data *ret = malloc(sizeof(*ret));
ret->id = id;
ret->part2[0] = '\0';
ret->part3 = 0.0;
fprintf(stderr, "%6s: 0x%.16" PRIXPTR "\n", __func__, (uintptr_t)ret);
return ret;
}
Run Code Online (Sandbox Code Playgroud)
getres37.h#ifndef GETRES37_H_INCLUDED
#define GETRES37_H_INCLUDED
extern struct res_data *getRes(int id);
#endif /* GETRES37_H_INCLUDED */
Run Code Online (Sandbox Code Playgroud)
$ make so-6051-9219-b
gcc -O3 -g -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -c -o main37.o main37.c
gcc -O3 -g -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -c -o getres37.o getres37.c
gcc -o so-6051-9219-b -O3 -g -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes main37.o getres37.o
$
Run Code Online (Sandbox Code Playgroud)
没有警告!
$ ./so-6051-9219-b
getRes: 0x00007FBA11600700
main: 0x00007FBA11600700
$ ./so-6051-9219-b
getRes: 0x00007FED23C02AA0
main: 0x00007FED23C02AA0
$
Run Code Online (Sandbox Code Playgroud)
这一次,没有问题;中指针main()的值与 中的值相匹配getRes()。
请注意,标头声明了一个不透明(不完整)类型struct res_data并声明了一个函数,该函数返回指向该类型的指针。该main()功能无法取消对它的引用返回指针-它不知道(或需要知道)结构的细节。这隐藏在包含getRes(). 这就是 C 支持信息隐藏的方式。使用不透明结构类型比有时看到的替代方案安全得多,后者在void *任何地方都使用。这是危险的,因为它不提供类型安全,不像不透明的结构类型。
另请注意,创建 MCVE(最小、完整、可验证示例)(或 MRE 或 SO 现在使用的任何名称)或 SSCCE(简短、自包含、正确示例)很容易。它真的根本不需要太多代码。但它确实让生活变得更轻松——我们不必做猜测!
JFTR:在运行 macOS Mojave 10.14.6 的 MacBook Pro 上进行测试,使用 GCC 9.2.0 和 Xcode 11.3.1。