由于大量的空闲时间,最近我在C语言中进行了大量编程。由于C(我认为)是一种很难掌握和学习的语言,所以我会犯错,并且内存泄漏左右,偶尔会陷入一个似乎无法解决的问题,就像我现在遇到的那样。
我的代码旨在成为一个简单的Bignum实现:有一点用于对数字进行签名,一个整数列表以及一个用于实际存在多少个整数的计数器。因为两百亿还不够!
无论如何,这是我的代码:
#include <stdlib.h>
const unsigned int INTSZ = (int)sizeof(int), CHRSZ = (int)sizeof(char);
typedef struct {
unsigned int negative : 1;
unsigned int count;
unsigned int* list;
} VLENNUM_I;
typedef struct {
unsigned int negative : 1;
unsigned int count;
unsigned char* list;
} VLENNUM_C;
int ceiling(double x) {
return (((int)x) == x) ? (int)x : (int)x + 1;
}
VLENNUM_I* vlnc_to_vlni(VLENNUM_C* toconv) {
VLENNUM_I result = { 0 };
VLENNUM_C drtc = *toconv;
result.negative = drtc.negative;
result.count = ceiling(drtc.count / INTSZ);
result.list = calloc(result.count, INTSZ);
unsigned int i = 0, temp = 0, toprl = 0;
for (; i < drtc.count; ++i) {
temp |= drtc.list[i] << (3 - i % 4) * 8; // Right here
if (i > 0 && !((i + 1) % 4)) {
result.list[toprl] = temp;
temp = 0;
++toprl;
}
}
if (!(i % 4)) result.list[toprl + 1] = temp;
return &result;
}
VLENNUM_C* vlni_to_vlnc(VLENNUM_I* toconv) {
VLENNUM_C result = { 0 };
VLENNUM_I drtc = *toconv;
result.negative = drtc.negative;
result.count = drtc.count * INTSZ;
result.list = calloc(result.count, CHRSZ);
unsigned int i = 0, c = 0, masks[4] = { 255, 65280, 16711680, 4278190080 };
for (; i < drtc.count; ++i)
for (int j = 0; j < 4; ++j)
result.list[(i * 4) + (3 - j)] = (char)((drtc.list[i] & masks[j]) >> (j * 8));
return &result;
}
int main(void) {
VLENNUM_I x = { 0 };
x.count = 1;
x.negative = 0;
x.list = malloc(1 * INTSZ);
x.list[0] = 172639;
VLENNUM_C* y = vlni_to_vlnc(&x);
VLENNUM_I* z = vlnc_to_vlni(y);
return 1;
}
VLENNUM_I
和VLENNUM_C
是“可变长度数字”,其中包含int
s或char
s的列表。 vlnc_to_vlni
将VLENNUM_C
转换为VLENNUM_I
,反之亦然vlni_to_vlnc
。输入和输出位于指针中,以防传递较大的值,因此实际上返回的是整数,而不是表示结构的大量数据。在Visual Studio 2020中运行代码会导致内存读取错误,我在注释中指出了该错误;使用VS的调试器单步执行代码会产生一些有用的信息,尽管对我而言这些信息毫无意义:toconv
充满了某种垃圾数据。例如,结构中包含的count
变量将替换为随机数,而不是应有的随机数。有人可以帮我找出这意味着什么以及如何解决吗?
答案 0 :(得分:2)
vlnc_to_vlni
和vlni_to_vlnc
都有致命缺陷:
VLENNUM_I* vlnc_to_vlni(VLENNUM_C* toconv) {
VLENNUM_I result = { 0 };
// ...
return &result;
}
VLENNUM_C* vlni_to_vlnc(VLENNUM_I* toconv) {
VLENNUM_C result = { 0 };
// ...
return &result;
}
您将返回局部变量的地址,这是结束内存错误的快速方法。操作系统使用调用堆栈跟踪程序的执行情况,该程序在程序启动时如下所示:
[ main: x, y, z ]
这部分调用栈(称为 stack frame )具有当前函数的地址(main
)和该函数的局部变量。致电vlni_to_vlnc
时:
[ main: x, y, z ][ vlni_to_vlnc: result, drtc, i, c, masks ]
该函数获取自己的堆栈框架,并为其自身的本地空间留出空间。返回&result
时,您将返回以下地址:
[ main: x, y, z ][ vlni_to_vlnc: result, drtc, i, c, masks ]
^^^^^^
但是函数结束时,堆栈框架消失了,这给您留下了这样的指针:
[ main: x, y, z ] [????]
^^^^^^
调用vlnc_to_vlni
时,其堆栈框架将移至vlni_to_vlnc
所在的位置:
[ main: x, y, z ][ vlnc_to_vlni: result, drtc, i, c, masks ]
^^^^^^ whoops!
简而言之,您的VLENNUM_I *
指向新分配的堆栈帧,然后将其写入-因此,您期望的数据将被更改。
在这种情况下,解决方案是执行以下操作之一:
malloc
动态分配它,然后在以后释放它vlnc_to_vlni(VLENNUM_C *toconv, VLENNUM_I *out)
)并将结果存储在其中答案 1 :(得分:0)
崩溃是因为代码通过返回其地址在两个函数中形成了指向本地堆栈变量的指针。这些结构要么需要在堆上分配,要么需要从其他范围引用。
#include <stdlib.h>
#include <assert.h>
const unsigned int INTSZ = (int)sizeof(int), CHRSZ = (int)sizeof(char);
typedef struct
{
unsigned int negative : 1;
unsigned int count;
unsigned int* list;
} VLENNUM_I;
typedef struct
{
unsigned int negative : 1;
unsigned int count;
unsigned char* list;
} VLENNUM_C;
int ceiling(double x)
{
return (((int)x) == x) ? (int)x : (int)x + 1;
}
void vlnc_to_vlni(VLENNUM_C* toconv, VLENNUM_I* result)
{
VLENNUM_C drtc = *toconv;
assert(result != NULL);
result->negative = drtc.negative;
result->count = ceiling(drtc.count / INTSZ);
result->list = (unsigned int*)calloc(result->count, INTSZ);
unsigned int i = 0, temp = 0, toprl = 0;
for (; i < drtc.count; ++i)
{
temp |= drtc.list[i] << (3 - i % 4) * 8; // Right here
if (i > 0 && !((i + 1) % 4))
{
result->list[toprl] = temp;
temp = 0;
++toprl;
}
}
if (!(i % 4))
result->list[toprl + 1] = temp;
}
void vlni_to_vlnc(VLENNUM_I* toconv, VLENNUM_C *result)
{
VLENNUM_I drtc = *toconv;
assert(result != NULL);
result->negative = drtc.negative;
result->count = drtc.count * INTSZ;
result->list = (unsigned char*)calloc(result->count, CHRSZ);
unsigned int i = 0, c = 0, masks[4] = {255, 65280, 16711680, 4278190080};
for (; i < drtc.count && result && result->list; ++i)
{
for (int j = 0; j < 4; ++j)
{
int k = (i * 4) + (3 - j);
if (k < drtc.count)
result->list[k] = (char)((drtc.list[i] & masks[j]) >> (j * 8));
}
}
}
int main(void)
{
VLENNUM_I x = {0};
x.count = 1;
x.negative = 0;
x.list = (unsigned int*)malloc(1 * INTSZ);
x.list[0] = 172639;
VLENNUM_C y = {0};
vlni_to_vlnc(&x, &y);
VLENNUM_I z = {0};
vlnc_to_vlni(&y, &z);
return 1;
}