Search code examples
cpointersfatfs

passing local pointer to function FatFs


I have the following function in C:

char* AUDIO_GetFile(FIL* fil, DIR* dir, FILINFO* fno) {
    FRESULT res;
    res = f_readdir(dir, fno); /* Read a directory item */
    if (fno->fname[0] == '\0')} /* End of files */
       ...
    }
    ...
    return &(fno->fname);
}

However I need a temporary variable to keep old value and then return &(fno->fname). I created local variable FILINFO t_fno and then pass it to function res = f_readdir(dir, &t_fno); everything is OK.

But if I wanted to use t_fno as local pointer and then pass it to f_readdir I don't get actual data but some random memory readings and program crashes on if (t_fno->fname[0] == '\0')}. I tried also initalizing FILINFO* t_fno = NULL but this doesn't help.

char* AUDIO_GetFile(FIL* fil, DIR* dir, FILINFO* fno) {
    FRESULT res;
    FILINFO* t_fno;
    res = f_readdir(dir, t_fno); /* Read a directory item */
    if (t_fno->fname[0] == '\0')} /* crash */
       ...
    }
    ...
    fno = t_fno;
    return &(fno->fname);
}


FRESULT f_readdir (
    DIR* dp,            /* Pointer to the open directory object */
    FILINFO* fno        /* Pointer to file information to return */
)
{
    FRESULT res;
    DEFINE_NAMEBUF;


    res = validate(dp);                     /* Check validity of the object */
    if (res == FR_OK) {
        if (!fno) {
            res = dir_sdi(dp, 0);           /* Rewind the directory object */
        } else {
            INIT_BUF(*dp);
            res = dir_read(dp, 0);          /* Read an item */
            if (res == FR_NO_FILE) {        /* Reached end of directory */
                dp->sect = 0;
                res = FR_OK;
            }
            if (res == FR_OK) {             /* A valid entry is found */
                get_fileinfo(dp, fno);      /* Get the object information */
                res = dir_next(dp, 0);      /* Increment index for next */
                if (res == FR_NO_FILE) {
                    dp->sect = 0;
                    res = FR_OK;
                }
            }
            FREE_BUF();
        }
    }

    LEAVE_FF(dp->fs, res);
}

Solution

  • According to its docs, f_readdir() expects its second argument to be a "[p]ointer to the file information structure to store the information about read item." This is consistent with the first use you described, wherein you declare t_fno as a local FILINFO structure, and pass its address. It is not consistent with declaring t_fno as a pointer, leaving it uninitialized, and passing its indeterminate initial value.

    The second way produces undefined behavior, which seems eminently reasonable given that the most charitable interpretation possible is that you've instructed the function to write data in some random place. Even if it is successful in doing so, you cannot know what the effect will be, or whether the data will be unchanged when you try to read them back later, or whether you can read them back later. C does not require or provide for you to reason about any of that -- it just declares the behavior to be undefined. You cannot safely or reliably do the job that way.

    As you observed, initializing the pointer does not help, unless (which you did not explore) you initialize it to point to actual storage compatible with a FILINFO structure. The pointer declaration by itself provides only for the pointer, not for anything to point to. Example:

    char* AUDIO_GetFile(FIL* fil, DIR* dir, FILINFO* fno) {
        FRESULT res;
        FILINFO t_info;
        FILINFO* t_fno = &t_info;
    
        res = f_readdir(dir, t_fno); /* Read a directory item */
    
        // ...
    

    Under most circumstances, I'd prefer not maintaining t_fno separately, but there could be viable reasons for keeping it.