C语言中,我的函数为什么返回NULL?

3
我认为我的函数返回了 NULL,因为我将其初始化为 NULL。但是如果不这样做,编译时会出现错误。
这只是我在 test.c 文件中制作的原型,用于测试。所以当我让它正常工作后,我会将 lookup 函数复制回正确的文件中。
如果有帮助的话,这是 cs50 的 pset6 的一部分。
const char* lookup(const char* extension);

int main(void)
{
    const char* type = "css";
    const char* ending = lookup(type);  
    printf("the exstension: %s\nis of type = %s\n", type, ending);
}

const char* lookup(const char* extension)
{

    char temp[strlen(extension)];

    for (int i = 0; i < strlen(temp); i++)
    {
        if (isalpha(extension[i]))
            temp[i] = tolower(extension[i]);
    }

    printf("temp = %s\n", temp);

    char* filetype = NULL;

    if (strcmp(temp,  "html") == 0)
        strcpy(filetype, "text/html"); 

    else if(strcmp(temp, "css") == 0)
        strcpy(filetype, "text/css");

    else if(strcmp(temp, "js") == 0)
        strcpy(filetype, "text/js");

    else if(strcmp(temp, "jpg") == 0)
        strcpy(filetype, "image/jpg");

    else if(strcmp(temp, "ico" ) == 0)
        strcpy(filetype, "image/x-icon");

    else if(strcmp(temp, "gif") == 0)
        strcpy(filetype, "image/gif");

    else if(strcmp(temp, "png") == 0)
        strcpy(filetype, "image/png");

    else
        return NULL;

    return filetype;
}

我正在使用所有正确的库,但当我尝试包含它们时,我的代码预览出现了问题!

@BLUEPIXY 我试图减少 return 的数量,因为我认为有很多 return 是不好的做法(如果我错了,请纠正我)。而且我不能将字符串赋值给一个变量,因为那样是错误的。我需要复制它。 - mrfr
7个回答

4
 char temp[strlen(extension)];

您没有为尾随的空字符保留空间,也没有设置它!例如:char temp[strlen(extension) + 1] = {0};

然后:

char* filetype = NULL;

if (strcmp(temp,  "html") == 0)
    strcpy(filetype, "text/html"); 

filetype所指向的对象必须经过分配,例如使用malloc,否则strcpy将复制一个空指针。


谢谢!但是你说的“否则strcpy将使用空指针进行复制”是什么意思?malloc如何确定是否使用空指针进行复制? - mrfr
还有,为什么我要将 temp[strlen(extension) + 1] 设置为 {0}; - mrfr
为确保在您的for循环中设置了尾随空字符。 - ouah

3

您确定extension只包含扩展名而没有.吗?我建议使用_stricmpstrcmpi进行不区分大小写的比较。为什么要使用strcpyfiletype复制到指针中,而不是直接赋值?您只有指针,没有使用malloc进行内存分配:

const char* lookup(const char* extension)
{
const char* filetype = NULL;

if (_stricmp(extension, "html") == 0)
    filetype = "text/html"; 
else if(_stricmp(extension, "css") == 0)
    filetype = "text/css";

else if(_stricmp(extension, "js") == 0)
    filetype = "text/js";

else if(_stricmp(extension, "jpg") == 0)
    filetype = "image/jpg";

else if(_stricmp(extension, "ico" ) == 0)
    filetype = "image/x-icon";

else if(_stricmp(extension, "gif") == 0)
    filetype = "image/gif";

else if(_stricmp(extension, "png") == 0)
    filetype = "image/png";

return filetype;
}

或者更好的做法:

const char* lookup(const char* extension)
{
  char * ext[] = { "html", "text/html", "css", "text/css", "js", "text/js", "jpg", "image/jpg", NULL };


  for ( int i = 0; ext[i]; i += 2 )
  {
    if ( !stricmp( extension, ext[i] ) )
      return ext[i+1];
  }
  return NULL;
}

_stricmp() 是什么,它从哪里来?它不属于标准的C语言也不属于POSIX标准。 - alk
strcmpistricmp_stricmp - 关于编译器。 - i486
应该是 const char* filetype;将字符串字面量转换为 char* 已经被弃用,而且也不是一个好主意。 - user1084944

