struggling to use calloc and realloc

Discussion in 'C Programming' started by dagger, Dec 18, 2003.

  1. dagger

    dagger Guest

    Hi there. I'm using C under FreeBSD with the gcc compiler and am having a
    bit of trouble using the calloc and realloc calls.

    As an example the code snippet:

    #include <stdio.h>

    int main() {

    char *ptr;

    ptr = (char *) calloc(1, sizeof(char));

    printf( "initial size (1 char) = %d\n", sizeof(ptr) );

    ptr = (char *) realloc(ptr, sizeof(char)*10);

    printf( "new size (10 chars) = %d\n", sizeof(ptr) );

    return 0;
    }


    and yet when I run it I get a size of 4 for each printf even though
    initially I allocated only the size of 1 char, and after reallocation there
    should be room for 10 bytes...? Or where am I making a mistake in my
    reasoning?

    Thanks for any help :)
     
    dagger, Dec 18, 2003
    #1
    1. Advertisements

  2. [trimmed mercilessly]
    sizeof(ptr) gives you the size of the pointer (and getting 4 for that
    means you're running the program on a machine where pointers are 4 bytes
    wide, which in this day and age suggests a desktop machine running a
    32-bit operating system).
    There's no way to get back the size of the memory a pointer points to;
    "sizeof *ptr" will give the size of one of whatever type ptr points at,
    but (especially for memory obtained from malloc and its friends) your
    code needs to keep track of how many of those it has space for itself.


    dave
     
    Dave Vandervies, Dec 18, 2003
    #2
    1. Advertisements

  3. dagger

    Servé Lau Guest

    Didn't you get a warning? For realloc and calloc you need stdlib.h too.
    Ah, you didn't get a warning because of the cast, don't cast the return
    value of the alloc functions.
    sizeof(ptr) is the same as sizeof(char *), on your system sizeof(char *) is
    4 bytes.
    sizeof gives the size of the variable itself, not the size of its value.

    You wouldn't expect this either.

    int x = 100;
    if (sizeof x == 100) false ot true?
     
    Servé Lau, Dec 18, 2003
    #3
  4. ^^^^^^^^
    The cast isn't needed, and will hide helpful compiler warnings should
    you have the misfortune to forget to include stdio.h. You should
    check that the allocation succeeded before continuing. Why not just
    use malloc()? And you might want to do

    malloc( sizeof(*ptr) );
    ptr is a pointer to a character. sizeof(ptr) is the space required
    by such an entity; on your implementation, it happens to be four
    bytes. This number has no relation to how many bytes of allocated
    memory, if any, ptr points to.
    Again, lose the cast. If the original allocation succeeds, but this
    one fails, the memory you originally allocated is now gone forever.
    Make sure you can free() what you allocated in this event:

    char *ptr, *tmp;

    ptr=malloc( sizeof(*ptr) );
    tmp=ptr;
    ptr=realloc( ptr, 10*sizeof(*ptr) );
    if( !ptr ) {
    free( tmp ); /* tmp pointed at the memory originally malloc()'ed,
    and that memory is now freed */
    /* go on with life */
    }
    else {
    /* do what you want with ptr */
    }
    Same error as above.
     
    Christopher Benson-Manica, Dec 18, 2003
    #4
  5. ^^^^^^^

    Of course, what would a post from me be like without some silly error
    or other? This is, of course, meant to be stdlib.h. I need a post
    preprocessor to catch these nasssties...
     
    Christopher Benson-Manica, Dec 18, 2003
    #5
  6. dagger

    nrk Guest

    sizeof operator determines the size of the type of its operand. In this
    case the type of the operand (ptr) is char *, and the size of a char * on
    your machine happens to be 4, which is what's being printed. sizeof will
    not tell you how many valid bytes ptr is pointing to (that is, it won't
    tell you what was allocated using calloc/malloc/realloc). This information
    cannot be extracted from the pointer. You need to keep track of the
    allocated size in a separate variable.

    -nrk.
     
    nrk, Dec 18, 2003
    #6
  7. A few notes here: first, sizeof(char) is a fancy way of writing 1.
    Second, calloc() is not a useful as it seems. Strictly speaking, it may
    not give the objects you allocate the value you expect. This is
    particularly true for floating point and pointer types. I need to check
    the standard to be sure, but I've been told that all-bits-zero is not
    guaranteed to be the representation of 0 for any type other than the
    character types.

    Finally, casting the return from malloc & friends is not recommended,
    and your example seems to demonstrate one of the reasons why. You forgot
    to #include <stdlib.h>, which means that there is no prototype in scope
    for calloc. This in turn means that the compiler assumed (incorrectly) a
    return type of int. ints and pointers may not be returned the same way,
    so the pointer that calloc returned might be lost, and ptr might be
    assigned garbage. Without the cast, the compiler would have been
    required to warn you about the conversion from int to a pointer. With
    the cast, there is no such guarantee.

    Consider using the comp.lang.c-approved idiom for calling malloc & friends:

    ptr = calloc(1, sizeof *ptr);

    This has number of advantages. The most important are that it doesn't
    mask the error mentioned above, it's largely self-maintaining if the
    type of ptr changes (for example, of you later choose to support
    internationalization using the type wchar_t instead of char), and it's
    more difficult to screw up than other idioms.
    There are at least 2 apparent problems here. First, you seem to be
    expecting sizeof to tell you how many characters you've allocated. It
    cannot do that. sizeof is purely a compile-time construct. Here it will
    give the size of the pointer ptr. If you want the size of a dynamically
    allocated object, you need to keep track of it yourself.

    The other problem is that you are using the wrong format specifier. %d
    can never be an appropriate format specifier for type size_t. %d is ONLY
    for type (signed) int. size_t cannot be an alias for (signed) int
    because size_t must be unsigned.

    Keep in mind that printf() is stupid. It can't determine the types of
    the objects you pass to it. It relies completely on you telling it what
    the types are. If you lie, all bets are off. Stack corruption leading to
    a program crash or worse is a likely result. Always double-check your
    format strings.

    For size_t, you can cast to unsigned long and print using %lu. This is
    mostly safe, but could potentially give incorrect results in C99, if the
    size_t value exceeds the range of unsigned long.
    This is not a safe way of using realloc. If it fails and returns NULL,
    you leak whatever memory you had already allocated. To realloc safely,
    store the result into a temporary pointer, then assign into your
    original pointer only if the result is non-NULL.

    Also, all the notes about calloc apply here as well (except that realloc
    doesn't initialize the bytes to all-bits-0).
    Same problems as the previous printf().
    Yes. You are printing the sizeof a pointer, not a dynamically allocated
    object. C provides no way of determining the size of a dynamically
    allocated object, post-allocation.

    -Kevin
     
    Kevin Goodsell, Dec 18, 2003
    #7
  8. It may be a trap representation for integer types, yes?
     
    Christopher Benson-Manica, Dec 18, 2003
    #8
  9. I think I've heard conflicting reports from different experts, or
    possibly I've misunderstood some of what I've heard. It's one of those
    things I've been meaning to look up for myself, but I'm afraid it'll
    take me hours to find and decrypt all the relevant sections. :/

    -Kevin
     
    Kevin Goodsell, Dec 18, 2003
    #9
  10. dagger

    Alex Guest

    Signed integer types. Yes.

    Alex
     
    Alex, Dec 18, 2003
    #10
  11. dagger

    Alex Guest

    No!

    I should have read the question first.

    All bits ONE can be a trap representation in one's complement.

    Alex
     
    Alex, Dec 18, 2003
    #11
  12. OH NO! You forgot to get yourself a prototype for calloc and realloc. You
    can get these most simply by doing this:

    If you hadn't added this cast, you would have got a very useful diagnostic.
    Note that (provided you remember to #include <stdlib.h>) the above is
    functionally equivalent to:

    ptr = calloc(1, sizeof *ptr);

    which is rather neater, easier to type, and easier to maintain.
    You are misinterpreting the result of the sizeof operator. It yields the
    size of the /pointer/, not the size of the thing to which it points.
     
    Richard Heathfield, Dec 18, 2003
    #12
  13. You seem to be implying that all-bits-0 may not be a trap representation
    for any integer type. If I am misunderstanding, please clarify.
    Otherwise, I'd be interested in how you justify this claim. My reading
    of the standard so far strongly suggests that any non-character integer
    type can have padding bits, and that those bits can be used to form a
    trap representation. Furthermore, it does not specify what values these
    padding bits take in any circumstance, and as far as I've seen does not
    specify that all-padding-bits-0 (and all value bits "don't cares")
    cannot be a trap representation.

    -Kevin
     
    Kevin Goodsell, Dec 18, 2003
    #13
  14. You forgot
    I think you mean
    ptr = calloc(1,1);
    or
    ptr = malloc(1);
    or
    ptr = calloc(1, sizeof *ptr);
    or
    ptr = malloc(*ptr);
    Your cast serves no useful function; all it does is mask your error in
    failing to #include <stdlib.h>.
    sizeof(char) is 1 by definition. The 3rd and 4th options above are in case
    the type of ptr should be changed later to some other kind of pointer.
    Calling calloc() instead of malloc() when you don't need to initialize the
    allocated array to "all bit zero" is wasteful.
    Failing to check whether malloc(), calloc(), or realloc() succeeded is a
    severe design error.
    This tells you what the size of a pointer to char is, so should be rewritten
    printf("Size of pointer to char is %u\n", (unsigned) sizeof ptr);
    Since sizeof yields a size_t, an unsigned integer (not necessarily an
    unsigned int), a cast should always be used unless you have the new C99
    specifier modifiers "%zu" and family. It would be safer to use "%lu" with
    (unsigned long) or "%llu" with (unsigned long long) rather than plain "%d"
    or "%u".
    See comments on the calloc() call.
    See comments on the previous printf(). You are _still_ printing the size
    of a pointer to char.
    All of this is covered in the FAQ. You should learn to check the FAQ
    always before posting.
     
    Martin Ambuhl, Dec 18, 2003
    #14
  15. His casting the return values from realloc and calloc would, for many
    compilers, supress any warning. And make sure that his code was wrong, too.
     
    Martin Ambuhl, Dec 18, 2003
    #15
  16. dagger

    Randy Howard Guest

    Note also that it returns a size_t, which is not guaranteed to fit in
    the "%d" specified in the printf() call.
     
    Randy Howard, Dec 18, 2003
    #16
  17. I recall there is this Defect Report #263 filed:
    ( http://anubis.dkuug.dk/jtc1/sc22/wg14/www/docs/dr_263.htm )

    [ idiomatic memset(..., 0, ...) and calloc examples ]

    Suggested[1] Technical Corrigendum

    Append to 6.2.6.2#5:

    For any integer type, the object representation where all the
    bits are zero shall be a representation of the value zero in
    that type.

    [1] The last subheading in this DR should read: "Proposed
    Technical Corrigendum", checked with comp.std.c recently.


    Regards
     
    Irrwahn Grausewitz, Dec 18, 2003
    #17
  18. So what is the status of this report? Is it accepted as a defect? I'm
    not familiar with how the C committee handles defect reports, but C++
    defects are usually given a status that indicates if it has been
    accepted (or rejected) as a defect and what plans are in the works to
    fix it. I don't see anything like that on the page you linked.

    -Kevin
     
    Kevin Goodsell, Dec 18, 2003
    #18
  19. size_t is guaranteed NOT to fit %d... unless size_t happens to promote
    to int, which is probably unlikely.

    -Kevin
     
    Kevin Goodsell, Dec 19, 2003
    #19
  20. It depends on what you mean by "fit". On many systems, size_t happens
    to be the same size as int (say, 32 bits), and the following:

    printf("sizeof(foo) = %d\n", sizeof(foo));

    is likely to work as expected as long as sizeof(foo) doesn't exceed
    INT_MAX (assuming an appropriate declaration of foo, of course).

    Having said that, it's still undefined behavior; working as expected
    is just one of the infinitely many possible results of undefined
    behavior. To avoid undefined behavior, you can do one of the
    following:

    /* Approach 1 */
    printf("sizeof(foo) = %d\n", (int)sizeof(foo));

    which will work if you happen to know that sizeof(foo) will never
    exceed INT_MAX; or

    /* Approach 2 */
    printf("sizeof(foo) = %lu\n", (unsigned long)sizeof(foo));

    which is guaranteed to work in C90, but could conceivably fail in C99
    if size_t is bigger than unsigned long and sizeof(foo) > ULONG_MAX; or

    /* Approach 3 */
    printf("sizeof(foo) = %zu\n", sizeof(foo));

    which will work in C99 (more precisely, if your C library's printf
    implementation supports the 'z' length modifier), but not in C90.

    In general, approach 2 is better than approach 1, and is unlikely to
    fail in practice.
     
    Keith Thompson, Dec 19, 2003
    #20
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.