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

Posted

技术标签:

【中文标题】访问指向结构的指针会导致内存读取冲突【英文标题】:Accessing a pointer to a struct results in a memory read violation 【发布时间】:2020-12-11 23:58:47 【问题描述】:

最近,由于空闲时间的涌入,我一直在用 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_IVLENNUM_C 是带有ints 或chars 列表的“可变长度数字”。 vlnc_to_vlniVLENNUM_C 转换为 VLENNUM_I,反之亦然 vlni_to_vlnc。输入和输出在指针中,以防传递一个大值,因此本质上返回一个整数,而不是代表结构的一大块数据。在 Visual Studio 2020 中运行代码会导致内存读取错误,我已通过注释指出了该错误;使用 VS 的调试器单步执行代码会产生一些有用的信息,尽管对我来说毫无意义:toconv 充满了某种垃圾数据。例如,包含在结构中的count 变量被替换为随机数,而不是它应该是什么。谁能帮我找出这意味着什么以及如何解决它?

【问题讨论】:

【参考方案1】:

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;

您返回的是局部变量的地址,这是一种快速解决内存错误的方法。操作系统使用 调用栈 来跟踪程序的执行,程序启动时看起来像这样:

[  main: x, y, z ]

调用堆栈的这个块(称为堆栈帧)具有当前函数的地址 (main) 和该函数的局部变量。当您拨打vlni_to_vlnc时:

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

该函数获得自己的堆栈帧,并为自己的本地人提供空间。当您返回 &amp;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))并将结果存储在那里

【讨论】:

在我尝试使用指针然后意识到我正在返回指向一块内存的指针,而不是静态分配列表。谢谢!【参考方案2】:

它崩溃是因为代码在两个函数中通过返回其地址形成了一个指向本地堆栈变量的指针。这些结构要么需要在堆上分配,要么从不同的范围引用。

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

【讨论】:

以上是关于访问指向结构的指针会导致内存读取冲突的主要内容,如果未能解决你的问题,请参考以下文章

C++ 读取访问冲突。 _Val 为 nullptr [关闭]

C++ - 使用 std::sort 对结构向量进行排序导致读取访问冲突

读取 ntdll.dll + offset 会导致访问冲突

C 读取访问冲突 - 指针

Vector::push_back() 给出读取访问冲突

Linux设备驱动程序 之 顺序锁