1
请注意这个代码:
char temp[strlen(extension)];

在C语言中,字符串以NULL结尾,因此您实际上不需要为终止字符保留空间,因此在运行时,您的临时字符串可能看起来更长。
请改用以下方法:
char temp[strlen(extension)+1];

之后:
temp[i] = '\0';

你能进一步解释一下 temp[i] = '\0'; 这部分的含义吗? - mrfr
'\0' 是C语言的约定,用于表示字符串的结尾。每个str*函数都将其作为分隔符来使用。 - SirDarius

1

你必须为终止空字符分配空间并终止字符串:

char temp[strlen(extension)+1];

for (int i = 0; i < strlen(temp); i++)
{
    if (isalpha(extension[i]))
        temp[i] = tolower(extension[i]);
}
temp[i]= '\0';

请注意,如果扩展名包含数字或其他非字母字符,则不会被复制。

是的,我99%确定我没有非字母字符。因为在分发代码的后面,他们会检查是否以“php”结尾。 - mrfr

1
你的临时变量缺少结尾的 '\0' 空间,你需要写出类似这样的代码。
char temp[strlen(extension) + 1];

需要分配一些空间来存储文件类型;可能通过编写来实现

char filetype[50]; // 50 should be enought for your case 

然而,我建议使用strcasecmp()(函数比较两个字符串,忽略字符的大小写),而不是strcmp(),并删除似乎无用的文件类型。

#include <stdio.h>
#include <strings.h>

const char *lookup(const char *extension);

int main(void)
{
    const char *const type = "css";
    const char *ending = lookup(type);
    printf("the exstension: %s\nis of type = %s\n", type, ending);
}

const char *lookup(const char *extension)
{
    if (strcasecmp(extension, "html") == 0)
        return "text/html";

    else if (strcasecmp(extension, "css") == 0)
        return "text/css";

    else if (strcasecmp(extension, "js") == 0)
        return "text/js";

    else if (strcasecmp(extension, "jpg") == 0)
        return "image/jpg";

    else if (strcasecmp(extension, "ico" ) == 0)
        return "image/x-icon";

    else if (strcasecmp(extension, "gif") == 0)
        return "image/gif";

    else if (strcasecmp(extension, "png") == 0)
        return "image/png";

    return NULL;
}

一种更具可扩展性的解决方案是使用数组来描述扩展,这样在添加新类型时就不需要更改代码:
#include <stdio.h>
#include <strings.h>

struct Type {
    const char *const extension;
    const char *const mime;
} knownTypes[] = {
    { "html", "text/html"    },
    { "css",  "text/css"     },
    { "js",   "text/js"      },
    { "jpg",  "image/jpg"    },
    { "ico",  "image/x-icon" },
    { "gif",  "image/gif"    },
    { "png",  "image/png"    }
};

static const size_t nbKnownTypes = sizeof(knownTypes) / sizeof(struct Type);

const char* lookup(const char* extension);

int main(void)
{
    const char *const type = "Css";
    const char *ending = lookup(type);
    printf("the exstension: %s\nis of type = %s\n", type, ending);
}

const char *lookup(const char *extension)
{
    for (size_t i = 0; i < nbKnownTypes; i++) {
         struct Type type = knownTypes[i];
         if (strcasecmp(extension, type.extension) == 0)
             return type.mime;
    }

    return "Unknown mime type";
}

使用这种设置,你可以轻松地添加新类型的扩展名和MIME类型(你可以将此结构放在单独的c文件中,这样可以防止重新编译所有内容,但这是另一个故事)。

0

1. 你的代码表现出未定义行为。在你的函数lookup中-

char temp[strlen(extension)];     // basically char temp[3]

使用循环填充完整数组,不留下空间给'\0',然后使用%s打印并将其传递给strcmp也会导致UB

像这样声明数组temp-

char temp[strlen(extension)+1)]={'\0'};        // +1 for null character

2. 当你复制指针filetype时-

if (strcmp(temp,  "html") == 0)
    strcpy(filetype, "text/html"); 

但是它指向NULL,因为它没有分配任何内存。

使用malloc为filetype分配内存。


-1

char* filetype = NULL 没有内存空间来使用 strcpy 函数复制字符串。请将此代码替换为 char* filetype = malloc(20)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接