Malloc分配空间是否正确?ARM平台库函数修改结构体成员失效求助
问题背景
遇到结构体传参后修改成员值的问题:示例项目中功能正常,但在Visual Studio Code环境下基于ARM Cortex M3和第三方库开发时功能失效,核心目标是修改结构体中的chip_count成员。
程序验证流程如下:创建特定结构体类型的指针,将该指针的地址传入函数,函数内部通过malloc申请新内存空间,将传入的指针指向该malloc分配的地址,修改该malloc空间中结构体的部分变量值,返回main函数后读取malloc空间的修改后内容,该逻辑本身已验证可行。
指针相关定义说明:vtss_state_t等价于struct vtss_state_s,vtss_inst_t是指向struct vtss_state_s(即vtss_state_t)的指针;vtss_create函数的参数为**vtss_inst_t const inst,即指向vtss_inst_t的指针,函数末尾将malloc返回的地址赋值给(*inst),即可让外部指针指向malloc分配的空间。
第一部分:在线编译器验证测试
先在在线编译器中验证修改传入结构体成员的逻辑,结果正常,相关代码如下:
FILE: state.h
#include <stdint.h> /****************************************************************************** Structures *******************************************************************************/ typedef struct tests { int x; } tests_t; typedef uint32_t u32; // This is the structure with chip_count typedef struct vtss_state_s { int a; int cookie; tests_t u; int port_count; u32 chip_count; // doesnt change in my real program :( } vtss_state_t; // typedef this structure so vtss_inst_t points to it...(?) typedef struct vtss_state_s *vtss_inst_t;
FILE: main.c
/******************************************************************************* Online C Compiler. Code, Compile, Run and Debug C program online. Write your code in this editor and press "Run" button to compile and execute it. *******************************************************************************/ #include <stdio.h> #include <stdlib.h> #include "state.h" #define VTSS_PORT_COUNT 1 #define VTSS_PORTS VTSS_PORT_COUNT // some global structure vtss_inst_t testspace; vtss_inst_t *testspace_ptr; /****************************************************************************** functions *******************************************************************************/ // vtss_inst_get () // const adress another access style void vtss_inst_get (const vtss_inst_t inst) { printf ("\ninst get()"); printf ("\ninst get() adress of inst:%d", inst); inst->a = 1111; } // This function just should proof that it can change the content void vtss_inst_default_set (vtss_state_t * vtss_state) { vtss_state->port_count = VTSS_PORTS; // 1 } //** This is the function which should change "chip_count" // Man soll einen Pointer auf einen Pointer auf die Struktur C<bergeben (wo sich chip_count befindet ) void vtss_inst_create (vtss_inst_t * const inst) { printf ("\r\n**vtss_inst_create()"); vtss_state_t *vtss_state; // temporary pointer ( just points to a structure ) vtss_state_t *vtss_state_2; // just a test compare // pass temporary pointer a new memory space: vtss_state = malloc (sizeof (*vtss_state)); // is this correct? anyway it also doesnt work without this malloc style printf ("\r\n\t malloc=: %d", vtss_state); // pass test temporary pointer a new memory space: vtss_state_2 = malloc (sizeof (*vtss_state_2)); printf ("\r\n\t (test) malloc2: %d", vtss_state_2); //Test: pass the temporary pointer even deeper & change content of structure where it points to // (change variable port_count to 1) vtss_inst_default_set (vtss_state); vtss_inst_default_set (vtss_state_2); //Test:Change values of the structure: vtss_state->a = 1; vtss_state->chip_count = 12121; vtss_state_2->chip_count = 12121; // ** Giving back the new adress from malloc: // Test:(from vtss_state or vtss_state2) (*inst) = vtss_state; //passing adress1 //Test: a should have the correct value: printf ("\r\n\ta=1? a =%d ", (*inst)->a); (*inst) = vtss_state_2; //passing adress2 // Some extra tests: (testspace) = vtss_state_2; // extra Test: pass the adress to a test pointer testspace_ptr = &testspace; // extra Test: pass the adress to a test pointer (*testspace_ptr) = vtss_state_2; // extra Test: pass the adress to a test pointer vtss_state_2->a = 22; // extra Test: change content of test pointer printf ("\r\n\ta=22? a=%d ", (*inst)->a); // } /****************************************************************************** main *******************************************************************************/ int main () { // **The main structure: vtss_state_t state_test; state_test.a = 1234; // pass some test value // **pointer to type of vtss_state_t vtss_inst_t inst; // **pass the adress of the structure!: inst = &state_test; // Test: add a value to a structure of the structure: // (is the same like inst->) state_test.u.x = 123; // Test: inst->chip_count = 888; vtss_inst_get (inst); // Test: I declared two global structure to test global access: printf ("\ntestspace points to: %d", testspace); printf ("\ntestspace ptr points to: %d", testspace_ptr); // Test variable a: printf ("\n1234? inst->a=%d", inst->a); // Test: show value of structure in a strucure: printf ("\n123? state_test.u.x=%d", state_test.u.x); // Test: before & after address is changed: printf ("\r\n * Before create(): Adress inst:%d", inst); printf ("\r\n******* Before create():chip_count: %d", inst->chip_count); vtss_inst_create (&inst); printf ("\r\n\n * After create(): Adress of inst:%d", inst); printf ("\r\n\t 22? a:%d", inst->a); printf ("\r\n even the layer2 function in create() worked:\n port_count=1?: %d", inst->port_count); printf ("\r\n\n\n******* chip_count has changes correctly to 12121: %d\n\n\n", inst->chip_count); //extra tests with the global structures: printf ("\ntestspace points to: %d", testspace); printf ("\n testspace has the correct value a: %d", testspace->a); printf ("\n testspace_ptr has the correct value a:: %d", (*testspace_ptr)->a); printf ("\n adress of pointer testspace_ptr points to?:%d", testspace_ptr); // extra test: vtss_inst_get (inst); printf ("\n there is even access to a:%d", inst->a); printf ("\nPE-Hello World"); return 0; }
第二部分:VS Code下ARM Cortex M3平台测试
需要修改chip_count值,但调用库函数vtss_inst_create时无法生效,库函数代码如下:
vtss_rc vtss_inst_create(const vtss_inst_create_t *const create, vtss_inst_t *const inst, void (*print)(void)) { vtss_state_t *vtss_state; vtss_state = malloc(sizeof(*vtss_state)); vtss_state->chip_count = 777; (*inst) = vtss_state; // 后续函数逻辑省略 }
main中调用方式:vtss_inst_create(&create, &inst);
在main中读取chip_count的代码:
sprintf(t2, "\r\n main: chipcount should be 777 %d ??", (int)(inst->chip_count)); print_uart(t2);
打印结果为482531848,为异常值。
之后仿写了多个同逻辑的测试函数,均正常工作:
- 单参数测试函数:
void createtest2(vtss_inst_t *const inst) { vtss_state_t *vtss_state; vtss_state = malloc(sizeof(*vtss_state)); vtss_state->chip_count = 888; (*inst) = vtss_state; }
调用createtest2(&inst);后,读取chip_count值为888,正常。
2. 全参数对齐测试函数:
vtss_rc vtss_inst_create3(const vtss_inst_create_t *const create, vtss_inst_t *const inst, void (*print)(void)) { vtss_state_t *vtss_state; vtss_state = malloc(sizeof(*vtss_state)); vtss_state->chip_count = 222; (*inst) = vtss_state; (void)create; (void)print; return 1; }
调用vtss_inst_create3(&create, &inst, spitest);后,读取chip_count值为222,正常。
只有原库函数无法正常修改chip_count的值,排查发现之前曾修改过库函数的参数列表,新增了一个测试参数,该库函数位于其他独立文件中,即使改回原参数列表问题依然存在。进一步排查发现,同个结构体的cookie成员可以正常修改,仅chip_count成员无法修改,怀疑是CMAKE配置关闭了过多编译警告,屏蔽了潜在错误,运行时才暴露问题,CMAKE编译选项配置如下:
add_target_compile_flags(base PRIVATE "" "-Wno-unused-parameter" "-Wno-pedantic" "-Wno-format" "-Wno-sign-conversion" "-Wno-switch-default" "-Wno-conversion" "-Wno-unused-variable" "-Wno-undef" "-Wno-unused-but-set-variable" "-Wno-unused-function" "-Wno-implicit-function-declaration" "-Wno-uninitialized")
问题根因排查方向
- 头文件定义不一致:库函数所在编译单元引用的
vtss_state_s结构体定义和main函数所在编译单元引用的定义不一致,比如内存对齐参数不同、chip_count的偏移量计算错误,导致修改的地址和实际读取的地址不是同一个位置 - 函数声明不匹配:修改过库函数参数后没有同步更新对外的头文件声明,加上编译选项关闭了
-Wimplicit-function-declaration,编译器不会报错,会默认函数返回int类型,调用时参数入栈规则不匹配,导致传参异常,inst指针指向错误地址 - 内存越界:库函数
malloc之后有其他代码逻辑写越界,覆盖了chip_count所在的内存地址,而cookie成员在结构体中位置靠前,没有被覆盖所以值正常 malloc失败:嵌入式环境下堆空间不足,malloc返回NULL,没有做判空处理,导致对空指针的偏移地址赋值,读取时拿到的是随机内存值
内容的提问来源于stack exchange,提问作者t1fpga1

