【问题标题】:can anyone explain me how does this function work?谁能解释一下这个功能是如何工作的?
【发布时间】:2017-06-29 06:49:04
【问题描述】:

我不明白这个函数是做什么的。谁能详细解释一下?

char *my_getline(FILE *stream) {
    char *line = NULL;
    size_t pos = 0;
    int c;

    while ((c = getc(stream)) != EOF) {
        char *newp = realloc(line, pos + 2);
        if (newp == NULL) {
            free(line);
            return NULL;
        }
        line = newp;
        if (c == '\n')
            break;
        line[pos++] = (char)c;
    }
    if (line) {
        line[pos] = '\0';
    }
    return line;
}

如果您可以对我的代码添加评论,我认为这会对我有所帮助。我想在一个字符串中搜索一个子字符串,我找到了这个函数代码。

这是主要功能:

int main(void) {
    char *str, *sub;
    size_t len1, len2, i, count = 0;
    printf("Insert string :\n");
    str = my_getline(stdin);
    printf("insert substring :\n");
    sub = my_getline(stdin);

    if (str && sub) {
        len1 = strlen(str);
        len2 = strlen(sub);
        for (i = 0; i + len2 <= len1; i++) {
            if (!memcmp(str + i, sub, len2)) {
                count++;
                printf("Substring found at index : %d\n", i);
            }
        }
        printf("in the number of: %d\n", count);
        if (count == 0) {
            printf("Substring not found\n");
        }
    }
    free(str);
    free(sub);
    return 0;
}

我了解main函数,但无法理解函数my_getline中的逻辑。

请帮助我理解逻辑。谢谢!

【问题讨论】:

  • 你能缩小范围吗?您了解该功能的哪些部分,哪些不了解?请在每行制作 cmets // understood// ??
  • 它以我能想象到的最低效的方式读取整行...

标签: c string function


【解决方案1】:
char *my_getline(FILE *stream) {
    // pointer to the line to be read:
    char *line = NULL;
    // position of the next character:
    size_t pos = 0;
    // single character:
    int c;

    while ((c = getc(stream)) != EOF) { // read 1 character at a time until EOF
        // allocate a new buffer with room for the char just read + a 0 terminator
        // when `line` is NULL, this is the same as `malloc()`, otherwise it 
        // will change the size of the allocation:
        char *newp = realloc(line, pos + 2);

        // check for errors:
        if (newp == NULL) {
            free(line);
            return NULL;
        }

        // no errors, assign new buffer to `line`:
        line = newp;

        // end of line found: we're done:
        if (c == '\n')
            break;

        // otherwise add new character to the line:
        line[pos++] = (char)c;
    }

    // if there was *anything* to read, add 0 terminator (marks end of string):
    if (line) {
        line[pos] = '\0';
    }
    return line;
}

就是这样。请注意,它的效率非常低,原因有两个:它一次只读取 一个 字符,并且为 每个字符调用realloc()

更好的解决方案是使用例如fgets() 并以合理的块增加缓冲区大小,例如这样:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define GETLINE_CHUNK 1024

static void xrealloc(void *bufPtr, size_t size)
{
    void **buf = bufPtr;
    void *tmp = realloc(*buf, size);
    if (!tmp)
    {
        free(*buf);
        *buf = 0;
    }
    *buf = tmp;
}    

char *my_getline(FILE *stream)
{
    // allocate first chunk:
    char *buf = malloc(GETLINE_CHUNK);
    if (!buf) return 0;
    *buf = 0;
    size_t pos = 0;

    // read up to GETLINE_CHUNK bytes, until newline:
    while (fgets(buf + pos, GETLINE_CHUNK, stream))
    {
        // look for newline:
        char *nlPos = strchr(buf, '\n');
        if (nlPos)
        {
            // found, then our line is complete
            *nlPos = 0;

            // shrink buffer to needed size
            xrealloc(&buf, nlPos-buf+1);
            return buf;
        }

        // set next offset to read
        pos = strlen(buf);

        // increase buffer size to have room for a whole other GETLINE_CHUNK:
        xrealloc(&buf, pos + GETLINE_CHUNK);
        if (!buf) return 0;
    }

    // if nothing was read, free buffer and return NULL:
    if (*buf == 0)
    {
        free(buf);
        buf = 0;
    }
    return buf;
}

int main(void)
{
    char *line = my_getline(stdin);
    if (line)
    {
        puts(line);
        free(line);
    }
    else puts("no input!");
    return 0;
}

【讨论】:

  • @joop 你错了,请仔细看。这只是void *诡计。
  • 是的,我刚刚看到了。我认为它过于复杂。为什么要投到void*
  • @joop 在签名中使用void * 保证可以使用任何类型的指针调用它。您可能会争辩说签名隐藏了它需要双指针的事实。不幸的是,在签名中使用void ** 在调用时需要显式转换。 (而且我讨厌指针类型的显式转换,它们通常是损坏代码的标志)
  • 它还会抑制错误消息和警告。这样,当您将指向 int 的指针或指向字符的指针传递给它时,它就不会被注意到。
  • @joop 你可以在这里选择两个缺点之一:C 允许用void * 表达“任何指针都可以”,但不能用“any double-指针很好”。 (void ** 与例如char ** 不兼容)。如果您在签名中有void **,它只需要调用者编写一个显式转换。调用者仍然可以转换 int *,但仍然不会被注意到(显式转换基本上告诉编译器“闭嘴,我知道我在做什么”)
【解决方案2】:

这个函数给你一行,让我们一步一步来:

char *my_getline(FILE *stream) {
    char *line = NULL; //this is just pointer initialization
    size_t pos = 0; //position variable definition and init 
    int c; //a variable to store temporary character

    while ((c = getc(stream)) != EOF) //read every character till end of file  
    {
        // To dynamically allocate memory, with reference to the 
        // number of character and plus '2' is just to compensate null
        // character and the character(Since pos is 0)

        char *newp = realloc(line, pos + 2); 

        if (newp == NULL) { // this is to check whether memory was alloacted properly or not.  
            free(line); //if not free line 
            return NULL;// break the program and return NULL
        }

        line = newp;// if memory is allocated properly store allocated memory in line pointer

        if (c == '\n') //if new line is detected
            break;// break the while loop
        line[pos++] = (char)c; // store the character in dynamically allocated memory and new character in new location.
    }

    if (line) { //if line contains something then add a null character at last, to complete that string
        line[pos] = '\0';
    }

    return line; //returns the content of line.
}

希望这会有所帮助:)

【讨论】:

  • 嗯,“也可以定义为字符”是错误的。它必须int,因此它可以保存任何unsigned char 值以及EOF
  • @FelixPalmen 只要你快乐
  • @VishwajeetVishu 这实际上非常重要,使用char 会导致难以定位的错误......所以感谢您更新答案。
  • @VishwajeetVishu 你能做同样的事情吗?请为每一行添加评论?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-06-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多