-2

So, to start off I've already looked at a few questions including this one and none of them seem to help.

I'm simply trying to write a function that extends the size of an array using realloc().

My code currently looks like this:

unsigned char *xtnd = malloc(4);
xtndc(&xtnd, 4);
// sizeof(*xtnd) should now be 8

void xtndc ( unsigned char ** bytesRef , uint8_t count ) {
    *bytesRef = realloc(*bytesRef, (sizeof(**bytesRef)) + count);
}

But no matter what I do it seems that the size of xtnd is always 4. After running xtndc() on it it should now be 8 bytes long.

Any suggestions?

Community
  • 1
  • 1
Nathan F.
  • 3,250
  • 3
  • 35
  • 69
  • 2
    `sizeof` doesn't give the size of the array, just the pointer itself. – Michael Albers Dec 15 '16 at 03:15
  • @MichaelAlbers That's why I'm dereferencing it? When i do `sizeof(**bytesRef)` i dereference the dereferenced pointer, right? I'm somewhat new to C so if that's not correct let me know – Nathan F. Dec 15 '16 at 03:15
  • 2
    Nope, not how it works. Read this: http://en.cppreference.com/w/c/language/sizeof – Michael Albers Dec 15 '16 at 03:18
  • What did you read about the `sizeof` operator? If as pointer was an array, it would be called "array", not "pointer" – too honest for this site Dec 15 '16 at 03:20
  • @Olaf Again, I'm somewhat new to C. I was under the impression that if i dereferenced the pointer back to it's object I could `sizeof()` the object. Now that I'm reading up on `sizeof()` (thanks to @MichaelAlbers) I see that's not how it's used. Thanks for the help guys! – Nathan F. Dec 15 '16 at 03:22
  • Note: `sizeof` is not a function, but an operator. The parentheses are not part of it. And dereferencing a **pointer** yields the object it points to, which is clearly not an array here, but `unsigned char`. Your impression was very correct, but why do you think dereferencing a `unsigned char **` yields an array? – too honest for this site Dec 15 '16 at 03:25
  • @Olaf good to know – Nathan F. Dec 15 '16 at 03:26
  • @Olaf I was under the impression that `unsigned char* c = malloc(4);` yielded a reference to an array of unsigned chars of length 4, since directly after I could assign values to it like so `c[0] = 0x01; c[1] = 0x02; ...`. But I'm pretty sure I have a better understanding of how it works now. – Nathan F. Dec 15 '16 at 04:02
  • @NathanFiscaletti Since `bytesRef` is an `unsigned char **`, dereferencing that gives you an `unsigned char *`, and dereferencing that gives you an `unsigned char`. What is the size of an `unsigned char`? It is 1. – user253751 Dec 15 '16 at 04:07

4 Answers4

2

The type of **bytesRef is unsigned char, so sizeof(**bytesRef) is 1. sizeof doesn't keep track of dynamic allocations, it's a compile time tool that gives you the size of a type, in this case unsigned char.

You have to keep track of the array size manually to calculate the new required size.

sth
  • 222,467
  • 53
  • 283
  • 367
1

Your program does in fact change the size of the memory block. It changes the size of your original memory block from 4 bytes to 5 bytes. It changes to 5 bytes because you are essentially doing sizeof(unsigned char) + 4 which 1 + 4 = 5. If you want to double the size instead, do count*sizeof(unsigned char) + count. There are two points to be noted here:

  1. The sizeof function returns the size of the data type, not the size of the allocated bytes. There is no way to know the size of the dynamically allocated memory.
  2. The function realloc (and malloc and calloc as well) is not always guaranteed to return the requested reallocation. It may or may not succeed all the time.
VHS
  • 9,534
  • 3
  • 19
  • 43
0

I fixed the problem with the following code.

typedef struct CArrPtr {
    unsigned char* ptr;
    size_t size;
} CArrPtr;
void xtndc ( CArrPtr *bytesRef, uint8_t count );

. . .    

CArrPtr xtnd = { .ptr = malloc(4), .size = 4 };
xtndc( &xtnd, 4 );
// xtnd.size is now 8 bytes 

. . . 

void xtndc ( CArrPtr *bytesRef, uint8_t count ) {
    unsigned char *nptr;
    if((nptr = realloc(bytesRef->ptr, bytesRef->size + count)) != 0) 
    {
        bytesRef->ptr = nptr;
        bytesRef->size = bytesRef->size + count;
    }
}

As I am somewhat new to C, what I learned from this is that malloc specifically creates a pointer to a memory block, but you have no direct access to information about the memory block. Instead, you must store the size of the array that you created with malloc somewhere as well.

Since in the past I'd been initializing arrays with unsigned char arr[size]; and then using sizeof on it, I was under the impression that sizeof returned the size of the array, which is of course wrong as it gives you the size of a type.

Glad I could learn something from this.

Nathan F.
  • 3,250
  • 3
  • 35
  • 69
  • 1
    But you have to call your function to do the reallocation, and then do arithmetic. You should aim to design your code so that you don't need that split of work. For example, you might have a structure type that keeps the pointer and the current length, and you'd pass a pointer to that structure to the `xtndc()` function so that the function can update the current length. Also note that `old_ptr = realloc(old_ptr, new_size);` leaks memory if the reallocation fails. You should use: `if ((new_ptr = realloc(old_ptr, new_size)) != 0) old_ptr = new_ptr;` or variants on that theme. Not the test. – Jonathan Leffler Dec 15 '16 at 04:03
  • @JonathanLeffler Thanks for the suggestions! I've updated the fixed code with them, it seems to work, hopefully it's up to par :) – Nathan F. Dec 15 '16 at 04:27
  • Looks better to me. If you only use it once, it might be overkill, but if you use it more than that, it reduces the amount of code to be written each time. – Jonathan Leffler Dec 15 '16 at 04:35
0
  1. sizeof is used to calculate size of data type or array. Pointer and array are very similar, but they are different things. For int *ap, sizeof(ap) will return 4 on x86, sizeof(*ap) will return 4; for int a[10], sizeof(a) will return 40.

  2. sizeof expression is processed at compile time, so it will be a constant written into the executable file before you run the program.

  3. malloc and realloc don't maintain size.

  4. If realloc succeeds, it will reallocate the requested size. So you don't need to check the size after realloc returns, but you should check the return value of realloc to ensure that realloc succeeds.

smilingwang
  • 119
  • 6