为什么读取结构指针字段无效?
Why is reading a struct pointer field invalid?
运行Valgrind中的程序说在结构的转换指针处有一个"Invalid read of size 8"。它与calloc有关吗?如果按原样读取,则为 (nil)。
有一个struct(称为trie),其用法如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
const int MAX_SIZE = 20;
struct _trie {
int maxNode;
int nextNode;
int** transition;
char* fin;
};
typedef struct _trie * Trie;
Trie createTrie (int maxNode){
Trie trie;
trie = (Trie) malloc(sizeof(Trie));
printf("size of trie: %lu, size of the struct: %lu, size of _trie: %lu\n",sizeof(trie),sizeof(Trie), sizeof(struct _trie));
trie->maxNode = maxNode;
printf("maxNode = %d, size of maxNode: %lu\n",trie->maxNode,sizeof(trie->maxNode));
printf("size of nextNode : %lu, size of transition: %lu, size of fin: %lu\n",
sizeof(trie->nextNode),sizeof(trie->transition),sizeof(trie->fin));
在这里,当 valgrid 尝试读取时,它说 "Invalid read of size 8":
//invalid read
printf("transitions points to: %p, address: %p\n",trie->transition,&trie->transition);
来自 char * fin 的相同消息:
//invalid read
printf("fin points to: %p, address: %p\n",trie->fin,&trie->fin);
getchar();
trie->transition = (int**)calloc(maxNode,sizeof(int*));
printf("trie->transition done.\n");
printf("transitions points to: %p, address: %p\n",trie->transition,&trie->transition);
if(trie->transition == NULL){
printf("null for trie->transition\n");
exit(0);
}
printf("Size of transition: %lu, size of int:%lu, pointer: %p\n\n",sizeof(trie->transition),sizeof(int),trie->transition);
for(int counter = 0; counter < maxNode; ++counter){
trie->transition[counter] = calloc(UCHAR_MAX,sizeof(int));
if(trie->transition[counter] == NULL){
printf("null for trie->transition[%d]\n",counter);
exit(0);
}
//printf("size of transition[%d]: %lu\n",counter,sizeof(trie->transition[counter]));
}
printf("\nFilling up trie->transition\n");
for(int counter = 0; counter < maxNode; ++counter){
for(int counter2 = 0; counter2 < UCHAR_MAX; ++counter2){
trie->transition[counter][counter2] = -1;
//printf("size of transition[%d][%d]: %lu, value: %d\n",counter,counter2,sizeof(trie->transition[counter]),trie->transition[counter][counter2]);
}
//getchar();
}
return (trie);
}
void free_all(Trie trie){
for(int counter = 0; counter < trie->maxNode; ++counter){
free(trie->transition[counter]);
}
free(trie->transition);
free(trie);
}
int main(int argc, char *argv[]){
Trie trie = createTrie(MAX_SIZE);
free_all(trie);
return (0);
}
Valgrind 输出:
==3079== Memcheck, a memory error detector
==3079== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==3079== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==3079== Command: ./debug_test
==3079==
size of trie: 8, size of the struct: 8, size of _trie: 24
maxNode = 20, size of maxNode: 4
size of nextNode : 4, size of transition: 8, size of fin: 8
==3079== Invalid read of size 8
==3079== at 0x1088AD: createTrie (in /projects/trie/debug_test)
==3079== by 0x108AE8: main (in /projects/trie/debug_test)
==3079== Address 0x5201048 is 0 bytes after a block of size 8 alloc'd
==3079== at 0x4C2DB2F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3079== by 0x108835: createTrie (in /projects/trie/debug_test)
==3079== by 0x108AE8: main (in /projects/trie/debug_test)
==3079==
transitions points to: (nil), address: 0x5201048
==3079== Invalid read of size 8
==3079== at 0x1088D1: createTrie (in /projects/trie/debug_test)
==3079== by 0x108AE8: main (in /projects/trie/debug_test)
==3079== Address 0x5201050 is 8 bytes after a block of size 8 alloc'd
==3079== at 0x4C2DB2F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3079== by 0x108835: createTrie (in /projects/trie/debug_test)
==3079== by 0x108AE8: main (in /projects/trie/debug_test)
==3079==
fin points to: (nil), address: 0x5201050
==3079== Invalid write of size 8
==3079== at 0x108907: createTrie (in /projects/trie/debug_test)
==3079== by 0x108AE8: main (in /projects/trie/debug_test)
==3079== Address 0x5201048 is 0 bytes after a block of size 8 alloc'd
==3079== at 0x4C2DB2F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3079== by 0x108835: createTrie (in /projects/trie/debug_test)
==3079== by 0x108AE8: main (in /projects/trie/debug_test)
==3079==
trie->transition done.
==3079== Invalid read of size 8
==3079== at 0x108923: createTrie (in /projects/trie/debug_test)
==3079== by 0x108AE8: main (in /projects/trie/debug_test)
==3079== Address 0x5201048 is 0 bytes after a block of size 8 alloc'd
==3079== at 0x4C2DB2F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3079== by 0x108835: createTrie (in /projects/trie/debug_test)
==3079== by 0x108AE8: main (in /projects/trie/debug_test)
==3079==
transitions points to: 0x5201910, address: 0x5201048
==3079== Invalid read of size 8
==3079== at 0x10893F: createTrie (in /projects/trie/debug_test)
==3079== by 0x108AE8: main (in /projects/trie/debug_test)
==3079== Address 0x5201048 is 0 bytes after a block of size 8 alloc'd
==3079== at 0x4C2DB2F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3079== by 0x108835: createTrie (in /projects/trie/debug_test)
==3079== by 0x108AE8: main (in /projects/trie/debug_test)
==3079==
==3079== Invalid read of size 8
==3079== at 0x108962: createTrie (in /projects/trie/debug_test)
==3079== by 0x108AE8: main (in /projects/trie/debug_test)
==3079== Address 0x5201048 is 0 bytes after a block of size 8 alloc'd
==3079== at 0x4C2DB2F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3079== by 0x108835: createTrie (in /projects/trie/debug_test)
==3079== by 0x108AE8: main (in /projects/trie/debug_test)
==3079==
Size of transition: 8, size of int:4, pointer: 0x5201910
==3079== Invalid read of size 8
==3079== at 0x108991: createTrie (in /projects/trie/debug_test)
==3079== by 0x108AE8: main (in /projects/trie/debug_test)
==3079== Address 0x5201048 is 0 bytes after a block of size 8 alloc'd
==3079== at 0x4C2DB2F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3079== by 0x108835: createTrie (in /projects/trie/debug_test)
==3079== by 0x108AE8: main (in /projects/trie/debug_test)
==3079==
==3079== Invalid read of size 8
==3079== at 0x1089B9: createTrie (in /projects/trie/debug_test)
==3079== by 0x108AE8: main (in /projects/trie/debug_test)
==3079== Address 0x5201048 is 0 bytes after a block of size 8 alloc'd
==3079== at 0x4C2DB2F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3079== by 0x108835: createTrie (in /projects/trie/debug_test)
==3079== by 0x108AE8: main (in /projects/trie/debug_test)
==3079==
Filling up trie->transition
==3079== Invalid read of size 8
==3079== at 0x108A20: createTrie (in /projects/trie/debug_test)
==3079== by 0x108AE8: main (in /projects/trie/debug_test)
==3079== Address 0x5201048 is 0 bytes after a block of size 8 alloc'd
==3079== at 0x4C2DB2F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3079== by 0x108835: createTrie (in /projects/trie/debug_test)
==3079== by 0x108AE8: main (in /projects/trie/debug_test)
==3079==
==3079== Invalid read of size 8
==3079== at 0x108A84: free_all (in /projects/trie/debug_test)
==3079== by 0x108AF8: main (in /projects/trie/debug_test)
==3079== Address 0x5201048 is 0 bytes after a block of size 8 alloc'd
==3079== at 0x4C2DB2F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3079== by 0x108835: createTrie (in /projects/trie/debug_test)
==3079== by 0x108AE8: main (in /projects/trie/debug_test)
==3079==
==3079== Invalid read of size 8
==3079== at 0x108AB3: free_all (in /projects/trie/debug_test)
==3079== by 0x108AF8: main (in /projects/trie/debug_test)
==3079== Address 0x5201048 is 0 bytes after a block of size 8 alloc'd
==3079== at 0x4C2DB2F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3079== by 0x108835: createTrie (in /projects/trie/debug_test)
==3079== by 0x108AE8: main (in /projects/trie/debug_test)
==3079==
==3079==
==3079== HEAP SUMMARY:
==3079== in use at exit: 0 bytes in 0 blocks
==3079== total heap usage: 24 allocs, 24 frees, 22,616 bytes allocated
==3079==
==3079== All heap blocks were freed -- no leaks are possible
==3079==
==3079== For counts of detected and suppressed errors, rerun with: -v
==3079== ERROR SUMMARY: 5167 errors from 11 contexts (suppressed: 0 from 0)
==3079== Invalid read of size 8
==3079== at 0x1088AD: createTrie (in /projects/trie/debug_test)
==3079== by 0x108AE8: main (in /projects/trie/debug_test)
==3079== Address 0x5201048 is 0 bytes after a block of size 8 alloc'd
==3079== at 0x4C2DB2F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3079== by 0x108835: createTrie (in /projects/trie/debug_test)
==3079== by 0x108AE8: main (in /projects/trie/debug_test)
这表示您的代码正在尝试从无效地址读取 8 字节值。
这个地址就在通过 createTrie
中的 malloc
分配的 8 字节块之后。换句话说,这一行:
trie = (Trie) malloc(sizeof(Trie));
为什么它认为trie
只指向8个字节的内存?因为你分配了 sizeof (Trie)
字节,而 Trie
是
typedef struct _trie * Trie;
或者换句话说,当您打算为整个结构分配足够的内存时,您为指针分配了内存。
强烈建议不要将指针隐藏在 typedef 后面,正是出于这个原因。
建议的修复:
typedef struct Trie Trie;
struct Trie {
int maxNode;
int nextNode;
int** transition;
char* fin;
};
Trie *createTrie(int maxNode) {
Trie *trie;
trie = malloc(sizeof *trie);
备注:
- 我们对
struct Trie
和(裸)Trie
使用相同的名称 (Trie
),因为其他任何东西都会造成不必要的混淆。
- 所有指针都明显声明为指针,
*
。
- 我们不转换
malloc
的 return 值,因为那将是另一个潜在的错误来源。
- 我们使用
sizeof *trie
来获取 trie
指向的类型中正确的字节数,无论 trie
是如何声明的。
运行Valgrind中的程序说在结构的转换指针处有一个"Invalid read of size 8"。它与calloc有关吗?如果按原样读取,则为 (nil)。
有一个struct(称为trie),其用法如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
const int MAX_SIZE = 20;
struct _trie {
int maxNode;
int nextNode;
int** transition;
char* fin;
};
typedef struct _trie * Trie;
Trie createTrie (int maxNode){
Trie trie;
trie = (Trie) malloc(sizeof(Trie));
printf("size of trie: %lu, size of the struct: %lu, size of _trie: %lu\n",sizeof(trie),sizeof(Trie), sizeof(struct _trie));
trie->maxNode = maxNode;
printf("maxNode = %d, size of maxNode: %lu\n",trie->maxNode,sizeof(trie->maxNode));
printf("size of nextNode : %lu, size of transition: %lu, size of fin: %lu\n",
sizeof(trie->nextNode),sizeof(trie->transition),sizeof(trie->fin));
在这里,当 valgrid 尝试读取时,它说 "Invalid read of size 8":
//invalid read
printf("transitions points to: %p, address: %p\n",trie->transition,&trie->transition);
来自 char * fin 的相同消息:
//invalid read
printf("fin points to: %p, address: %p\n",trie->fin,&trie->fin);
getchar();
trie->transition = (int**)calloc(maxNode,sizeof(int*));
printf("trie->transition done.\n");
printf("transitions points to: %p, address: %p\n",trie->transition,&trie->transition);
if(trie->transition == NULL){
printf("null for trie->transition\n");
exit(0);
}
printf("Size of transition: %lu, size of int:%lu, pointer: %p\n\n",sizeof(trie->transition),sizeof(int),trie->transition);
for(int counter = 0; counter < maxNode; ++counter){
trie->transition[counter] = calloc(UCHAR_MAX,sizeof(int));
if(trie->transition[counter] == NULL){
printf("null for trie->transition[%d]\n",counter);
exit(0);
}
//printf("size of transition[%d]: %lu\n",counter,sizeof(trie->transition[counter]));
}
printf("\nFilling up trie->transition\n");
for(int counter = 0; counter < maxNode; ++counter){
for(int counter2 = 0; counter2 < UCHAR_MAX; ++counter2){
trie->transition[counter][counter2] = -1;
//printf("size of transition[%d][%d]: %lu, value: %d\n",counter,counter2,sizeof(trie->transition[counter]),trie->transition[counter][counter2]);
}
//getchar();
}
return (trie);
}
void free_all(Trie trie){
for(int counter = 0; counter < trie->maxNode; ++counter){
free(trie->transition[counter]);
}
free(trie->transition);
free(trie);
}
int main(int argc, char *argv[]){
Trie trie = createTrie(MAX_SIZE);
free_all(trie);
return (0);
}
Valgrind 输出:
==3079== Memcheck, a memory error detector
==3079== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==3079== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==3079== Command: ./debug_test
==3079==
size of trie: 8, size of the struct: 8, size of _trie: 24
maxNode = 20, size of maxNode: 4
size of nextNode : 4, size of transition: 8, size of fin: 8
==3079== Invalid read of size 8
==3079== at 0x1088AD: createTrie (in /projects/trie/debug_test)
==3079== by 0x108AE8: main (in /projects/trie/debug_test)
==3079== Address 0x5201048 is 0 bytes after a block of size 8 alloc'd
==3079== at 0x4C2DB2F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3079== by 0x108835: createTrie (in /projects/trie/debug_test)
==3079== by 0x108AE8: main (in /projects/trie/debug_test)
==3079==
transitions points to: (nil), address: 0x5201048
==3079== Invalid read of size 8
==3079== at 0x1088D1: createTrie (in /projects/trie/debug_test)
==3079== by 0x108AE8: main (in /projects/trie/debug_test)
==3079== Address 0x5201050 is 8 bytes after a block of size 8 alloc'd
==3079== at 0x4C2DB2F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3079== by 0x108835: createTrie (in /projects/trie/debug_test)
==3079== by 0x108AE8: main (in /projects/trie/debug_test)
==3079==
fin points to: (nil), address: 0x5201050
==3079== Invalid write of size 8
==3079== at 0x108907: createTrie (in /projects/trie/debug_test)
==3079== by 0x108AE8: main (in /projects/trie/debug_test)
==3079== Address 0x5201048 is 0 bytes after a block of size 8 alloc'd
==3079== at 0x4C2DB2F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3079== by 0x108835: createTrie (in /projects/trie/debug_test)
==3079== by 0x108AE8: main (in /projects/trie/debug_test)
==3079==
trie->transition done.
==3079== Invalid read of size 8
==3079== at 0x108923: createTrie (in /projects/trie/debug_test)
==3079== by 0x108AE8: main (in /projects/trie/debug_test)
==3079== Address 0x5201048 is 0 bytes after a block of size 8 alloc'd
==3079== at 0x4C2DB2F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3079== by 0x108835: createTrie (in /projects/trie/debug_test)
==3079== by 0x108AE8: main (in /projects/trie/debug_test)
==3079==
transitions points to: 0x5201910, address: 0x5201048
==3079== Invalid read of size 8
==3079== at 0x10893F: createTrie (in /projects/trie/debug_test)
==3079== by 0x108AE8: main (in /projects/trie/debug_test)
==3079== Address 0x5201048 is 0 bytes after a block of size 8 alloc'd
==3079== at 0x4C2DB2F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3079== by 0x108835: createTrie (in /projects/trie/debug_test)
==3079== by 0x108AE8: main (in /projects/trie/debug_test)
==3079==
==3079== Invalid read of size 8
==3079== at 0x108962: createTrie (in /projects/trie/debug_test)
==3079== by 0x108AE8: main (in /projects/trie/debug_test)
==3079== Address 0x5201048 is 0 bytes after a block of size 8 alloc'd
==3079== at 0x4C2DB2F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3079== by 0x108835: createTrie (in /projects/trie/debug_test)
==3079== by 0x108AE8: main (in /projects/trie/debug_test)
==3079==
Size of transition: 8, size of int:4, pointer: 0x5201910
==3079== Invalid read of size 8
==3079== at 0x108991: createTrie (in /projects/trie/debug_test)
==3079== by 0x108AE8: main (in /projects/trie/debug_test)
==3079== Address 0x5201048 is 0 bytes after a block of size 8 alloc'd
==3079== at 0x4C2DB2F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3079== by 0x108835: createTrie (in /projects/trie/debug_test)
==3079== by 0x108AE8: main (in /projects/trie/debug_test)
==3079==
==3079== Invalid read of size 8
==3079== at 0x1089B9: createTrie (in /projects/trie/debug_test)
==3079== by 0x108AE8: main (in /projects/trie/debug_test)
==3079== Address 0x5201048 is 0 bytes after a block of size 8 alloc'd
==3079== at 0x4C2DB2F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3079== by 0x108835: createTrie (in /projects/trie/debug_test)
==3079== by 0x108AE8: main (in /projects/trie/debug_test)
==3079==
Filling up trie->transition
==3079== Invalid read of size 8
==3079== at 0x108A20: createTrie (in /projects/trie/debug_test)
==3079== by 0x108AE8: main (in /projects/trie/debug_test)
==3079== Address 0x5201048 is 0 bytes after a block of size 8 alloc'd
==3079== at 0x4C2DB2F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3079== by 0x108835: createTrie (in /projects/trie/debug_test)
==3079== by 0x108AE8: main (in /projects/trie/debug_test)
==3079==
==3079== Invalid read of size 8
==3079== at 0x108A84: free_all (in /projects/trie/debug_test)
==3079== by 0x108AF8: main (in /projects/trie/debug_test)
==3079== Address 0x5201048 is 0 bytes after a block of size 8 alloc'd
==3079== at 0x4C2DB2F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3079== by 0x108835: createTrie (in /projects/trie/debug_test)
==3079== by 0x108AE8: main (in /projects/trie/debug_test)
==3079==
==3079== Invalid read of size 8
==3079== at 0x108AB3: free_all (in /projects/trie/debug_test)
==3079== by 0x108AF8: main (in /projects/trie/debug_test)
==3079== Address 0x5201048 is 0 bytes after a block of size 8 alloc'd
==3079== at 0x4C2DB2F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3079== by 0x108835: createTrie (in /projects/trie/debug_test)
==3079== by 0x108AE8: main (in /projects/trie/debug_test)
==3079==
==3079==
==3079== HEAP SUMMARY:
==3079== in use at exit: 0 bytes in 0 blocks
==3079== total heap usage: 24 allocs, 24 frees, 22,616 bytes allocated
==3079==
==3079== All heap blocks were freed -- no leaks are possible
==3079==
==3079== For counts of detected and suppressed errors, rerun with: -v
==3079== ERROR SUMMARY: 5167 errors from 11 contexts (suppressed: 0 from 0)
==3079== Invalid read of size 8
==3079== at 0x1088AD: createTrie (in /projects/trie/debug_test)
==3079== by 0x108AE8: main (in /projects/trie/debug_test)
==3079== Address 0x5201048 is 0 bytes after a block of size 8 alloc'd
==3079== at 0x4C2DB2F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3079== by 0x108835: createTrie (in /projects/trie/debug_test)
==3079== by 0x108AE8: main (in /projects/trie/debug_test)
这表示您的代码正在尝试从无效地址读取 8 字节值。
这个地址就在通过 createTrie
中的 malloc
分配的 8 字节块之后。换句话说,这一行:
trie = (Trie) malloc(sizeof(Trie));
为什么它认为trie
只指向8个字节的内存?因为你分配了 sizeof (Trie)
字节,而 Trie
是
typedef struct _trie * Trie;
或者换句话说,当您打算为整个结构分配足够的内存时,您为指针分配了内存。
强烈建议不要将指针隐藏在 typedef 后面,正是出于这个原因。
建议的修复:
typedef struct Trie Trie;
struct Trie {
int maxNode;
int nextNode;
int** transition;
char* fin;
};
Trie *createTrie(int maxNode) {
Trie *trie;
trie = malloc(sizeof *trie);
备注:
- 我们对
struct Trie
和(裸)Trie
使用相同的名称 (Trie
),因为其他任何东西都会造成不必要的混淆。 - 所有指针都明显声明为指针,
*
。 - 我们不转换
malloc
的 return 值,因为那将是另一个潜在的错误来源。 - 我们使用
sizeof *trie
来获取trie
指向的类型中正确的字节数,无论trie
是如何声明的。