【问题标题】:Return a string on C在 C 上返回一个字符串
【发布时间】:2014-12-15 22:39:09
【问题描述】:

我收到了一个我不知道如何解决的核心转储。我搜索了其他问题并用谷歌搜索了我的问题,但我就是不知道如何解决这个问题......

代码如下:

const char checkExtension(const char *filename)
{
    const char *point = filename; 
    const char *newName = malloc(sizeof(filename-5));

    if((point = strrchr(filename,'.palz')) != NULL ) 
    {   
        if(strstr(point,".palz") == 0) 
        {
          strncpy(newName, filename, strlen(filename)-5); 
          printf("%s\n",newName );     // the name shows correctly
          return newName;              // Segmentation fault (core dumped)
        }
    }
    return point;
}

该函数被称为char checkExtensions(const char *filename)。由于我在网上找到的解决方案,我添加了 const,但到目前为止我还无法使其工作......
提前感谢您的帮助!

【问题讨论】:

  • 字符串在 C 中由“char *”或“const char *”表示。您的函数,如所写,返回一个“const char”(注意缺少指针)。
  • @JoshPetrie 所以我不能返回一个字符?我必须从参考中返回它?
  • 是的,您必须返回 char *。但请注意,您的函数存在内存管理问题。调用者不知道他们是否需要释放返回值
  • filename-5 是一个指针,所以 sizeof(filename-5) 是一个指针的大小。 sizeof 的使用由 编译器 评估。这绝对不是你想要的。您根据指针大小分配了 4 或 8 个字节。你不是故意的。查看您对strncpy 的电话。
  • 你真正需要弄清楚的是这个函数应该做什么。它的规格是什么?

标签: c string segmentation-fault return


【解决方案1】:

这里有多个错误。您还没有说出您要实现的目标,这必须从代码中暗示出来。您已将 pointnewName 声明为 const,但已重新分配了一个值。你已经测试了strstr() == 0,它应该是strstr() == NULL。您调用了strrchr(filename,'.palz'),但发送了一个字符串而不是一个字符。然后你返回了局部变量point,它在你有机会使用它之前就超出了范围,因为它没有被声明为静态的。因此,无论您返回的是 char 还是 char 指针,都无关紧要。

char *checkExtension(const char *filename) {
    // if filename has extension .palz return a pointer to
    // the filename stripped of extension or return NULL
    char *point;
    static char newName[512];
    strncpy(newName, filename, 512);
    if ((point = strstr(newName, ".palz")) != NULL ) {
        if (strlen (point) == 5) {
            *point = 0; // string terminator
            // printf("%s\n",newName ); // use only for debugging
            return newName;
        }
    }
    return NULL;
}

或者提供一个函数可以修改的字符串 -

char *checkExtension(const char *filename, char *newName) { ... }

或者提供一个函数可以修改的文件名 -

char *checkExtension(char *filename) {
    char *point;
    if ((point = strstr(filename, ".palz")) != NULL ) {
        if (strlen (point) == 5) {
            *point = 0; // string terminator
            return filename;
        }
    }
    return NULL;
}

