简体   繁体   English

访问指向结构的指针会导致内存读取冲突

[英]Accessing a pointer to a struct results in a memory read violation

Recently I've been programming a lot in C, due to an influx of free time.最近,由于空闲时间的涌入,我一直在用 C 编程。 As C (in my opinion) is a very difficult language to pick up and learn, I make mistakes and memory leaks left and right, occasionally getting stuck on a seemingly unfixable problem, like I have right now.由于 C(在我看来)是一种非常难以掌握和学习的语言,我会犯错误和左右内存泄漏,偶尔会卡在一个看似无法解决的问题上,就像我现在一样。

My code is intended to be a naïve Bignum implementation: have a bit for signing a number, a list of integers, and a counter for how many ints are actually there.我的代码旨在成为一个简单的 Bignum 实现:有一些用于对数字进行签名、一个整数列表和一个用于表示实际存在多少个整数的计数器。 Because two quintillion isn't enough!因为两个 quintillion 是不够的!

Anyways, this is my code:无论如何,这是我的代码:

#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 and VLENNUM_C are "variable length numbers" with lists of int s or char s. VLENNUM_IVLENNUM_C是带有int s 或char s 列表的“可变长度数字”。 vlnc_to_vlni converts a VLENNUM_C to a VLENNUM_I , and vice versa for vlni_to_vlnc . vlnc_to_vlni转换一个VLENNUM_CVLENNUM_I ,反之为vlni_to_vlnc Inputs and outputs are in pointers in case a large value is passed so essentially an integer is returned instead of a large chunk of data representing a struct.输入和输出在指针中,以防传递大值,因此本质上返回一个整数,而不是表示结构的大块数据。 Running the code in Visual Studio 2020 results in a memory read error where I have pointed it out with a comment;在 Visual Studio 2020 中运行代码会导致内存读取错误,我已用注释指出; stepping through the code with VS's debugger results in some helpful albeit meaningless to me information: toconv is filled with some kind of garbage data.使用 VS 的调试器逐步执行代码会产生一些有用但对我来说毫无意义的信息: toconv充满了某种垃圾数据。 For example, the count variable contained inside the struct is replaced with a random number instead of what it should be.例如,包含在结构中的count变量被替换为随机数而不是它应该是的。 Can someone help me find out what this means and how to fix it?有人可以帮我找出这意味着什么以及如何解决它吗?

Both vlnc_to_vlni and vlni_to_vlnc have a fatal flaw: vlnc_to_vlnivlni_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;
}

You're returning the address of a local variable, which is a quick way to end up with memory errors.您正在返回一个局部变量的地址,这是一种以内存错误告终的快速方法。 The operating system keeps track of program execution using a call stack , which looks something like this when the program starts:操作系统使用调用堆栈跟踪程序执行,当程序启动时,它看起来像这样:

[  main: x, y, z ]

This chunk of the call stack (called a stack frame ) has the address of the current function ( main ) and that function's local variables.这块调用堆栈(称为堆栈帧)具有当前函数( main )的地址和该函数的局部变量。 When you call vlni_to_vlnc :当您调用vlni_to_vlnc

[ main: x, y, z ][ vlni_to_vlnc: result, drtc, i, c, masks ]

That function gets its own stack frame with space for its own locals.该函数获得自己的堆栈帧,并为自己的本地人提供空间。 When you return &result , you're returning this address:当您返回&result ,您将返回此地址:

[ main: x, y, z ][ vlni_to_vlnc: result, drtc, i, c, masks ]
                                 ^^^^^^

But the stack frame goes away when the function ends, which leaves you with a pointer like this:但是当函数结束时堆栈帧消失了,这给你留下了一个像这样的指针:

[ main: x, y, z ]                [????]
                                 ^^^^^^

When you call vlnc_to_vlni , its stack frame goes where vlni_to_vlnc 's was:当您调用vlnc_to_vlni ,它的堆栈帧会转到vlni_to_vlnc所在的vlni_to_vlnc

[ main: x, y, z ][ vlnc_to_vlni: result, drtc, i, c, masks ]
                                 ^^^^^^ whoops!

In short, your VLENNUM_I * is pointing into the newly allocated stack frame, which you then write to -- so the data you expect to be there is being changed.简而言之,您的VLENNUM_I *指向新分配的堆栈帧,然后您将其写入 - 因此您期望的数据正在更改。

The solutions in this case would be to do one of the following:在这种情况下,解决方案是执行以下操作之一:

  • return the struct by value按值返回结构
  • dynamically allocate it with malloc and then free it later使用malloc动态分配它,然后稍后释放它
  • take the result pointer as an argument (eg vlnc_to_vlni(VLENNUM_C *toconv, VLENNUM_I *out) ) and store the result there将结果指针作为参数(例如vlnc_to_vlni(VLENNUM_C *toconv, VLENNUM_I *out) )并将结果存储在那里

It is crashing because the code forms a pointer to a local stack variable in both functions by returning its address.它崩溃是因为代码通过返回其地址在两个函数中形成一个指向本地堆栈变量的指针。 The structures either need to be allocated on the heap or referenced from a different scope.这些结构要么需要在堆上分配,要么从不同的范围引用。

#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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM