【问题标题】:Function that modifies string and returns the modified string修改字符串并返回修改后的字符串的函数
【发布时间】:2021-01-02 11:41:50
【问题描述】:

我有一个从宏中获取字符串的函数。它修改字符串,然后返回一个新的修改后的字符串。我的意图是使用这个返回的字符串。但是,它不起作用,因为它不返回修改后的字符串。

#include <stdio.h>
#include "string.h"

#define ENCRYPTED_FILE  "hello.txt"

char *decrypt(){
    char str[]=ENCRYPTED_FILE;
    strtok(str,".txt");
    strcat(str,"_decrypted.txt");
    //printf("%s\n",str);
    return str;
};
   
int main()
{        
    printf("%s\n",decrypt());     //output: *** stack smashing detected ***: ./a.out terminated 
    return 0;
}

【问题讨论】:

  • 问:str 的大小是多少?它可以容纳多长的字符串?
  • 大到足以容纳“hello_decrypted.txt”字符串
  • 你为什么这么认为?
  • 我现在增加了大小 char str[40]=ENCRYPTED_FILE;还是错误

标签: c concatenation c-strings function-definition


【解决方案1】:

对于初学者,该函数返回一个指向本地数组str 的第一个元素的指针,该元素具有自动存储持续时间,退出函数后将不再存在。

因此函数返回一个无效的指针。

您需要为数组动态分配内存。

还有这个strtok的电话

strtok(str,".txt");

没有意义。该函数不搜索子字符串".txt"。它搜索由字符串".txt" 指定的字符集的第一个字符。相反,您可以使用函数strstr

还有这个代码strcat

 strcat(str,"_decrypted.txt");

调用未定义的行为,因为目标数组没有足够的空间来存储附加的字符串文字。

该函数可以看起来像下面的演示程序中显示的那样。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define ENCRYPTED_FILE  "hello.txt"

char *decrypt( void )
{
    const char *encrypted_file = ENCRYPTED_FILE;
    
    const char *p = strstr( encrypted_file, ".txt" );
    
    if ( p == NULL ) p = encrypted_file + strlen( encrypted_file );
    size_t n = p - encrypted_file;
    
    const char *decrypted_file = "_decrypted.txt";
    
    char *s = malloc( n + strlen( decrypted_file ) + 1 );
    
    memmove( s, encrypted_file, n );
    s[n] = '\0';

    strcat( s + n, decrypted_file );

    return s;
}

int main(void) 
{
    char *s = decrypt();
    
    puts( s );
    
    free( s );
    
    return 0;
}

程序输出是

hello_decrypted.txt

【讨论】:

    【解决方案2】:

    您正在返回一个指向临时数组的指针,该数组在控制权离开函数后变得悬空。首先,您必须通过malloc 在堆上分配它,然后确保其分配的大小足以包含原始大小加上额外的“_decrypted”后缀。

    【讨论】:

      【解决方案3】:

      我刚刚注意到您的库符号为 #include "string.h" 而不是 #include &lt;string.h&gt;,这可能会解决问题。

      【讨论】:

        【解决方案4】:

        恕我直言,您对strtok() 功能的使用不当。每次找到点.tx 时,它都会将字符串拆分为子字符串。正如您编写的代码,恐怕这不是您想要的(消除.txt 后缀?)

        阅读strtok() 的手册页,它会准确解释这个函数的实际作用。

        另一方面,您不能截断.txt 开头的字符串,然后将更长的字符串附加到它上面。当您声明 str[] 数组(通过明确不使用长度)时,编译器保留了尽可能多的字符来保存来自宏的文本,再加上一个来保存 \0 分隔符。所以你的数组只有空间来容纳 10 个字符("hello.txt" 的 9 个字符,加上一个用于字符串结尾的'\0')。当然,没有地方可以容纳hello_decripted.txt,这需要19 个字符加上\0 的1 个字符。解决此问题的方法可能是在数组声明中指明您希望编译器使用多少个字符,如下所示:

            char str[100] = ENCRYPTED_FILE;
        

        然后您最多可以扩展 100 个字符(99 个字符加上字符串结尾字符 \0 的持有者)。

        如果您找到要搜索的字符串 (.txt) 并将 \0 放在它的第一个位置,您将截断原始字符串,您将能够执行您真正想要的操作,即是:

        #include <stdio.h>
        #include <stdlib.h>
        #include "string.h"  /* is this what you actually mean and not <string.h>? */
        
        #define ENCRYPTED_FILE  "hello.txt"
        
        char *decrypt(){
            char str[100]=ENCRYPTED_FILE;
            char *p = strstr(str,".txt");
            if (p != NULL) {  /* the string actually has a .txt suffix */
                *p = '\0'; /* string truncated */
            }
            strcat(str,"_decrypted.txt"); /* add new suffix */
            //printf("%s\n",str);
            /* you cannot return str as str is a local variable,
             * and it will cease to exist as soon as we leave this
             * function body, better return a new dynamically
             * allocated string (that need to be freed with free(3)
             */
            return strdup(str);
        };
           
        int main()
        {        
            /* the stack smashing probably is due to returning the
             * address of a local variable, that ceased to exist.
             */
            char *name = decrypt();
            printf("%s\n", name);
            free(name); /* return the memory allocated in decrypt() */
            return 0;
        }
        

        这将解决尊重您意图的问题。但你在另一点上错了:

        如果字符串.txt 出现在原始名称的末尾之前怎么办?在我看来,您要搜索的是.txt 后缀(以前称为扩展名) 是什么阻碍了您的文件被命名为blahblah.txt01.txt 之类的名称? --其中出现了两次子字符串.txt--) 这不是搜索.txt 后缀的正确算法。正确的方法是搜索.txt 是否在字符串的末尾,为此,使用的算法不同(而且效率更高):

        char *decrypt(){
            char str[100]=ENCRYPTED_FILE;
            char *suff = ".txt";
            /* go to the point that is strlen(str) further than
             * the beginning of the string minus the string
             * of the suffix */
            char *p = str + strlen(str) - strlen(suff);
            if (strcmp(p, suff) == 0) {  /* the string actually has a .txt suffix */
                *p = '\0'; /* string truncated */
            }
            /* from this point on, everything goes the same */
            strcat(str,"_decrypted.txt"); /* add new suffix */
            //printf("%s\n",str);
            return strdup(str);
        };
        

        在这种情况下,您只需要进行一次字符串比较(在 strstr() 的正文中进行多次以搜索完整匹配),您就会知道它是否失败或没有快速有效地进行。

        注意

        关于代码中#include "string.h" 行的最后一点说明:如果您有一个名为相同的本地文件(在本地目录中),则可以包含一个带双引号的文件而不是一对&lt;&gt; 字符作为一些库文件,因为这将使它在系统库之一之前被找到。但是如果你包含标准库的包含文件是一个坏习惯,因为如果你以后决定创建一个包含文件(在其他模块或程序中)并创建一个本地string.h文件,这个程序会突然开始编译错误你不会猜到为什么。小心#include 名称和调用它们的两种方式。命名为&lt;file.h&gt; 的文件通常是标准库包含文件,在系统的固定位置进行搜索。命名为"file.h" 的文件首先在工作目录中搜索,如果没有找到,则在库固定路径中搜索。尝试仅将" 用于您的文件或构建目录中的文件,并仅使用&lt;&gt; 搜索系统文件。

        【讨论】:

          猜你喜欢
          • 2013-12-27
          • 2017-05-23
          • 1970-01-01
          • 2018-04-01
          • 2011-12-30
          • 2014-05-30
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多