Home > Software engineering >  How can a function change the original variable, when it is passed as value argument to the function
How can a function change the original variable, when it is passed as value argument to the function

Time:08-07

I have the following code snippet:

static const unsigned MAX_DIR_ITEMS = 4;

void fetchAlbum(int sd, const unsigned volref, const char *root, const char *name) {
    int dirItems = MAX_DIR_ITEMS;
    VFSDirInfo dirInfo[MAX_DIR_ITEMS];
    FileRef dirRef;

    if (dlp_VFSFileOpen(sd, volref, srcAlbumDir, vfsModeRead, &dirRef) < 0) {
        jp_logf(L_GUI, "Could not open dir '%s' on volume %d\n", srcAlbumDir, volref);
        return;
    }

    enum dlpVFSFileIteratorConstants itr = vfsIteratorStart;
    while (itr != vfsIteratorStop) {
        PI_ERR bytes;
        itr = vfsIteratorStart;
        jp_logf(L_DEBUG, "Enumerate dir '%s', dirRef=%d, itr=%d, dirItems=%d\n", srcAlbumDir, dirRef, (int)itr, dirItems);
        if ((bytes = dlp_VFSDirEntryEnumerate(sd, dirRef, (unsigned long *)&itr, &dirItems, dirInfo)) < 0) {
            jp_logf(L_FATAL, "Enumerate ERROR: bytes=%d, dirRef=%d, itr=%d, dirItems=%d\n", bytes, dirRef, (int)itr, dirItems);
            break;
        } else {
            jp_logf(L_DEBUG, "Enumerate OK: bytes=%d, dirRef=%d, itr=%d, dirItems=%d\n", bytes, dirRef, (int)itr, dirItems);
        }

The used functions are defined as:

/** @file pi-args.h
 *  @brief Macros for prototype definitions
 *
 */
#if ((defined(__STDC__) || defined(SABER)) && !defined(NO_PROTOTYPE)) || defined(__cplusplus) || defined(USE_PROTOTYPE) || defined(CAN_PROTOTYPE)
#   define PI_ARGS(x)       x
#   define PI_CONST const
#else
#   define PI_ARGS(x)       ()
#   define PI_CONST
#endif

typedef unsigned long FileRef;          /**< Type for file references when working with VFS files and directories. */

/** @name Expansion manager and VFS manager constants */
/*@{*/

    /** @brief VFS file iterator constants */
    enum dlpVFSFileIteratorConstants {
        vfsIteratorStart    = 0,        /** < Indicates that iterator is beginning */
        vfsIteratorStop     = -1        /**< Indicate that iterator has gone through all items */
    };

/*@}*/

/** @name Expansion manager functions */
/*@{*/

    /** @brief Iterate through the entries in a directory
     *
     * Supported on Palm OS 4.0 and later. At the beginning you set
     * @p dirIterator to #vfsIteratorStart, then call this function
     * repeatedly until it returns an error code of the iterator becomes
     * #vfsIteratorStop.
     *
     * @bug On some early OS 5 devices like Tungsten T and Sony NX70, NX73 this
     * call crashes the device. This has been confirmed to be a bug in HotSync on
     * the device, as tests showed that a regular HotSync conduit does crash the
     * device with this call too.
     *
     * @param sd Socket number
     * @param dirref Directory reference obtained from dlp_VFSFileOpen()
     * @param diriterator Ptr to an iterator. Start with #vfsIteratorStart
     * @param maxitems On input, the max number of VFSDirInfo structures stored in @p dirItems. On output, the actual number of items.
     * @param diritems Preallocated array that contains a number of VFSDirInfo structures on return.
     * @return A negative value if an error occured (see pi-error.h)
     */
    extern PI_ERR dlp_VFSDirEntryEnumerate
        PI_ARGS((int sd, FileRef dirref, unsigned long *diriterator,
            int *maxitems, struct VFSDirInfo *diritems));

/*@}*/

With this code I get the following surprising output. So I'm wondering, how variable dirRef could change its value.

Enumerate dir '/Photos & Videos', dirRef=445317240, itr=0, dirItems=4
Enumerate OK: bytes=170, dirRef=0, itr=-1, dirItems=4

The library I'm using comes from here: https://github.com/desrod/pilot-link

CodePudding user response:

Passing a variable by value does not provide a way to change it inside the function.

int foo(int x)
{
    x = x *2;
    return x;
}

The variable x is automatic local variable and if you change it inside the function it will not affect the object which was passed to the function by value.

example:

int main(void)
{
    int y = 4;
    foo(y);
    printf("%d\n", y);
}

Output: 4.

You can be sure that the variable passed to this function will not change due to changes made to the parameter inside the function. It is guaranteed by the C standard.

Is it possible, that the macro PI_ARGS changes something in invisible way?

No, you can easily check it yourself by browsing the git repo you posted:

#if ((defined(__STDC__) || defined(SABER)) && !defined(NO_PROTOTYPE)) || defined(__cplusplus) || defined(USE_PROTOTYPE) || defined(CAN_PROTOTYPE)
#   define PI_ARGS(x)       x
#   define PI_CONST const
#else
#   define PI_ARGS(x)       ()
#   define PI_CONST
#endif

PS Sometimes people are confused by macros like functions.

#define X2(x) ((x) = (x) * 2)

int main(void)
{
    int y = 5;
    X2(y);

    printf("%d\n", y);
}

CodePudding user response:

I've found out the cause for the problem with the following code.

It appears, that unsigned long has 64 bits on my machine. I always thought, that for a 64 bit variable I would need unsigned long long. So when function dlp_VFSDirEntryEnumerate() returns, itr is written back as 64 bit value as fault and the variable dirItems becomes partly overwritten. The still only mystery is, that dirItems becomes 0 instead ffffffff.

static const unsigned MIN_DIR_ITEMS = 16;
static const unsigned MAX_DIR_ITEMS = 1024;

    void fetchAlbum(int sd, const unsigned volref, const char *root, const char *name) {
        int dirItems = MIN_DIR_ITEMS;
        VFSDirInfo dirInfos[MAX_DIR_ITEMS];
        char srcAlbumDir[strlen(root)   (name ? strlen(name) : 0)   2];
        FileRef dirRef;
    
        printf("[GUI]     Fetching album '%s' on volume %d\n", name, volref);
    
        strcpy(srcAlbumDir, root); // Album is in /<root>/<name>.
        // Unfiled album is really just root dir.
        if (name) {
            strcat(strcat(srcAlbumDir, "/"), name);
        }
        if (dlp_VFSFileOpen(sd, volref, srcAlbumDir, vfsModeRead, &dirRef) < 0) {
            return;
        }
    
        // Iterate over all the files in the album dir, looking for jpegs and 3gp's and 3g2's (videos).
        enum dlpVFSFileIteratorConstants itr = vfsIteratorStart;
        printf("[DEBUG]   &dirItems='%p', sizeof(dirItems)=%lu\n", &dirItems, sizeof(dirItems));
        printf("[DEBUG]   &dirRef=  '%p', sizeof(dirRef)=%lu\n", &dirRef, sizeof(dirRef));
        printf("[DEBUG]   &itr=     '%p', sizeof(itr)=%lu\n", &itr, sizeof(itr));
        printf("[DEBUG]   sizeof(int)=%lu\n", sizeof(int));
        printf("[DEBUG]   sizeof(long)=%lu\n", sizeof(long));
        printf("[DEBUG]   sizeof(unsigned)=%lu\n", sizeof(unsigned));
        printf("[DEBUG]   sizeof(unsigned long)=%lu\n", sizeof(unsigned long));
        printf("[DEBUG]   sizeof(unsigned long long)=%lu\n", sizeof(unsigned long long));

        for (int dirItems_backup; (dirItems_backup = dirItems) <= MAX_DIR_ITEMS; dirItems *= 2) {
            PI_ERR bytes;
            itr = vfsIteratorStart;
            FileRef dirRef_backup = dirRef; // Unfortunately the following call deletes the original value
            printf("[DEBUG]    Enumerate album '%s', dirRef=%llx, itr=%llx, dirItems=%d\n", srcAlbumDir, dirRef, (long long int)itr, dirItems);
            if ((bytes = dlp_VFSDirEntryEnumerate(sd, dirRef, (unsigned long *)&itr, &dirItems, dirInfos)) < 0) {
                printf("[FATAL]    Enumerate ERROR: bytes=%d, dirRef=%llx, itr=%llx, dirItems=%d\n", bytes, dirRef, (long long int)itr, dirItems);
                break;
            }
            printf("[DEBUG]    Enumerate OK: bytes=%d, dirRef=%llx, itr=%llx, dirItems=%d\n", bytes, dirRef, (long long int)itr, dirItems);
            dirRef = dirRef_backup;
            printf("[DEBUG]    Rescued from backup: dirRef=%llx\n", dirRef);
            if (dirItems < dirItems_backup) {
                break;
            }
        }
        dlp_VFSFileClose(sd, dirRef);
        [.....] // process files from the retrieved dirInfos
        return;
    }

This is the output I get:

[GUI]     Fetching album '(null)' on volume 1
[DEBUG]   &dirItems='0x7ffee6335848', sizeof(dirItems)=4
[DEBUG]   &dirRef=  '0x7ffee6335850', sizeof(dirRef)=8
[DEBUG]   &itr=     '0x7ffee633584c', sizeof(itr)=4
[DEBUG]   sizeof(int)=4
[DEBUG]   sizeof(long)=8
[DEBUG]   sizeof(unsigned)=4
[DEBUG]   sizeof(unsigned long)=8
[DEBUG]   sizeof(unsigned long long)=8
[DEBUG]    Enumerate album '/Photos & Videos', dirRef=20a70070, itr=0, dirItems=16
[DEBUG]    Enumerate OK: bytes=170, dirRef=0, itr=ffffffffffffffff, dirItems=8
[DEBUG]    Rescued from backup: dirRef=20a70070

So the correct code now is:

        unsigned long itr;
        [.....]
            itr = (unsigned long)vfsIteratorStart;
            if ((bytes = dlp_VFSDirEntryEnumerate(sd, dirRef, &itr, &dirItems, dirInfos)) < 0) {

  • Related