所以我正在编写一个解析路径的小函数,它看起来像这样:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int parse_path() {
char *pathname = "this/is/a/path/hello";
int char_index = 0;
char current_char = pathname[char_index];
char *buffer = malloc(2 * sizeof(char));
char *current_char_str = malloc(2 * sizeof(char));
while (current_char != '\0' && (int)current_char != 11) {
if (char_index == 0 && current_char == '/') {
char_index++; current_char = pathname[char_index];
continue;
}
while (current_char != '/' && current_char != '\0') {
current_char_str[0] = current_char;
current_char_str[1] = '\0';
buffer = (char *)realloc(buffer, (strlen(buffer) + 2) * sizeof(char));
strcat(buffer, current_char_str);
char_index++; current_char = pathname[char_index];
}
if (strlen(buffer)) {
printf("buffer(%s)\n", buffer);
current_char_str[0] = '\0';
buffer[0] = '\0';
}
char_index++; current_char = pathname[char_index];
}
};
int main(int argc, char *argv[]) {
parse_path();
printf("hello\n");
return 0;
}
现在,我的代码中存在未定义的行为,看来main方法中的printf
调用正在更改buffer
变量...如您所见,该程序的输出为:
buffer(this)
buffer(is)
buffer(a)
buffer(path)
buffer(hello)
buffer(buffer(%s)
)
buffer(hello)
hello
我看过其他提到相同问题的文章,人们告诉我使用static
char数组等,但这似乎无济于事。
有什么建议吗?
由于某种原因,在此程序中,"hello"
的{{1}}字符串有时出现在我的printf
变量中。
答案 0 :(得分:4)
塞巴斯蒂安,如果您在@PaulOgilvie回答之后仍遇到问题,则很可能是由于不了解他的回答。您的问题是由于buffer
被分配,但未初始化。当您调用malloc
时,它将分配至少一个请求大小的块,并返回一个指向新块起始地址的指针-但对新块的内容不执行任何操作-表示该块是完全随机值,恰好在新块的地址范围内。
因此,当您第一次调用strcat(buffer, current_char_str);
时,buffer
中除了随机垃圾而且没有 nul-termination 字符之外,什么都没有-您确实会调用 Undefined行为。 (在buffer
中没有字符串结尾)
要解决该错误,只需将buffer
设置为空字符串,只需将第一个字符设置为 nul-termination ,即可将其设为空字符串字符,或使用calloc
来分配块,以确保所有字节均设置为零。
例如:
int parse_path (const char *pathname)
{
int char_index = 0, ccs_index = 0;
char current_char = pathname[char_index];
char *buffer = NULL;
char *current_char_str = NULL;
if (!(buffer = malloc (2))) {
perror ("malloc-buffer");
return 0;
}
*buffer = 0; /* make buffer empty-string, or use calloc */
...
也不要对路径或数字(包括0
和2
进行硬编码,但是现在让它们滑动)。在"this/is/a/path/hello"
中对parse_path()
进行硬编码是一个非常无用的功能。相反,将您的pathname
变量作为参数,这样我就可以采用您要发送给它的任何路径...
虽然realloc
一次包含2个字符的整个想法效率很低,但是您始终需要realloc
使用临时指针,而不是指针本身。为什么? realloc
可以并且确实失败,当失败时,它返回NULL
。如果您使用的是指针本身,则在发生故障时将用NULL
覆盖当前的指针地址,从而使地址永久丢失到现有内存块中,从而造成内存泄漏。相反,
void *tmp = realloc (buffer, strlen(buffer) + 2);
if (!tmp) {
perror ("realloc-tmp");
goto alldone; /* use goto to break nested loops */
}
...
}
alldone:;
/* return something meaningful, your function is type 'int' */
}
一个包含修复程序和临时指针的简短示例为:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int parse_path (const char *pathname)
{
int char_index = 0, ccs_index = 0;
char current_char = pathname[char_index];
char *buffer = NULL;
char *current_char_str = NULL;
if (!(buffer = malloc (2))) {
perror ("malloc-buffer");
return 0;
}
*buffer = 0; /* make buffer empty-string, or use calloc */
if (!(current_char_str = malloc (2))) {
perror ("malloc-current_char_str");
return 0;
}
while (current_char != '\0' && (int) current_char != 11) {
if (char_index == 0 && current_char == '/') {
char_index++;
current_char = pathname[char_index];
continue;
}
while (current_char != '/' && current_char != '\0') {
current_char_str[0] = current_char;
current_char_str[1] = '\0';
void *tmp = realloc (buffer, strlen(buffer) + 2);
if (!tmp) {
perror ("realloc-tmp");
goto alldone;
}
strcat(buffer, current_char_str);
char_index++;
current_char = pathname[char_index];
}
if (strlen(buffer)) {
printf("buffer(%s)\n", buffer);
current_char_str[0] = '\0';
buffer[0] = '\0';
}
if (current_char != '\0') {
char_index++;
current_char = pathname[char_index];
}
}
alldone:;
return ccs_index;
}
int main(int argc, char* argv[]) {
parse_path ("this/is/a/path/hello");
printf ("hello\n");
return 0;
}
(注意:),您的逻辑在上面已经相当折磨了,您可以使用PATH_MAX
大小的固定缓冲区(包括limits.h
)并省去分配。否则,您可以应该为buffer
分配一些预期数量的字符,例如strlen (pathname)
,这样可以确保每个路径组件有足够的空间而无需重新分配。我宁愿分配1000个字符,也不愿意加索引担心一次重新分配2个字符...)
使用/输出示例
> bin\parsepath.exe
buffer(this)
buffer(is)
buffer(a)
buffer(path)
buffer(hello)
hello
无需分配的更直接的方法
只需使用大小为PATH_MAX
的缓冲区或分配的大小至少为strlen (pathname)
的缓冲区,您就可以简单地逐步降低字符串,而无需进行任何重新分配,例如
#include <stdio.h>
#include <limits.h> /* for PATH_MAX - but VS doesn't provide it, so we check */
#ifndef PATH_MAX
#define PATH_MAX 2048
#endif
void parse_path (const char *pathname)
{
const char *p = pathname;
char buffer[PATH_MAX], *b = buffer;
while (*p) {
if (*p == '/') {
if (p != pathname) {
*b = 0;
printf ("buffer (%s)\n", buffer);
b = buffer;
}
}
else
*b++ = *p;
p++;
}
if (b != buffer) {
*b = 0;
printf ("buffer (%s)\n", buffer);
}
}
int main (int argc, char* argv[]) {
char *path = argc > 1 ? argv[1] : "this/is/a/path/hello";
parse_path (path);
printf ("hello\n");
return 0;
}
使用/输出示例
> parsepath2.exe
buffer (this)
buffer (is)
buffer (a)
buffer (path)
buffer (hello)
hello
或
> parsepath2.exe another/path/that/ends/in/a/filename
buffer (another)
buffer (path)
buffer (that)
buffer (ends)
buffer (in)
buffer (a)
buffer (filename)
hello
现在,您可以将要解析的任何路径作为自变量传递到程序,并且无需进行任何更改或重新编译即可对其进行解析。仔细研究一下,如果您有任何疑问,请告诉我。
答案 1 :(得分:2)
您strcat
到buffer
的东西,但buffer
尚未初始化。 strcat
将首先搜索第一个空字符,然后复制字符串以在那里串联。您现在可能正在覆盖不属于您的内存。
在外部while
循环之前,请执行以下操作:
*buffer= '\0';
答案 2 :(得分:1)
您的代码中存在两个主要问题:
malloc()
分配的数组未初始化,因此在设置strlen(buffer)
指向数组的空终止符之前,调用buffer
时会出现不确定的行为。该程序可能只是崩溃,但是在您的情况下,内存块中存在任何内容,之后将其保留到第一个空字节。'/'
,则仅应从路径中获取下一个字符。在您的情况下,您跳过空终止符,并且在读取字符串常量末尾时,程序具有未定义的行为。实际上,解析将通过另一个字符串常量"buffer(%s)\n"
和另一个"hello"
继续进行。字符串常量似乎是相邻的,没有在系统上填充,这只是一个巧合。这是更正的版本:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
void parse_path(const char *pathname) {
int char_index = 0;
char current_char = pathname[char_index];
char *buffer = calloc(1, 1);
char *current_char_str = calloc(1, 1);
while (current_char != '\0' && current_char != 11) {
if (char_index == 0 && current_char == '/') {
char_index++; current_char = pathname[char_index];
continue;
}
while (current_char != '/' && current_char != '\0') {
current_char_str[0] = current_char;
current_char_str[1] = '\0';
buffer = (char *)realloc(buffer, strlen(buffer) + 2);
strcat(buffer, current_char_str);
char_index++; current_char = pathname[char_index];
}
if (strlen(buffer)) {
printf("buffer(%s)\n", buffer);
current_char_str[0] = '\0';
buffer[0] = '\0';
}
if (current_char == '/') {
char_index++; current_char = pathname[char_index];
}
}
}
int main(int argc, char *argv[]) {
parse_path("this/is/a/path/hello");
printf("hello\n");
return 0;
}
输出:
buffer(this)
buffer(is)
buffer(a)
buffer(path)
buffer(hello)
hello
但是请注意一些尚存的问题:
current_char != 11
:您是要停在TAB还是换行符?这里是一个更简单的版本,具有相同的行为:
#include <stdio.h>
#include <string.h>
void parse_path(const char *pathname) {
int i, n;
for (i = 0; pathname[i] != '\0'; i += n) {
if (pathname[i] == '/') {
n = 1; /* skip path separators and empty items */
} else {
n = strcspn(pathname + i, "/"); /* get the length of the path item */
printf("buffer(%.*s)\n", n, pathname + i);
}
}
}
int main(int argc, char *argv[]) {
parse_path("this/is/a/path/hello");
printf("hello\n");
return 0;
}