【讨论】:

    【解决方案2】:

    你的代码有几个问题:

    • 错误的返回类型:

      const char checkExtension(const char *filename){
      

      您需要返回一个指针 (const char *),而不是单个字符。

    • 内存不足:

      const char checkExtension(const char *filename){
      
        const char *newName = malloc(sizeof(filename-5));
      

      您正在分配 指针的大小 (char *),通常为 4 或 8。您需要调用 strlen() 以找出 字符串的大小:

    • 多字节字符:

      if((point = strrchr(filename,'.palz')) != NULL ) {  
      

      '.palz' 是一个多字节字符文字。虽然这在 C 中是允许的,但它的值是实现定义的,可能不会达到您的预期。字符串文字使用 引号 (".palz")。

    • 没有终止零:

      strncpy(newName, filename, strlen(filename)-5); 
      

      请注意,strncpy() 不一定以空值终止目标字符串。它最多写 strlen(filename)-5 个字符。如果源字符串包含更多字符(如您的情况),它将写入终止零。


    我不确定你到底想做什么。也许是这样的:

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    
    const char *checkExtension(const char *filename)
    {
      int len = strlen (filename)-5;
      char *newName = NULL; /* return NULL on allocation failure. */
    
      if (len > 0 && !strcmp (filename+len, ".palz")) {
        newName = malloc (len+1);
        if (newName) {
          memcpy (newName, filename, len);
          newName[len] = 0;
        }
      }
    
      return newName;
    }
    
    int main (int ac, char **av)
    {
      if (ac > 1) {
        const char *p = checkExtension (av[1]);
        puts (p ? p : "NULL");
      } else {
        puts ("?");
      }
    
      return 0;
    }
    

    【讨论】:

      【解决方案3】:

      您的代码存在几个相当大的问题。在我打字的时候补上它,所以它可能不会立即修复所有内容。忍受我。

      1. 您需要返回char *,而不是char

        const char checkExtension(const char *filename)
        {
            const char *point = filename; 
        
      2. malloc内存,但指令流不保证它会被释放或返回。

      3. sizeof(filename) 应该是 strlen(filename),减 5(sans 扩展名)但 +1(以 0 结尾)。

            const char *newName = malloc(sizeof(filename-5));
        
      4. strrchr 搜索单个字符。一些编译器允许“多字节字符常量”,但他们期望像 2——而不是 5。由于您知道字符串的长度和开头,请使用strcmp。 (首先确保至少 5 个字符。如果没有,无论如何在测试中没有用处。)

            if((point = strrchr(filename,'.palz')) != NULL ) {  
        
      5. 呃,strstr 在字符串中搜索字符串,如果没有找到则返回 0(实际上是 NULL)。这与您之前的测试相矛盾。删除它。

            if(strstr(point,".palz") == 0)
            {
        
      6. strncpy 复制 n 字符,但众所周知(并且记录)如果没有被复制,则不会添加终止 0。您必须自己处理。

      7. .. 这实际上是malloc 行应该出现的位置,就在使用返回它之前。

                  strncpy(newName, filename, strlen(filename)-5);
                  printf("%s\n",newName ); // the name shows correctly
                  return newName; // Segmentation fault (core dumped)
              }
            }
        
      8. 您在此处返回原始字符串。那你怎么知道你需要free呢?如果你覆盖了 previous char *,它的内存将会丢失。最好返回原始字符串的副本(这样它总是可以被释放),或者,我更喜欢返回 NULL 以向调用例程指示“不需要进一步的操作”。

            return point;    
        }
        

      希望我没有忘记任何事情。

      【讨论】:

        【解决方案4】:

        这很可能是你的问题:

        const char *newName = malloc(sizeof(filename-5));

        首先,文件名是const char * 类型,这意味着(filename - 5) 也是这种类型。因此,sizeof(filename - 5) 将始终返回架构的指针数据类型的大小(x32 为 4,x64 为 8)。 因此,根据您的架构,您可以调用malloc(4)malloc(8)

        其余代码甚至无法编译,而且存在严重的字符串操作问题,因此很难判断您的目标是什么。我想strncpy() 将太多数据复制到newName 缓冲区中,导致缓冲区溢出。

        如果您的目标是从路径中提取文件名,那么您应该只使用char *basename(char *path)

        【讨论】:

          【解决方案5】:

          您的代码有很多问题。以下是其中一些:

          1. 您的函数返回char,它是一个字符。您需要返回一个指向字符数组的指针,一个 C 字符串。
          2. 您没有分配正确的内存量。您在指针上使用 sizeof() 会产生指针的大小。
          3. 您使调用者无法知道是否释放内存。有时你堆分配,有时不是。你的方法会泄露。
          4. 您将'.palz'(一个字符文字)传递给strrchr,它需要一个char。你要传递的是'.'

          更好的方法是让调用者分配内存。这是一个完整的程序,展示了如何:

          #include <string.h>
          #include <stdio.h>
          
          void GetNewFileName(const char *fileName, char *newFileName)
          {
              const char *dot = strrchr(fileName, '.');
              if (dot)
              {
                  if (strcmp(dot, ".palz") == 0)
                  {
                      size_t len = dot - fileName;
                      memcpy(newFileName, fileName, len);
                      newFileName[len] = 0;
                      return;
                  }
              }
              size_t len = strlen(fileName);
              memcpy(newFileName, fileName, len);
              newFileName[len] = 0;
              return;
          }
          
          int main(void)
          {
              char fileName[256];
              char newFileName[256];
          
              strcpy(fileName, "foo.bar");
              GetNewFileName(fileName, newFileName);
              printf("%s %s\n", fileName, newFileName);
          
              strcpy(fileName, "foo.bar.palz");
              GetNewFileName(fileName, newFileName);
              printf("%s %s\n", fileName, newFileName);
          
              strcpy(fileName, "foo.bar.palz.txt");
              GetNewFileName(fileName, newFileName);
              printf("%s %s\n", fileName, newFileName);
          
              return 0;
          }
          

          输出

          foo.bar foo.bar foo.bar.palz foo.bar foo.bar.palz.txt foo.bar.palz.txt

          请注意,strcmp 对字母大小写比较敏感。在 Windows 上,文件名不区分大小写。我会把这个问题留给你处理。

          通过让调用者分配内存,您可以让他们选择分配内存的位置。如果他们愿意,他们可以使用本地堆栈分配的缓冲区。而且调用者很容易分配内存,因为新文件名永远不会比原始文件名长。

          【讨论】:

          • 谢谢你的回答,你能解释一下“size_t len = dot - fileName;”是什么吗?为什么它是点文件名而不是文件名 - 点?感谢您的宝贵时间!
          • 因为dot大于filename,所以是字符串最后一个句点的地址。
          • 更大是因为filename指向文件名第一个字母的地址,点指向“.”的地址,所以点更大对吧?跨度>
          猜你喜欢
          • 2016-09-21
          • 1970-01-01
          • 2018-05-09
          • 2014-12-18
          • 2018-04-20
          • 2012-07-04
          • 1970-01-01
          • 1970-01-01
          • 2014-12-11
          相关资源
          最近更新 更多