Thread: C code critique

  1. #1
    Registered User
    Join Date
    Nov 2011
    Posts
    52

    C code critique

    I am still pretty new to C and I learned a lot from this forum. This is a working code (In windows by the way). I already learned that you don't have to cast the return of malloc. I am wondering if you can give me tips on how can I improve my C coding techniques or if you spot a bad habbit (like casting malloc's return value).

    This function will create a file making sure all sub-directory exists before creating the file.

    Code:
    int edb_create_file(char * path, FILE *fl) {
        char *dirPath = strdup(path);
        if(!dirPath)
            return EDB_OUT_OF_MEMORY;
        // This will remove the file name so it will only have the directory
        *(strrchr(dirPath, '\\') + 1) = '\0';
    
        // Check if parent dir exist if not make sure
        // all sub-dir exist and created before adding the file
        if(access(dirPath, F_OK) != 0)
        {
            char *curPath = malloc(strlen(dirPath));    // Current Path
            if(!curPath)
                return EDB_OUT_OF_MEMORY;
    
            char *nxtPath = strstr(dirPath, "\\");      // Get the first occurance of '\\'
            do {
                ++nxtPath;                              // Skip "\\" so strstr() will not see it next time.
                // Copy dirPath until nxtPath
                strncpy(curPath, dirPath, nxtPath - dirPath);
                *(curPath + (nxtPath - dirPath)) = '\0';// Add '\0' to the end of curPath
                if(access(curPath, F_OK) != 0)          // Check if dir doesn't exist
                    if(mkdir(curPath) != 0)             // Create dir
                        return EDB_CREATE_FAILED;       // Exit when failed
    
                nxtPath = strstr(nxtPath, "\\");        // Get the next dir
            } while(nxtPath);
            free(curPath);
        }
        free(dirPath);
    
        fl = fopen(path,"wb");
        if(fl)
            return EDB_SUCCESS;
        else
            return EDB_CREATE_FAILED;
    }

  2. #2
    Registered User
    Join Date
    Sep 2006
    Posts
    8,868
    I haven't run it, but it reads and looks, just wonderful.

    Well done!!

  3. #3
    - - - - - - - - oogabooga's Avatar
    Join Date
    Jan 2008
    Posts
    2,808
    strrchr will return NULL if it doesn't find a \, so you're code assumes at least one \. Since you're creating a dup of the path anyway, you may as well use the fact that you can definitely write to it: Find the first \ in dirPath; save the next char and write a zero there; use dirPath for dir tests; write saved char back; stir briskly.

  4. #4
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,612
    The function does not in fact return a file to the caller. You want to reassign the FILE*, so the proper parameter type would be FILE**.

  5. #5
    Registered User
    Join Date
    Nov 2011
    Posts
    52
    Quote Originally Posted by oogabooga View Post
    strrchr will return NULL if it doesn't find a \, so you're code assumes at least one \. Since you're creating a dup of the path anyway, you may as well use the fact that you can definitely write to it: Find the first \ in dirPath; save the next char and write a zero there; use dirPath for dir tests; write saved char back; stir briskly.
    Thanks! That makes it more efficient. I did add an extra free so it will free dirPath on every code path.

    @whiteflags
    Can you explain further why do I need **FILE instead of *FILE? I cannot fully grasp the idea yet. Why it will not return a file if I use *FILE?

    Code:
    int edb_create_file(char * path, FILE **fl) {
        char *dirPath = strdup(path);
        if(!dirPath)
            return EDB_OUT_OF_MEMORY;
    
        // Check if parent dir exist if not make sure
        // all sub-dir exist and created before adding the file
        if(access(dirPath, F_OK) != 0)
        {
            char *nxtPath = strstr(path, "\\");         // Get the first occurance of '\\'
            do {
                ++nxtPath;                              // Skip "\\" so strstr() will not see it next time.
                // Copy path until nxtPath
                strncpy(dirPath, path, nxtPath - path);
                *(dirPath + (nxtPath - path)) = '\0';   // Add '\0' to the end of curPath
                if(access(dirPath, F_OK) != 0)          // Check if dir doesn't exist
                    if(mkdir(dirPath) != 0) {           // Create dir
                        free(dirPath);                  // Free before return
                        return EDB_CREATE_FAILED;       // Exit when failed
                    }
                nxtPath = strstr(nxtPath, "\\");        // Get the next dir
            } while(nxtPath);
        }
        free(dirPath);
    
        *fl = fopen(path,"wb");
        if(*fl)
            return EDB_SUCCESS;
        else
            return EDB_CREATE_FAILED;
    }
    Thank you for the tips!

  6. #6
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,612
    As you likely know, arguments are passed by value in C. This includes pointers, but pointers work because they contain the address of the object you want to actually change. You need to use FILE** because you want to change the file pointer, not the underlying file object, so you need another level of indirection.

    If I called your function like this:

    Code:
    FILE* file;
    endb_create_file("path/to/file.foo", file);
    It will just change a copy of the file pointer, not my file pointer.

    If I could do this:
    Code:
    FILE* file;
    endb_create_file("path/to/file.foo", &file);
    Since I passed the location of my file pointer, you can actually change it.

  7. #7
    Registered User
    Join Date
    Jun 2005
    Posts
    6,815
    You might want to check that the malloc() call is allocating enough memory.

    A line of the form
    Code:
         char *curPath = malloc(strlen(dirPath));
    often needs to add one before calling malloc(), to allow for copying a training zero.
    Right 98% of the time, and don't care about the other 3%.

    If I seem grumpy or unhelpful in reply to you, or tell you you need to demonstrate more effort before you can expect help, it is likely you deserve it. Suck it up, Buttercup, and read this, this, and this before posting again.

  8. #8
    Registered User
    Join Date
    Nov 2011
    Posts
    52
    Quote Originally Posted by whiteflags View Post
    As you likely know, arguments are passed by value in C. This includes pointers, but pointers work because they contain the address of the object you want to actually change. You need to use FILE** because you want to change the file pointer, not the underlying file object, so you need another level of indirection.

    If I called your function like this:

    Code:
    FILE* file;
    endb_create_file("path/to/file.foo", file);
    It will just change a copy of the file pointer, not my file pointer.

    If I could do this:
    Code:
    FILE* file;
    endb_create_file("path/to/file.foo", &file);
    Since I passed the location of my file pointer, you can actually change it.
    I kept on thinking about what you said last night and I just finally realized how it works while brushing my teeth this morning . Thank you very much that will help me a lot.

    @grumpy
    I wont need to add an extra char this time since I am replacing the last '\\' with '\0' but I will remember what you said thanks.

  9. #9
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by mnd22
    I wont need to add an extra char this time since I am replacing the last '\\' with '\0' but I will remember what you said thanks.
    Write a comment to document that.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 20
    Last Post: 12-09-2011, 03:28 PM
  2. Linked List question from a newb(Need code Critique)
    By Gatt9 in forum C++ Programming
    Replies: 2
    Last Post: 01-08-2006, 03:11 AM
  3. Critique my code?
    By bigbadbowlindud in forum C++ Programming
    Replies: 20
    Last Post: 06-24-2004, 12:54 PM
  4. could ya'll critique this code
    By linuxdude in forum C Programming
    Replies: 0
    Last Post: 02-19-2004, 09:55 PM
  5. C 101 critique, please?
    By adobephile in forum C Programming
    Replies: 13
    Last Post: 01-01-2003, 07:05 PM