strtok和内存泄漏

我用strtok()写了一个简单的url解析器。 这是代码

#include  #include  typedef struct { char *protocol; char *host; int port; char *path; } aUrl; void parse_url(char *url, aUrl *ret) { printf("Parsing %s\n", url); char *tmp = (char *)_strdup(url); //char *protocol, *host, *port, *path; int len = 0; // protocol agora eh por exemplo http: ou https: ret->protocol = (char *) strtok(tmp, "/"); len = strlen(ret->protocol) + 2; ret->host = (char *) strtok(NULL, "/"); len += strlen(ret->host); //printf("char at %d => %c", len, url[len]); ret->path = (char *)_strdup(&url[len]); ret->path = (char *) strtok(ret->path, "#"); ret->protocol = (char *) strtok(ret->protocol, ":"); // host agora é por exemplo address.com:8080 //tmp = (char *)_strdup(host); //strtok(tmp, ":"); ret->host = (char *) strtok(ret->host, ":"); tmp = (char *) strtok(NULL, ":"); if(tmp == NULL) { if(strcmp(ret->protocol, "http") == 0) { ret->port = 80; } else if(strcmp(ret->protocol, "https") == 0) { ret->port = 443; } } else { ret->port = atoi(tmp); } //host = (char *) strtok(NULL, "/"); } /* * */ int main(int argc, char** argv) { printf("hello moto\n"); aUrl myUrl; parse_url("http://teste.com/Teste/asdf#coisa", &myUrl); printf("protocol is %s\nhost is %s\nport is %d\npath is %s\n", myUrl.protocol, myUrl.host, myUrl.port, myUrl.path); return (EXIT_SUCCESS); } 

正如你所看到的,我使用strtok()很多,所以我可以“切片”url。 我不需要支持不同于http或https的url,所以它完成的方式解决了我的所有问题。 我担心的是(这是在嵌入式设备上运行) – 我是在浪费内存吗? 当我写的东西像

 ret->protocol = (char *) strtok(tmp, "/"); 

然后打电话

 ret->protocol = (char *) strtok(ret->protocol, ":"); 

我首先将指针ret->协议保留在内存中吗? 我想也许我应该设置第一次调用tmp指针,调用strtok指向ret-> protocol到字符串的右边部分(第二次调用)然后free(tmp)。

什么应该是使用strtok的最佳方式?

要直接回答你的问题,strtok只会返回一个指向你给它作为输入的字符串内部位置的指针 – 它不会为你分配新的内存,所以不需要在它给你的任何指针上调用free回来了。

对于它的价值,您还可以查看“strchr”和“strstr”,它们是在字符串中搜索单个字符或序列的非破坏性方式。

另请注意,此处的内存分配存在问题 – 您使用strdup()在解析函数中分配新字符串,然后将该内存块的片段分配给“ret”字段。 因此,你的调用者将负责释放strdup’d字符串,但由于你只是将这个字符串隐式地传回ret中,调用者需要神奇地知道要传递给free的指针。 (可能是ret-> protocol,但可能不是,取决于输入的外观。)

strtok修改了字符串,用NULL替换指定的字符。 由于C中的字符串以NULL结尾,现在看来您的原始指针指向较短的字符串,即使原始字符串仍然存在且仍占用相同数量的内存(但字符替换为NULL)。 我认为,字符串的结尾包含一个双NULL。

简短的回答是:保持指向字符串缓冲区开头的指针,并在解析时指向另一个指向字符串的“当前”指针。 当您使用strtok或以其他方式迭代字符串时,您更新“当前”指针但保留单独的开始指针。 当你完成后,free()开始指针。 没有记忆泄露。

你知道你可以继续使用NULL作为strtok的第一个参数来解析字符串吗?

第一个电话:

 char* token = strtok(string, delimiters); 

然后:

 token = strtok(NULL, other_delimiters); 

这样可以简化代码:

 int parse_url(char *url, aUrl *ret) { //get protocol char* token = strtok(url, "/"); if( token == NULL ) return -1; strcpy(ret->protocol, token); strcat(ret->protocol, "//"); // skip next '/' token = strtok(NULL, "/"); if( token == NULL ) return -1; //get host token = strtok(NULL, "/"); if( token == NULL ) return -1; strcpy(ret->host, token); // get path token = strtok(NULL, "#"); if( token == NULL ) return -1; strcpy(ret->path, token); // ... return 0; } 

你可以看到我有一个返回值来知道解析是否成功完成。

感谢您分享您的代码! 我在valgrind中运行它并修复了由strdup函数生成的两个内存泄漏。

 #include  #include  #include  typedef struct { char *protocol; char *host; int port; char *path; } URL; void parse_url(char *url, URL *ret) { char *tmp = (char *) strdup(url); int len = 0; ret->protocol = (char *) strtok(tmp, "/"); len = strlen(ret->protocol) + 2; ret->host = (char *) strtok(NULL, "/"); len += strlen(ret->host); ret->path = (char *) strdup(&url[len]); ret->path = (char *) strtok(ret->path, "#"); ret->protocol = (char *) strtok(ret->protocol, ":"); ret->host = (char *) strtok(ret->host, ":"); tmp = (char *) strtok(NULL, ":"); if (tmp == NULL) { if (strcmp(ret->protocol, "http") == 0) { ret->port = 80; } else if (strcmp(ret->protocol, "https") == 0) { ret->port = 443; } } else { ret->port = atoi(tmp); } } void free_url(URL *url) { free(url->path); free(url->protocol); } int main(int argc, char** argv) { URL url; parse_url("http://example.com:3000/Teste/asdf#coisa", &url); printf("protocol: %s\nhost: %s\nport: %d\npath: %s\n", url.protocol, url.host, url.port, url.path); free_url(&url); return (EXIT_SUCCESS); }