【问题标题】:C pointer segmentation faultC 指针分段错误
【发布时间】:2013-09-18 20:13:07
【问题描述】:

我有这个 C 代码:

#include <errno.h>
#include <sys/errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include<fcntl.h>
#include <unistd.h>
#include <sys/stat.h>

char *buf;
int c,s; int port=45678;

int recv_m(int c,char *buf);

void get(){

char fileNameBuf[20];
int i=0;
char *s = buf;

if (*s=='/') {
    s++;
    while (*s!=' ') {

        fileNameBuf[i]=*s;
        *s++; 
        i++;
    }
        fileNameBuf[i]='\0';    

}
}

int main(){

//server connected
while ((c = accept(s, (struct sockaddr *) &client, (socklen_t *) &clientlen)) > 0){
    // Do whatever a web server does.

    char recv_buf[50];
    char *r=recv_buf;
    while(recv(c, r , 1, 0)!=0){
         //stores the received message in recv_buf
    }


    recv_buf[i]='\0';

          if (strncmp(recv_buf, "GET ", 4)==0){
                            buf=recv_buf+4;
            get();
          }

}
    return (0);
}

*buf 指向字符串/index.html HTTP/1.0。在函数结束时,fileNameBuf 应该存储字符串index.html

while 循环中的次数应为 10。当我运行此代码时,i = 381 并且出现分段错误(核心转储)。

我做错了什么?

这是整个代码,所以*buf是问题所在?

