提问人:Roger Dodger 提问时间:9/12/2023 最后编辑:Vlad from MoscowRoger Dodger 更新时间:9/12/2023 访问量:68
程序运行,但 Valgrind 在尝试写入 malloc 的内存时检测到问题
Program runs but Valgrind detecting a problem when attempting to write to malloc'd memory
问:
为了学习更多 C,我正在尝试重新创建基本数据结构。这是我尝试使用数组的最小示例,该数组可以编译并运行,但 valgrind 检测到问题:
#include <stdlib.h>
#include <stdio.h>
typedef void * vp_t;
typedef struct {
int len;
vp_t *start;
} arr_t;
arr_t * array_new(int len) {
arr_t *arr = malloc(sizeof(arr_t));
arr->start = malloc(len * sizeof(vp_t));
arr->len = len;
return arr;
}
void array_set(arr_t *arr, int i, vp_t vp) {
vp_t *dest = arr->start + i * sizeof(vp_t);
*dest = vp;
}
int array_get(arr_t *arr, int i) {
int *p = *(arr->start + i * sizeof(vp_t));
return *p;
}
void array_delete(arr_t *arr) {
free(arr->start);
free(arr);
}
int main() {
int x=0, y=1, z=2;
arr_t *arr = array_new(3);
array_set(arr, 0, &x);
array_set(arr, 1, &y);
array_set(arr, 2, &z);
for (int i = 0; i < 3; ++i) printf("%i ", array_get(arr, i));
putchar('\n');
array_delete(arr);
return 0;
}
程序按预期输出。但是,valgrind 在我第二次和第三次调用 array_set 函数时检测到问题。针对此处的示例代码运行 valgrind,我得到:1 2 3
==91933== Invalid write of size 8
==91933== at 0x109244: array_set (min.c:22)
==91933== by 0x109312: main (min.c:39)
==91933== Address 0x4a990d0 is 32 bytes before an unallocated block of size 4,194,032 in arena "client"
==91933==
==91933==
==91933== Process terminating with default action of signal 11 (SIGSEGV)
==91933== Access not within mapped region at address 0x2003A98F4C
==91933== at 0x109244: array_set (min.c:22)
==91933== by 0x109327: main (min.c:40)
min.c:22
指在array_set函数中。 指 。Valgrind 没有抱怨第 38 行,.*dest = vp
min.c:39
array_set(arr, 1, &y)
array_set(arr, 0, &x)
我一直在玩 gdb,但我还没有弄清楚。感谢您的观看。
答:
当您需要寻址数组时,只需将多少个单元格(除了单元格大小)添加到指针上即可。
void array_set(arr_t *arr, int i, vp_t vp) {
vp_t *dest = arr->start + i;
*dest = vp;
}
int array_get(arr_t *arr, int i) {
int *p = *(arr->start + i);
return *p;
}
arr->start 的类型为 vp_t,因此当您向指针添加 1 时,编译器将根据需要增加 (sizeof(vp_t)) 以转到下一个单元格。
评论
set()
正在保存结构,而正在返回...好奇。。。get()
int
这是解决方案,但首先:为什么你需要一个 void ** 数组?,如果你想创建一个整数数组,使用 int* 创建,一个 void 数组,它只推荐在你需要任何类型的数组时。 无论如何,你正在创建一个 void*(一维),而实际上,它应该是一个二维数组 void **
#include <stdlib.h>
#include <stdio.h>
typedef struct {
int len;
void **start;
} arr_t;
arr_t * array_new(int len) {
arr_t *arr = malloc(sizeof(arr_t));
arr->start = malloc(len * sizeof(void*));
arr->len = len;
return arr;
}
void array_set(arr_t *arr, int i, void *vp) {
arr->start[i] = vp;
}
int array_get(arr_t *arr, int i) {
return *(int*)arr->start[i];
}
void array_delete(arr_t *arr) {
free(arr->start);
free(arr);
}
int main() {
int x=0, y=1, z=2;
arr_t *arr = array_new(3);
array_set(arr, 0, &x);
array_set(arr, 1, &y);
array_set(arr, 2, &z);
for (int i = 0; i < 3; ++i) printf("%i ", array_get(arr, i));
putchar('\n');
array_delete(arr);
return 0;
}
但是如果你需要一个任何数组(我认为这是使用 void * 的唯一原因),这里是“正确”的实现
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
enum {
UNDEFINED,
STRING,
INTEGER
};
typedef struct {
int len;
void **start;
int *types;
} arr_t;
arr_t * array_new(int len) {
arr_t *arr = malloc(sizeof(arr_t));
arr->start = malloc(len * sizeof(void*));
arr->types = malloc(len *sizeof(int));
//start everything as undefinde
memset(arr->types,UNDEFINED,len);
arr->len = len;
return arr;
}
void array_set_int(arr_t *arr, int i, int value) {
int *element = malloc(sizeof(int));
*element = value;
arr->start[i] =element;
arr->types[i] = INTEGER;
}
void array_set_str(arr_t *arr, int i, char * value) {
arr->start[i] = strdup(value);
arr->types[i] = STRING;
}
int array_get_type(arr_t *arr, int i){
if(i > arr->len){
return -1;
}
return arr->types[i];
}
int array_get_int(arr_t *arr, int i) {
return *(int*)arr->start[i];
}
char * array_get_str(arr_t *arr, int i) {
return (char*)arr->start[i];
}
void array_delete(arr_t *arr) {
for(int i = 0; i < arr->len;i++){
if(arr->start[i]){
free(arr->start[i]);
}
}
free(arr->types);
free(arr->start);
free(arr);
}
int main() {
arr_t *arr = array_new(3);
array_set_int(arr, 0, 10);
array_set_int(arr, 1, 20);
array_set_str(arr, 2, "aaaa");
for(int i = 0; i < arr->len; i++){
int type = array_get_type(arr,i);
if(type == STRING){
char *value = array_get_str(arr,i);
printf("value is %s\n",value);
}
if(type == INTEGER){
int value = array_get_int(arr,i);
printf("value is %d\n",value);
}
}
array_delete(arr);
return 0;
}
评论
main()
set()
int
*vp
get()
**start
memset(arr->types,UNDEFINED,len);
array_get_type()
array_delete()
malloc()
您错误地使用了指针。
例如,让我们考虑函数array_set
void array_set(arr_t *arr, int i, vp_t vp) {
vp_t *dest = arr->start + i * sizeof(vp_t);
*dest = vp;
}
根据 C 标准,像这样的表达式指向数组的元素相对于 ponter 指向的元素pointer + i
i-th
因此,如果你需要在函子中获取指向第 i 个元素的指针,你需要编写
vp_t *dest = arr->start + i;
nstead 的
vp_t *dest = arr->start + i * sizeof(vp_t);
所以 functon 看起来像
void array_set(arr_t *arr, int i, vp_t vp) {
vp_t *dest = arr->start + i;
*dest = vp;
}
请注意,下标运算符的计算结果为 。所以函数也可以写成pointer[i]
*( pointer + i )
void array_set(arr_t *arr, int i, vp_t vp) {
arr->start[i] = vp;
}
程序的其他部分也存在同样的问题,您应该相应地更新这些问题。
例如,该函数可能如下所示array_get
int array_get(arr_t *arr, int i) {
int *p = arr->start[i];
return *p;
}
这是错误的
vp_t *dest = arr->start + i * sizeof(vp_t);
在 C 语言中,当您进行指针算术(即向指针添加一个数字)时,编译器将负责将数字乘以指向的对象的大小。例如,如果您有
int64_t a[50];
int *b = a;
int *c = &(a[21]);
b + 8
指向 not - 编译器知道指向大小为 8 字节的对象,并将添加的数字乘以 8。同样,将是 21,而不是 168,因为编译器知道将地址的差异除以对象大小。a[8]
a[1]
b
b
c - b
在我的示例中,将相同,然后编译器将 64 乘以得到要添加到的字节数。这显然在阵列之外,这就是 Valgrind 在您的案例中检测到的。b + 8 * sizeof(int64_t)
b + 64
sizeof(int64_t)
b
另一种看待这个问题的方法是,在 C 语言中,它们在功能上是相同的。你从来没见过人写信,是吗?您也不需要为指针算术进行乘法运算。a[i]
*(a + i)
a[i * (sizeof *a)]
之所以需要 in,是因为无法推断块中将包含的对象的类型。sizeof
malloc()
malloc()
您的代码还有另一个问题。数组存储指向要存储的对象的指针。这可能是因为您希望能够存储任何类型的对象。但是如果你这样做,你必须小心确保物体不会消失,让你的指针悬空。
例如,x、y 和 z 都是自动示波器。当函数退出时,它们的存储将消失。这不是代码的问题,因为退出函数与退出程序相同。但是,如果您有类似的东西:
int populateArray(arr_t *array)
{
int x = 1, y = 2, z = 3;
array_set(arr, 0, &x);
array_set(arr, 1, &y);
array_set(arr, 2, &z);
}
int main()
{
arr_t *arr = array_new(3);
populateArray(arr);
// At this point your array contains 3 dangling pointers.
// Valgrind will complain if you try to access any of them.
}
那坏了。
上一个:C 语言中的指针比较
评论
set()
get()
vp_t *dest = arr->start+i;
printf("%p\n", arr->start + i * sizeof(vp_t));
printf("%p\n", arr->start + i );
vp_t
int