C指针分割错误

时间:2013-09-14 04:08:34

标签: c pointers space

我有这个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是问题吗?

2 个答案:

答案 0 :(得分:2)

看来buf是一个char数组。如果是这样,您应该使用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数组,尽管数组和指针有几个共同点,但是执行buf ++是不合法的C.Here是来自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.
}

答案 1 :(得分: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>>

有时会发生这种情况;你在清理过程中太无情了。因此,您必须回到之前的代码并更慢地删除内容。

撤销 - 第3阶段

让我们从第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传递给带有签名get()的已修改void get(char *buf)

你展示的代码真的会让你崩溃吗?如果是这样,调试器会说出它崩溃的原因是什么?


防弹 - 第4阶段

确定buf指向的字符串实际上是"/index.html\r\n\r\n" and not“/ index.html HTTP / 1.1 \ r \ n \ r \ n”之后,很明显我是在疏忽不确保代码不会读取超过null终止字符串的结尾,也不会写入缓冲区的末尾。然而,这正是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);
}