【问题讨论】:

    标签: c pointers space


    【解决方案1】:

    看来 buf 是一个字符数组。如果是这样,您应该使用 char 指针访问 buf。试试这个:

    int main ()  {
    
        char buf[] = "/index.html HTTP/1.0";
        char fileNameBuf[10];
        int i=0;
    
        char *s = buf;
        if (*s=='/') {
    
            s++;
            while (*s!=' ') {
                fileNameBuf[i]=*s;
                *s++;
                i++;
                printf("%d\n",i);
            }
        }
    }
    

    如果 buf 是一个 char 数组,并且即使数组和指针有几个共同点,在 C 语言中执行 buf++ 也是不合法的。这是来自 Kernighan/Ritchie C 书籍的文本。您可能也将 buf 声明为数组。

    必须牢记数组名称和指针之间的一个区别。指针是一个变量,所以 pa=a 和 pa++ 是合法的。但是数组名不是变量;像 a=pa 和 a++ 这样的结构是非法的。

    由于这个原因,在下面的代码中执行“arr++”会出错。

    int main() {
        int arr[10];
        int *ptr = arr;
    
        arr++; // Would be illegal.
        ptr++; // This is okay.
    }
    

    【讨论】:

    • @user1887339 你的 recv() 调用看起来很可疑。我已更新(编辑)我的答案。您需要在 recv() 调用中传递大于 1 的长度。
    • 我用了 1 个 bcuz 我一个接一个地接收到消息字符,直到 "\r\n\r\n" 出现。
    • Thnx.. 我会编辑它.. 但是,您可能应该一口气读完整本书。 recv() 是一个系统调用,因此非常昂贵。
    • @user1887339 在这种情况下,您收到了多少数据(字节)。如果我们收到超过 20 个字节怎么办? rcv_buf 将能够处理超过 20 个字节,但 get() 函数不能。因此,如果我们超过 20,那么 fileNameBuf 将超过 20 并崩溃。因此,您应该确保读取的 buf 不超过 20 个字节。一种方法是在我们传递足够大的缓冲区时检查 recv() 的返回值,以便我们可以一次性读取它。
    • 最后一点。众所周知,recv() 函数仅在对等 TCP 关闭连接时返回 0。 while() 循环将继续读取,直到 recv() 返回 0。在这种情况下,如果网络服务器在一页中返回大量内容,那么您可能会读取超过 20(甚至 50)个字节!
    【解决方案2】:

    要么您对 buf 中的内容的假设一定是错误的,要么我们对您所说的意思的解释有误:

    *buf 指向字符串"/index.html HTTP/1.1"

    如果您声明 char **buf; 并设置:

    char *str = "/index.html HTTP/1.1";
    char **buf = str;
    

    然后*buf 指向字符串的开头。这就是为什么创建 SSCCE (Short, Self-Contained, Correct Example) 很重要的原因;它消除了歧义。


    SSCCE

    这段代码:

    #include <stdio.h>
    
    const char *buf = "/index.html HTTP/1.1";
    
    static
    void get(void)
    {
        char fileNameBuf[10];
        int i=0;
    
        if (*buf=='/')
        { 
            buf++;
            while (*buf!=' ')
            {
                fileNameBuf[i]=*buf;
                buf++; 
                i++;
                printf("%d\n", i);
            }
        }
        printf("%.*s\n", (int)sizeof(fileNameBuf), fileNameBuf);
    }
    
    int main(void)
    {
        get();
        return 0;
    }
    

    产生这个输出:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    index.html
    

    当然,我必须注意不要打印超出数组末尾的内容。你的数组是最小的;它不能包含包含文件名的字符串(空终止符没有空格)。但它不应该崩溃——如果char *buf = "/index.html HTTP/1.1";!


    完成的代码 - 阶段 1

    这与作为程序提交的内容密切相关。它编译得很干净——我没有尝试过运行它。

    #include <netinet/in.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <strings.h>
    #include <sys/socket.h>
    
    char *buf;
    int c, s; int port = 45678;
    struct sockaddr_in server, client;
    char *ipaddress = "127.0.0.1";
    int clientlen = sizeof(client);
    int portset = 0;
    
    int recv_m(int c, char *buf);
    
    static
    void get(void)
    {
        printf("in get method\n");
        char fileNameBuf[20];
        int i = 0;
        printf("%s\n", buf);
        char *s = buf;
    
        if (*s == '/')
        {
            printf("buf==/\n");
            s++;
            while (*s != ' ')
            {
                // printf("%c\n",*buf);
                // printf("in while\n");
                fileNameBuf[i] = *s;
                s++;
                i++;
                printf("%d\n", i);
            }
            fileNameBuf[i]='\0';
            printf("<<%s>>\n", fileNameBuf);
        }
        else
        {
            printf("!= '/'\n");
        }
    }
    
    int main(void)
    {
        bzero(&server, sizeof(server));
    
        server.sin_family = AF_INET;
        server.sin_addr.s_addr = INADDR_ANY;
        server.sin_port = htons(port);
    
    // if (!inet_aton(ipaddress, &server.sin_addr))
    //  fprintf (stderr, "inet_addr() conversion error\n");
    
        s = socket(AF_INET, SOCK_STREAM, 0); // Create socket
        if (!s) {
            perror("socket");
            exit(0);
        }
    
        if (bind(s, (struct sockaddr *) &server, sizeof(server)) < 0) {
            perror("bind");
            exit(0);
        }
        printf("binded\n");
        if (listen(s, SOMAXCONN) < 0) {
            perror("listen");
            exit(0);
        }
        printf("Waiting for connection\n");
        while ((c = accept(s, (struct sockaddr *) &client, (socklen_t *) &clientlen)) > 0)
        {
            // Do whatever a web server does.
            printf("got connected\n");
            char recv_buf[50];
            char el[4] = "\r\n\r\n";
            int h = 0; int i = 0;
            char *r = recv_buf;
            while (recv(c, r, 1, 0) != 0)
            {
    
                if (h == 4) break;
    
                if (*r == el[h]) {
                    h++;
                }
                r++;
                i++;
    
                if (h == 4) break;
            }
    
            recv_buf[i] = '\0';
    
            printf("%s\n", recv_buf);
    
    
            if (  strncmp(recv_buf, "GET ", 4) == 0) {
                printf("check get\n");
                buf = recv_buf+4;
                printf("%s\n", buf);
                get();
            }
    
        }
        return(0);
    
    }
    

    这不是 SSCCE。所有与设置套接字和从套接字读取相关的代码都应该与手头的问题相切。

    精简代码 - 第 2 阶段

    减少过程包括消除不必要的。

    #include <stdio.h>
    #include <string.h>
    
    char *buf;
    
    static void get(void)
    {
        printf("in get method\n");
        char fileNameBuf[20];
        int i = 0;
        printf("%s\n", buf);
        char *s = buf;
    
        if (*s == '/')
        {
            printf("buf==/\n");
            s++;
            while (*s != ' ')
            {
                fileNameBuf[i] = *s;
                s++;
                i++;
                printf("%d\n", i);
            }
            fileNameBuf[i]='\0';
            printf("<<%s>>\n", fileNameBuf);
        }
        else
        {
            printf("!= '/'\n");
        }
    }
    
    int main(void)
    {
        char recv_buf[50];
        strcpy(recv_buf, "GET /index.html HTTP/1.1\r\n\r\n");
    
        printf("<<%s>>\n", recv_buf);
    
        if (strncmp(recv_buf, "GET ", 4) == 0)
        {
            printf("check get\n");
            buf = recv_buf+4;
            printf("%s\n", buf);
            get();
        }
        return(0);
    }
    

    这也可以干净地编译;不幸的是,它也为我成功运行(GCC 4.8.1,Mac OS X 10.8.4):

    <<GET /index.html HTTP/1.1
    
    >>
    check get
    /index.html HTTP/1.1
    
    
    in get method
    /index.html HTTP/1.1
    
    
    buf==/
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <<index.html>>
    

    这种情况有时会发生;你清理的太狠了。所以,你必须回到前面的代码,更慢地删除东西。

    裁员——第三阶段

    让我们从第 1 阶段获取完整代码并在本地运行它。浏览器可以连接到localhost:45678/index.html,输出为:

    binded
    Waiting for connection
    got connected
    GET /index.html HTTP/1.1
    Host: localhost:45678
    
    check get
    /index.html HTTP/1.1
    Host: localhost:45678
    
    in get method
    /index.html HTTP/1.1
    Host: localhost:45678
    
    buf==/
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <<index.html>>
    

    没有任何内容发送回等待的浏览器(它仍在等待,但很快就会超时)。代码循环回到下一个接受;目前尚不清楚它是否正确关闭了商店,但它在第一个周期没有崩溃。

    所以,这有点徒劳无功……您的代码似乎工作正常。它仍然应该改进——首先将这些全局变量中的每一个都变成main()中的局部变量,然后将buf传递给带有签名void get(char *buf)的修改后的get()

    您显示的代码真的会崩溃吗?如果是这样,调试器对崩溃的原因有何说明?


    防弹——第 4 阶段

    在确定buf 指向的字符串实际上是"/index.html\r\n\r\n" and not"/index.html HTTP/1.1\r\n\r\n"` 之后,很明显我没有确保代码不会读到空终止字符串的末尾,也不会写到缓冲区的末尾。然而,这正是 SSCCE 如此重要的原因,以及诊断打印如此重要的原因。如果问题包含正在扫描的实际字符串,那么发现问题会简单得多。

    这段代码更接近于防弹。在其他主要更改中,它尝试在单个 recv() 操作中读取请求,而不是逐字节读取请求。这使避免recv() 溢出的责任。所有的全局变量都消失了; buf 作为参数传递给 get()get() 已被编写用于检测 EOS 和超长名称,以及处理直到第一个空格的名称。它仍然具有文件名中每个字符的调试代码。 main() 中的代码已被修饰,以发送回一个有效 HTTP 或足够有效 HTTP 的响应,其中包含每次处理时都会更改的一些 HTML。看到浏览器发出的请求很有趣。还有一个错误报告功能,它写入标准错误,采用格式字符串和参数,如printf() 等,还为系统错误添加正确的错误号和消息,然后以失败状态退出。这使得错误报告不那么痛苦;对于每个错误,一行调用就足够了,而不是 3 或 4 行(取决于您选择的格式)。这些错误也可能比perror() 更具表现力。

    #include <ctype.h>
    #include <errno.h>
    #include <netinet/in.h>
    #include <stdarg.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <strings.h>
    #include <sys/socket.h>
    #include <unistd.h>
    
    static void err_exit(const char *fmt, ...);
    
    static
    void get(char *buf)
    {
        printf("in get method\n");
        char fileNameBuf[256];
        size_t i = 0;
        printf("%s\n", buf);
        char *s = buf;
    
        if (*s == '/')
        {
            printf("buf==/\n");
            s++;
            while (*s != '\0' && *s != ' ' && i < sizeof(fileNameBuf))
            {
                printf("i = %3d: c = %3d = 0x%.2X = '%c'\n",
                       (int)i, *s, *s & 0xFF, isprint(*s) ? *s : '.');
                fileNameBuf[i++] = *s++;
            }
            fileNameBuf[i]='\0';
            printf("<<%s>>\n", fileNameBuf);
        }
        else
        {
            printf("!= '/'\n");
        }
    }
    
    int main(void)
    {
        char *buf;
        int fd;
        int s;
        int port = 45678;
        struct sockaddr_in server, client;
        int clientlen = sizeof(client);
        int msgnum = 314159;
    
        bzero(&server, sizeof(server));
        server.sin_family = AF_INET;
        server.sin_addr.s_addr = INADDR_ANY;
        server.sin_port = htons(port);
    
        s = socket(AF_INET, SOCK_STREAM, 0);
        if (!s)
            err_exit("socket()\n");
        if (bind(s, (struct sockaddr *) &server, sizeof(server)) < 0)
            err_exit("bind()\n");
        printf("bound to address\n");
        if (listen(s, SOMAXCONN) < 0)
            err_exit("listen()\n");
        printf("Waiting for connection\n");
    
        while ((fd = accept(s, (struct sockaddr *) &client, (socklen_t *) &clientlen)) > 0)
        {
            printf("got connection\n");
            char recv_buf[4096];
            char el[5] = "\r\n\r\n";
            ssize_t length;
            /* Read message in one go; leave space for a null at the end */
            if ((length = recv(fd, recv_buf, sizeof(recv_buf)-1, 0)) > 0)
            {
                recv_buf[length] = '\0';
                if (strstr(recv_buf, el) == 0)
                    err_exit("Incomplete message (%d bytes and no CRLF, CRLF pair)\n", length);
                printf("%d: <<%s>>\n", (int)length, recv_buf);
    
                if (strncmp(recv_buf, "GET ", 4) == 0)
                {
                    printf("check get\n");
                    buf = recv_buf + 4;
                    printf("<<%s>>\n", buf);
                    get(buf);
                    char message[256];
                    char format1[] =
                        "<html><head><title>Hello World!</title></head>"
                        "<body><h1>This is no fun at all (%d).</h1></body></html>\r\n\r\n";
                    int msg_len = snprintf(message, sizeof(message), format1, msgnum++);
                    char format2[] =
                        "HTTP/1.1 200 OK\r\n"
                        "Content-Type: text/html\r\n"
                        "Content-Length: %d\r\n"
                        "Content-Encoding: UTF-8\r\n\r\n%s";
                    char response[1024];
                    size_t nbytes = snprintf(response, sizeof(response), format2,
                                             msg_len, message);
                    write(fd, response, nbytes);
                }
            }
            close(fd);
        }
        return(0);
    }
    
    static void err_exit(const char *fmt, ...)
    {
        int errnum = errno;
        va_list args;
        va_start(args, fmt);
        vfprintf(stderr, fmt, args);
        va_end(args);
        fprintf(stderr, "%d: %s\n", errnum, strerror(errnum));
        exit(1);
    }
    

    【讨论】:

    • 但是buf是一个指向字符串recv_buf的指针,还是我的代码做错了?
    猜你喜欢
    • 2016-02-24
    • 1970-01-01
    • 2012-04-10
    • 2021-04-10
    • 1970-01-01
    • 2021-05-25
    • 1970-01-01
    • 2016-01-02
    • 1970-01-01
    相关资源
    最近更新 更多