Thread: Help with malloc and pointers.

  1. #1
    Registered User
    Join Date
    Nov 2013
    Posts
    16

    Help with malloc and pointers.

    One thing of possible importance to know beforehand is that, while this program is meant to be run on Linux, I have a Mac to write on and won't be able to get to the computer lab for maybe ten hours.

    So I've got a homework problem where, ultimately, I have to sort lines in a text file (including strings and integers) using qsort. The professor wants us to do this by reading the whole file into memory. She even gave us some bits of code to integrate into our program to help us.
    I'm stuck on the first part.
    Namely, I'm trying to allocate the memory for the file and then read it into an array. To check that it works, I'm trying to print out the array. I've been getting two main error reports, though they may be pointing to the same problem. The first is a warning before I run the program, saying "passing 'const char *' to parameter of type 'char *' discards qualifiers". The second is when my program quits on me with "Segmentation fault: 11".
    I feel like I really don't know what I'm doing. Here's my code:

    Code:
    ​#include <stdio.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <unistd.h>
    #include <stdlib.h>
    
    
    int filesize(char * filename)
    {
        struct stat status;
        if (stat(filename, &status) == -1)
        {
            return -1;
        }
        else
        {
            return (int) status.st_size;
        }
    }
    
    
    int main(int argc, const char * argv[])
    {
        FILE *read;
        char in_name;
        int n = 0;
    
        printf("What file should be read?\n");
        if (scanf("%s", &in_name) != 1)
        {
            printf("Invalid input.\n");
            return 1;
        }
    
        read = fopen(&in_name,"r");
    
        if (read == NULL)
        {
            printf("Cannot open input file.\n");
            return 1;
        }
    
        char * data = malloc(filesize(argv[1]));
    
        data[n] = fgetc(read);
        while (data[n] != EOF)
        {
            if (data[n] == '\n')
                data[n] = '\0';
            data[++n] = fgetc(read);
        }
    
        for (int i = 0; i <= n; i++)
        {
            printf("%d \n", data[i]);
        }
    
        fclose(read);
        return 0;
    }
    Two parts of this code are basically regurgitated from what the professor told us.

    The first is supposed to help find out how much memory to allocate.
    Code:
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <unistd.h>
    /* returns size of file *filename in bytes, or -1 on error */
    int filesize(char * filename) {
        struct stat status;
        if (stat(filename, &status) == -1) {
            return -1;
        }
        else {
            return (int) status.st_size;
        }
    }
    The second, she said:
    You can use malloc to get space in memory:
    Code:
    char * data = malloc(filesize(argv[1]));

    Now you can operate on 'data' like an array -- the first character is data[0], the next one data[1], etc.

    This is what the error
    "passing 'const char *' to parameter of type 'char *' discards qualifiers" is referring to.

  2. #2
    Registered User
    Join Date
    Nov 2013
    Posts
    31
    Code:
    int main(int argc, const char * argv[]) //remove the const key word
    
      if (scanf("%s", &in_name) != 1) //%s is used for strings, %c is used for char
    
    
    
    
    
    Last edited by new2C-; 11-19-2013 at 12:59 PM.

  3. #3
    Registered User
    Join Date
    Nov 2012
    Posts
    1,393
    Don't remove const from your main declaration. I doubt this is allowed anyway. The operating system gives you a const char * array it is not guaranteed to be writable.

    The problem is, you declared your filesize function in this way:

    Code:
    int filesize(char * filename);
    But when you say filename(argv[1]) you are giving it a const char *. The basic rule is to make your function take a const pointer if possible, or even better to make it take a const * restrict pointer.

    See also: stat(3): file status - Linux man page

    Notice stat accepts a const char * pointer, so if you make your filesize function take a const pointer, you can also use that with stat.

  4. #4
    Registered User
    Join Date
    Nov 2010
    Location
    Long Beach, CA
    Posts
    5,909
    Quote Originally Posted by c99tutorial View Post
    Don't remove const from your main declaration. I doubt this is allowed anyway. The operating system gives you a const char * array it is not guaranteed to be writable.
    Actually, you have that completely backwards. The standard says const should not be used to qualify argv in the declaration of main, and argv and the strings pointed to must be writable:
    Quote Originally Posted by C99 5.1.2.2.1
    1 The function called at program startup is named main. The implementation declares no
    prototype for this function. It shall be defined with a return type of int and with no
    parameters:
    int main(void) { /* ... */ }
    or with two parameters (referred to here as argc and argv, though any names may be
    used, as they are local to the function in which they are declared):
    int main(int argc, char *argv[]) { /* ... */ }
    or equivalent;9) or in some other implementation-defined manner.

    2 If they are declared, the parameters to the main function shall obey the following
    constraints:
    — The value of argc shall be nonnegative.
    — argv[argc] shall be a null pointer.
    — If the value of argc is greater than zero, the array members argv[0] through
    argv[argc-1] inclusive shall contain pointers to strings, which are given
    implementation-defined values by the host environment prior to program startup. The
    intent is to supply to the program information determined prior to program startup
    from elsewhere in the hosted environment. If the host environment is not capable of
    supplying strings with letters in both uppercase and lowercase, the implementation
    shall ensure that the strings are received in lowercase.
    — If the value of argc is greater than zero, the string pointed to by argv[0]
    represents the program name; argv[0][0] shall be the null character if the
    program name is not available from the host environment. If the value of argc is
    greater than one, the strings pointed to by argv[1] through argv[argc-1]
    represent the program parameters.
    The parameters argc and argv and the strings pointed to by the argv array shall
    be modifiable by the program, and retain their last-stored values between program
    startup and program termination.

    9) Thus, int can be replaced by a typedef name defined as int, or the type of argv can be written as
    char ** argv, and so on.
    @RElainaJ:
    The problem is the const you put on argv. That declared argv as an array of pointers to const char, meaning the chars pointed to (the command line params) can't be modified. It's like a contract between you and the compiler. So you promised not to modify them, but then you passed them to a function (filesize()) that does not make such a promise, thus the compiler complains since you violate that contract. It's safe to pass non-const variables to const parameters but not the other way around. Since the teacher provided the filesize() function without the const qualifier, leave it that way. Remove const from argv in main.
    Last edited by anduril462; 11-19-2013 at 01:54 PM. Reason: formatting

  5. #5
    Registered User
    Join Date
    Nov 2013
    Posts
    16
    Thank you all so much for the quick responses! I'll try to fix it when I have time later this afternoon.

  6. #6
    Registered User
    Join Date
    Nov 2013
    Posts
    16
    So the const issue is fixed but I'm still getting a seg fault. I had a friend try the code on his Linux and he got this same thing. Specifically, mine says "Segmentation fault: 11" while his just says "segmentation fault".
    Code:
    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <unistd.h>
    #include <stdlib.h>
    
    
    int filesize(char * filename)
    {
        struct stat status;
        if (stat(filename, &status) == -1)
        {
            return -1;
        }
        else
        {
            return (int) status.st_size;
        }
    }
    
    
    int main(int argc, char * argv[])
    {
        FILE *read;
        char in_name;
        int n = 0;
    
        printf("What file should be read?\n");
        if (scanf("%s", &in_name) != 1)
        {
            printf("Invalid input.\n");
            return 1;
        }
    
        read = fopen(&in_name,"r");
    
        if (read == NULL)
        {
            printf("Cannot open input file.\n");
            return 1;
        }
    
        char * data = malloc(filesize(argv[1]));
    
        data[n] = fgetc(read);
        while (data[n] != EOF)
        {
            if (data[n] == '\n')
                data[n] = '\0';
            data[++n] = fgetc(read);
        }
    
        for (int i = 0; i <= n; i++)
        {
            printf("%d \n", data[i]);
        }
    
        fclose(read);
        return 0;
    }
    Last edited by RElainaJ; 11-19-2013 at 04:42 PM. Reason: Format

  7. #7
    Registered User
    Join Date
    Nov 2010
    Location
    Long Beach, CA
    Posts
    5,909
    It's really hard to say what is causing the seg fault if you don't tell us what input you provide, the contents of the file, etc. Quick note, the difference between your error message and your friends is nothing of concern. Different OSes/versions may report a seg fault slightly differently, but the result is the same. You're accessing a segment of memory in a way that you are not supposed to: writing to read-only memory, reading or writing from invalid memory, dereferencing a NULL pointer, etc.
    Code:
        char in_name;
        int n = 0;
     
        printf("What file should be read?\n");
        if (scanf("%s", &in_name) != 1)
    How many characters can you fit in the variable in_name? Hint, it's less than 2. If you want to store a whole string of characters, you should declare a char array and read into that. I prefer fgets for reading a line of text instead of scanf, just remember to remove the trailing new line if it's there (see here).

    You should also decide whether you want to read in the file name with scanf/fgets or via command line parameters (argv). If you use argv, make sure you check that argc is big enough. If you don't provide any filename, argv[1] will be NULL and filesize will call stat with a NULL filename, probably resulting in a seg fault.

    You're reading in the file and turning a newline to a null character, to separate lines. That's good. But when you print, you are printing each character as a decimal number, so (assuming ASCII or similar character set), you will read 'A' and print 65. You also aren't sorting anything.

    It's not totally clear what the program should do however. You would need to provide us the complete assignment description for us to help you further, and a correct sample input file and correct program output would also be useful.

  8. #8
    Registered User
    Join Date
    Nov 2012
    Posts
    1,393
    You don't need to choose between fgets and scanf, just use both (sscanf)

    Code:
    char mybuf[10000], filename[10000];
    
    fgets(mybuf, sizeof(mybuf), stdin);
    sscanf(mybuf, " %s", filename);

  9. #9
    Registered User
    Join Date
    Nov 2013
    Posts
    16
    Ultimately, the program is supposed to be able to sort a combination of integers and char arrays.
    For example, my text file reads
    "goat4
    nine
    1
    2
    pink"
    and should, in the end, read
    "1
    2
    4
    goat
    nine
    pink"

    I have to admit, using char arrays instead of strings really confuses me in C, but I thought in_name would work. In an earlier project, I did this.
    Code:
    FILE *read;
    char in_name;
    
    printf("What file should be read?\n");
       if (scanf("%s", &in_name) != 1)
       {
          printf("Invalid input.\n");
          return 1;
       }
    
    read = fopen(&in_name,"r");
       if (read == NULL)
       {
          printf("Cannot open input file.\n");
          return 1;
       }

    That worked just fine.


    I just noticed I was trying to print decimal numbers. Whoops. Changed it. I understand that I'm not sorting anything yet, I'm just trying to allocate space and read the file at the moment.

    The error seems to be occurring at "data[n] = fgetc(read);" as I finally tracked down the library in Xcode and was able to run it in the application. What's more, now it says "(lldb)" and clicking on that gives me the following.
    "argv = (char **)0x7fff5fbff8f0n = (int)0
    data = (char *)NULL
    read = (FILE *) 0x7fff7d88d280"

    I feel vaguely like I have more information now, though I'm not quite sure how to interpret it.
    Last edited by RElainaJ; 11-19-2013 at 05:53 PM. Reason: Format

  10. #10
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    You say it worked just fine, but it didn't. I'm surprised you got away with it, as you must have been overwriting your other variables -- I guess you only needed the filename long enough to open it and then clobbered it with your real data as you read it in.

    There are no strings in C, alas, so char arrays will have to do.

    If your data is null, that means you asked malloc for a non-positive number of bytes, or there was some other system error (rather unlikely). Your function returns -1 for a reason; you should check your return value before blindly passing it off to malloc.

  11. #11
    Registered User
    Join Date
    Nov 2012
    Posts
    1,393
    Quote Originally Posted by RElainaJ View Post
    I have to admit, using char arrays instead of strings really confuses me in C, but I thought in_name would work. In an earlier project, I did this.
    ...
    I feel vaguely like I have more information now, though I'm not quite sure how to interpret it.
    The fact that you did it before and it happened to work doesn't mean anything. Welcome to Undefined Behaviour (UB). If you overwrite the bounds of your array there's a good chance that it will happen to work sometimes, because there are so many spots available in memory to your program, you might happen to hit one that will sort of work some of the time under certain conditions. Obviously however this is not a correct program.

  12. #12
    Registered User
    Join Date
    Nov 2013
    Posts
    16
    Alright, so do I need to make a char array with an indeterminate size?

  13. #13
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    No such thing as indeterminate size. There is, I think, such a thing as FILENAME_MAX which you could use as the size of your array (the value depends on your system, but it's the longest a filename is supposed to be able to be on that system).

  14. #14
    Registered User
    Join Date
    Nov 2013
    Posts
    16
    So, does this work?
    Code:
    FILE *read;
    char in_name[255];
    
    printf("What file should be read?\n");
    if (scanf("%s", in_name) != 1)
       {
          printf("Invalid input.\n");
          return 1;
       }
    read = fopen(in_name,"r");

  15. #15
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    You need to provide scanf with the variable you want the input information to be stored in.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 7
    Last Post: 05-19-2010, 02:12 AM
  2. Malloc and pointers
    By taurus in forum C Programming
    Replies: 8
    Last Post: 10-25-2008, 09:00 AM
  3. Problem with malloc and pointers to pointers
    By mike_g in forum C Programming
    Replies: 7
    Last Post: 03-29-2008, 06:03 PM
  4. malloc and pointers
    By St0rM-MaN in forum C Programming
    Replies: 14
    Last Post: 06-20-2007, 11:03 AM
  5. Pointers and malloc
    By Unregistered in forum C Programming
    Replies: 2
    Last Post: 03-21-2002, 09:20 PM

Tags for this Thread