简体   繁体   中英

malloc() successful but allocates less memory than expected

I use malloc to allocate 8192 bytes of memory; malloc returns successfully, but for some reason, I cannot access memory beyond 4205 bytes of that memory chunk.

I've also tried allocating even larger chunks of memory (ie 8192 * 2) but still no luck, only the first 4205 byes of memory is accessible :(

Here is the partial code:

int num_ino = 256;
struct inode * ino_table = malloc(8192);
assert(ino_table);
for(int i = 0; i < num_ino; i ++){
  printf("pre core dump %d\n", i);
  memcpy(ino_table + i * sizeof(struct inode), &inotable[i], sizeof(struct inode));
}

Here is what happens in gdb:

Breakpoint 1, unixfilesystem_init (dfd=3) at unixfilesystem.c:54
54        assert(ino_table);

(gdb) p *(ino_table)
$1 = {i_mode = 0, i_nlink = 0 '\000', i_uid = 0 '\000', i_gid = 0 '\000', i_size0 = 0     '\000', i_size1 = 0, i_addr = {0, 0, 0, 0, 0, 0, 0, 0}, 
  i_atime = {0, 0}, i_mtime = {0, 0}}

(gdb) p *(ino_table + 4205)
$2 = {i_mode = 0, i_nlink = 0 '\000', i_uid = 0 '\000', i_gid = 0 '\000', i_size0 = 0     '\000', i_size1 = 0, i_addr = {0, 0, 0, 0, 0, 0, 0, 0}, 
  i_atime = {0, 0}, i_mtime = {0, 0}}

(gdb) p *(ino_table + 8000)
Cannot access memory at address 0x643a30

(gdb) p *(ino_table + 4206)
Cannot access memory at address 0x625ff0

When you perform pointer arithmetic on ino_table , the unit is sizeof(struct inode) , not byte.

Thus, the

ino_table + i * sizeof(struct inode)

should become

ino_table + i

Finally, I'd change the malloc() like so:

struct inode * ino_table = malloc(num_ino * sizeof(struct inode));

That seems reasonable,

This command in gdb:

   p *(ino_table + 8000)

Does not say "print the content of ino_table offset by 8000 bytes". It says, "print the content of ino_table offset by 8000 elements".

This means gdb evaluates *(ino_table + 8000) just as C does, which gives you the 8000th element. This is the same as saying p ino_table[8000] .

In bytes, that is sizeof(struct inode) * 8000 . This will be out of bounds unless sizeof(struct inode) is 1.

When allocating space for an array of elements, one would normally say how many elements is needed, however you have told malloc() to get 8192 bytes. If you want 8192 elements of a struct inode in your ino_table , you would do:

 struct inode * ino_table = malloc(8192 * sizeof *ino_table);

Note that gdb does not do bound checking on your array/buffers. If you successfully can do p *(ino_table + 4205) , that can very well be out of bounds regarding the ino_table , and could be pointing into available memory elsewhere. When gdb finally errors out with Cannot access memory at address 0x643a30 , that means you're now trying to access memory that does not exist - ie it is not mapped into the address space of your process at all.

Your problem is most probably pointer arithmetic.

ino_table + i * sizeof(struct inode)

is most probably not what you want, but

ino_table + i

as due to pointer arithmetic ino_table + i is already doing

((char *)ino_table) + i * sizeof(struct inode)

The same applies to gdb, where ino_table + 8000 refers to &ino_table[8000] - the 8000th element of ino_table , which, unless your struct inode is defined to be 1 byte in size, would be outside of the allocated memory.


More genericly speaking:

If you have a pointer P to a type T, then P++ will increment the address stored in P by sizeof(T), and NOT by one byte.


Also, instead of

ino_table + i

you could do

&ino_table[i]

which is preferred by many people (I personally don't really care) because it makes it more obvious that increasing i increases the accessed address by one element, and not one byte .


And for the malloc you could do

malloc(number_of_elements * sizeof(type)) - don't worry about performance difference or code size, as when number_of_elements is constant, the expression should be optimized into one constant number anyway.

Or, if you wanna be extra-careful and/or don't know if you will access parts which you haven't written to, you could use:

calloc(number_of_elements, sizeof(type))

The difference to malloc is that calloc makes sure you get nulled memory (on most implementations by serving you a pointer to a special copy-on-write zeroed memory page of the system), as well as returning memory aligned to sizeof(type) (malloc will normally also return aligned memory, but in the case of malloc, it will probably be aligned to the largest alignable value)


As bounds checking has been mentioned in a different answer: You could have a look at gcc mudflap extension - it doesn't exactly do bounds checking, but many other memory related things, and IMHO is one of the easiest things to set up besides valgrind (which doesn't need any setup, but unfortunately doesn't always work)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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