regarding myfopen() in c program

荒凉一梦 提交于 2019-12-13 07:57:14

问题


My question is regarding this question: segmentation fault on c K&R fopen and fillbuf.

  1. In this question, in myfopen() function, there is a for loop; what I don't understand is that fp will have value starting from _iob but I don't get what values will fp->flag take after the first three values(which have been fixed). Will they take zero values and one more thing is there that in the answers(see beneath the code) they are saying that we have to use malloc to provide memory space to fp but fp has already been provided memory space because _iob array has space and fp will keep on taking addresses of elements of arrays so what is the need of malloc?Also if all the elements are zero then will the for loop always break at fp =_iob+3?
  2. Secondly in main() function, the mode mentioned is "r" but the system call being used afterwords is write() although it does not show error but why is this possible?
  3. Thirdly, the compiler does not show error or warning on compiling this code but shows a dialog box stating that "f_open.exe has stopped working" and this remains the same even if we write the malloc line(or without) or even if there is "r" mode or "w" mode. So what is wrong?

    #include<fcntl.h>
    #include<unistd.h>
    #include<stdlib.h>
    #define PERM 0644
    #define EOF (-1)
    #define BUFSIZE 1024
    #define OPEN_MAX 20
    
    typedef struct _iobuf{
        int cnt;
        char *ptr;
        char *base;
        int flag;
        int fd;
    } myFILE;
    
    enum _flags {
        _READ   = 01,
        _WRITE  = 02,
        _UNBUF  = 04,
        _EOF    = 010,
        _ERR    = 020
    };
    
    myFILE _iob[OPEN_MAX]={
        {0, (char *) 0, (char *) 0, _READ, 0 },
        {0, (char *) 0, (char *) 0, _WRITE, 1 },
        {0, (char *) 0, (char *) 0, _WRITE | _UNBUF, 2 }
    };
    
    #define stdin (&_iob[0])
    #define stdout (&_iob[1])
    #define stderr (&_iob[2])
    
    #define getc(p)     ( --(p)->cnt>=0 ? (unsigned char) *(p)->ptr++ : _fillbuf(p) )
    
    int _fillbuf(myFILE *fp)
    {
        int bufsize;
    
        if((fp->flag & (_READ|_EOF|_ERR))!=_READ)
            return EOF;
    
        bufsize=(fp->flag & _UNBUF)? 1 : BUFSIZE;
    
        if(fp->base==NULL)
            if((fp->base=(char *)malloc(bufsize))==NULL)
                return EOF;
    
        fp->ptr=fp->base; 
        fp->cnt=read(fp->fd, fp->ptr, bufsize);
    
        if(--fp->cnt<0){
            if(fp->cnt == -1)
                fp->flag |= _EOF;
            else
                fp->flag |= _ERR;
            return EOF;
        }
        return (unsigned char) *fp->ptr++;  
    }
    
    myFILE *myfopen(char *name, char *mode)
    {
        int fd;
        myFILE *fp;
    
        if(*mode!='r' && *mode!='w' && *mode!='a')
              return NULL;
        for(fp=_iob; fp<_iob+OPEN_MAX; fp++)
            if((fp->flag & (_READ | _WRITE))==0)
                break;
    
        if(fp>=_iob+OPEN_MAX)
            return NULL;
    
        if(*mode=='w')
             fd=creat(name, PERM);
        else if(*mode=='a'){
            if((fd=open(name, O_WRONLY, 0))==-1)
                fd=creat(name, PERM);   
            lseek(fd, 0L, 2);
        } else
            fd=open(name, O_RDONLY, 0);
    
        if(fd==-1)
            return NULL;
    
        fp->fd = fd;
        fp->cnt = 0;
        fp->base = NULL;
        fp->flag = (*mode=='r')? _READ : _WRITE;
    
            return fp;    
        } 
    
    int main(int argc, char *argv[])
    {
        myFILE *fp;
       int c;
    
        if((fp=myfopen(argv[1], "r"))!=NULL)
            write(1, "opened\n", sizeof("opened\n"));
    
         while((c=getc(fp))!=EOF)
              write(1, &c, sizeof(c));
    
        return 0;
    }
    

Solution provided was:

myFILE *fp;

if(*mode!='r' && *mode!='w' && *mode!='a')
      return NULL;
for(fp=_iob; fp<_iob+OPEN_MAX; fp++)
    if((fp->flag & (_READ | _WRITE))==0) // marked line
        break;

When you reach the marked line, you try to dereference the fp pointer. Since it is (likely, but not certainly) initialized to zero (but I should say NULL), you are dereferencing a null pointer. Boom. Segfault.

Here's what you need to change.

myFILE *fp = (myFILE *)malloc(sizeof(myFILE));

Be sure to #include to use malloc.

Also your close function should later free() your myFILE to prevent memory leaks.

As you can see the above is the answer given for the linked question: segmentation fault on c K&R fopen and fillbuf


回答1:


Diagnosis in other question is bogus

As noted in the comments, the diagnosis in the other question is bogus. In particular, the loop in myfopen() that reads:

for (fp =_iob; fp <_iob + OPEN_MAX; fp++)
    if ((fp->flag & (_READ | _WRITE)) == 0)
        break;

is perfectly correct. It iterates over the elements of the array _iob, and never encounters a null pointer as claimed. The buffer _iob is initialized for the first three elements; the remainder are all zeros.

Probable causes of trouble

The most likely causes of this program crashing are either:

  1. No argument is provided for the name of the file.
  2. The name provided cannot be opened for reading.

The code shown for main() is:

int main(int argc, char *argv[])
{
    myFILE *fp;
   int c;

    if((fp=myfopen(argv[1], "r"))!=NULL)
        write(1, "opened\n", sizeof("opened\n"));

     while((c=getc(fp))!=EOF)
          write(1, &c, sizeof(c));

    return 0;
}

The code does not check for either of these common problems. It should be more like:

int main(int argc, char *argv[])
{
    myFILE *fp;
    int c;

    if (argc != 2)
    {
        static const char usage[] = "Usage: mystdio filename\n";
        write(2, usage, sizeof(usage)-1);
        return 1;
    }

    if ((fp = myfopen(argv[1], "r")) == NULL)
    {
        static const char filenotopened[] = "mystdio: failed to open file ";
        write(2, filenotopened, sizeof(filenotopened)-1);
        write(2, argv[1], strlen(argv[1]));
        write(2, "\n", 1);
        return 1;
    }

    write(1, "opened\n", sizeof("opened\n"));

    while ((c = getc(fp)) != EOF)
        write(1, &c, sizeof(c));

    return 0;
}

You have to add #include <string.h> because this uses strlen(). The error reporting via file descriptor I/O is clumsy, but this code precludes the use of the regular functions from <stdio.h>. The program does include <stdlib.h> so it would be possible to write exit(EXIT_FAILURE); instead of return 1;, but the net result is the same when the current function is main() — though not in functions called (directly or indirectly) from main().



来源:https://stackoverflow.com/questions/50648250/regarding-myfopen-in-c-program

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!