Search code examples
pointersc99localtimetime.h

c99 problems with pointers and localtime_r


I am assigning one variable to hold the current time:

struct tm *cur = malloc (sizeof (cur));
time_t t = time (NULL);
localtime_r (&t, cur);

I then print the year. It is correct. Next I enter a loop where I assign a new variable time values from a file:

struct stat file_stats;
struct tm *file = malloc (sizeof (file));
lstat (argv[itor], &file_stats);
//check1
localtime_r(&file_stats.st_mtime, file);
//check2

At "check1," cur->tm_year prints a correct and reasonable value. At "check2," cur->tm_year prints "0". What is happening here? I assume it has something to do with me missing something with pointer manipulation. Any help would be greatly appreciated, especially an explanation of what I'm misunderstanding.


Solution

  • I adapted your code into this SSCCE:

    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/stat.h>
    #include <time.h>
    
    int main(int argc, char **argv)
    {
        struct tm *cur = malloc(sizeof(cur));
        time_t t = time (NULL);
        localtime_r(&t, cur);
    
        printf("curr 1 year: %d\n", cur->tm_year + 1900);
    
        for (int itor = 1; itor < argc; itor++)
        {
            struct stat file_stats;
            struct tm *file = malloc(sizeof(file));
            file->tm_year = 0;
            if (lstat(argv[itor], &file_stats) == 0)
            {
                printf("curr 2 year: %d\n", cur->tm_year + 1900);
                localtime_r(&file_stats.st_mtime, file);
                printf("curr 3 year: %d\n", cur->tm_year + 1900);
                printf("file 1 year: %d\n", file->tm_year + 1900);
            }
        }
        return(0);
    }
    

    The results I got running it were:

    curr 1 year: 2013
    curr 2 year: 2013
    curr 3 year: 2013
    file 1 year: 2013
    curr 2 year: 2013
    curr 3 year: 2013
    file 1 year: 2010
    curr 2 year: 2013
    curr 3 year: 2013
    file 1 year: 2013
    curr 2 year: 2013
    curr 3 year: 2013
    file 1 year: 2011
    

    It was useful I had some older files around. Superficially, it looks like there's no problem. However, valgrind had a fit:

    ==50495== Memcheck, a memory error detector
    ==50495== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
    ==50495== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
    ==50495== Command: ltr 3d.c const-stuff.c ltr.c madump.c pthread-1.c pthread-2.c pthread-3.c recv.c regress.c send.c strandsort.c x.c
    ==50495== 
    ==50495== WARNING: Support on MacOS 10.8 is experimental and mostly broken.
    ==50495== WARNING: Expect incorrect results, assertions and crashes.
    ==50495== WARNING: In particular, Memcheck on 32-bit programs will fail to
    ==50495== WARNING: detect any errors associated with heap-allocated data.
    ==50495== 
    ==50495== Invalid write of size 4
    ==50495==    at 0x105C48: timesub (in /usr/lib/system/libsystem_c.dylib)
    ==50495==    by 0x1058FE: _st_localsub (in /usr/lib/system/libsystem_c.dylib)
    ==50495==    by 0x10609D: localtime_r (in /usr/lib/system/libsystem_c.dylib)
    ==50495==    by 0x100000DE8: main (ltr.c:10)
    ==50495==  Address 0x10001b188 is 0 bytes after a block of size 8 alloc'd
    ==50495==    at 0x5686: malloc (vg_replace_malloc.c:274)
    ==50495==    by 0x100000DC3: main (ltr.c:8)
    ==50495== 
    ==50495== Invalid write of size 4
    ==50495==    at 0x105C70: timesub (in /usr/lib/system/libsystem_c.dylib)
    ==50495==    by 0x1058FE: _st_localsub (in /usr/lib/system/libsystem_c.dylib)
    ==50495==    by 0x10609D: localtime_r (in /usr/lib/system/libsystem_c.dylib)
    ==50495==    by 0x100000DE8: main (ltr.c:10)
    ==50495==  Address 0x10001b198 is 16 bytes after a block of size 8 alloc'd
    ==50495==    at 0x5686: malloc (vg_replace_malloc.c:274)
    ==50495==    by 0x100000DC3: main (ltr.c:8)
    ==50495== 
    

    And the output went on in a similar vein for quite a while. But it highlights the trouble: you should be using sizeof(*cur) and sizeof(*file) in the malloc() calls. That yields:

    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/stat.h>
    #include <time.h>
    
    int main(int argc, char **argv)
    {
        struct tm *cur = malloc(sizeof(*cur));
        time_t t = time (NULL);
        localtime_r(&t, cur);
    
        printf("curr 1 year: %d\n", cur->tm_year + 1900);
    
        for (int itor = 1; itor < argc; itor++)
        {
            struct stat file_stats;
            struct tm *file = malloc(sizeof(*file));
            file->tm_year = 0;
            if (lstat(argv[itor], &file_stats) == 0)
            {
                printf("curr 2 year: %d\n", cur->tm_year + 1900);
                localtime_r(&file_stats.st_mtime, file);
                printf("curr 3 year: %d\n", cur->tm_year + 1900);
                printf("file 1 year: %d\n", file->tm_year + 1900);
            }
        }
        return(0);
    }
    

    And valgrind gives that a clean bill of health.