如何从C中的字符串中获取子字符串?

时间:2015-04-03 20:21:54

标签: c string pointers

我想在C中创建一个从字符串中获取子字符串的函数。这就是我到目前为止所做的:

char* substr(char* src, int start, int len){
    char* sub = malloc(sizeof(char)*(len+1));
    memcpy(sub, &src[start], len);
    sub[len] = '\0';
    return sub;
}

int main(){
    char* test = malloc(sizeof(char)*5); // the reason I don't use char* = "test"; is because I wouldn't be able to use free() on it then
    strcpy(test, "test");
    char* sub = substr(test, 1, 2); // save the substr in a new char*
    free(test); // just wanted the substr from test
    printf("%s\n", sub); // prints "es"

    // ... free when done with sub
    free(sub);
}

有什么办法可以将子字符串保存到test而无需创建新的char*?如果我test = substr(test, 1, 2)test的旧值不再有指向它的指针,所以它是泄露的内存(我想。当涉及到C语言时,我就是一个菜鸟。)

5 个答案:

答案 0 :(得分:1)

void substr(char* str, char* sub , int start, int len){
    memcpy(sub, &str[start], len);
    sub[len] = '\0';
}

int main(void)
{
    char *test = (char*)malloc(sizeof(char)*5);
    char *sub = (char*)malloc(sizeof(char)*3);
    strcpy(test, "test");
    substr(test, sub, 1, 2);

    printf("%s\n", sub); // prints "es"
    free(test);
    free(sub);

    return 0;
}

答案 1 :(得分:0)

嗯,你总是可以保持malloc内存的地址是一个单独的指针:

char* test = malloc(~~~)
char* toFree = test;
test = substr(test,1,2);
free(toFree);

但是,这种数据混乱的大多数特性和功能已经在string.h中完成了。其中一个功能可能会完成你想要完成的工作。正如其他人所指出的那样,movemem()可以将子字符串移动到char指针的开头,中提琴!

如果您特别想要创建一个新的动态字符串,同时保持原始的独立和安全,并且还希望能够重叠这些指针......这很棘手。如果您传入源和目标,然后对受影响的内存进行范围检查,并且如果存在重叠则释放源,那么您可能会这样做...但这看起来有点过于复杂。

我也不喜欢malloc记忆,我相信更高级别的自由,但那可能只是我。

顺便说一下,

char* test = "test";

是C中的那些利基案例之一。初始化指向字符串文字(引号中的东西)的指针时,它会将数据放入内存的特殊部分,仅用于文本数据。你可以(很少)编辑它,但你不应该,它不会增长。

答案 2 :(得分:0)

让我们分解所谈论的内容:

  1. 您分配了一些内存,并创建变量test以指向它。
  2. 您分配了更多内存,并且您希望将该指针存储在名为test的变量中。
  3. 您声称要将两条信息存储在同一指针中。你不能这样做!

    解决方案1 ​​

    使用两个变量。我不知道为什么这是不可接受的......

    char *input = "hello";
    char *output = substr(input, 2, 3);
    

    解决方案2

    让您的输入参数不是堆内存。我们可以通过多种方式实现这一目标:

    // Use a string literal
    char *test = substr("test", 2, 2);
    
    // Use a stack allocated string
    char s[] = "test";
    char *test = substr(s, 2, 2);
    

    个人...

    如果您已经将子字符串的长度传递给函数,我个人宁愿看到该函数只是传递了将数据推入的内存块。类似的东西:

    char *substr(char *dst, char *src, size_t offset, size_t length) {
        memcpy(dst, src + offset, length);
        dst[length] = '\0';
        return dst;
    }
    
    int main() {
        char s[5] = "test";
        char d[3] = "";
    
        substr(d, s, 2, 2);
    }
    

答案 3 :(得分:0)

有很多方法可以做到这一点,你接近它的方式是一个很好的方法,但有几个方面你似乎有点困惑。首先,不需要分配test。只需使用指针即可。您可以在示例中执行char *test = "test";。也不需要释放它。

接下来,当您开始动态分配内存时,您需要始终检查返回以确保分配成功。否则,如果在没有分配内存时尝试写入内存位置,则可以轻松进行段错误。

substr中,您还应验证发送给该函数的startlen的范围,以确保您没有尝试读取该字符串的结尾。< / p>

仅处理正数时,最好使用类型size_tunsigned。代码中永远不会出现否定的startlen,因此size_t可以很好地满足目的。

最后,最好始终检查指向要释放的内存块的指针实际上是否保留有效地址以防止释放内存块两次等等(例如if (sub) free (sub);

如果您有任何疑问,请查看以下内容并告知我们。我更改了代码以接受来自stringstartlen的命令行参数,因此使用的是:

./progname the_string_to_get_sub_from start len

我希望以下帮助。

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

char* substr (char* src, size_t start, size_t len)
{
    /* validate indexes */
    if (start + len > strlen (src)) {
        fprintf (stderr, "%s() error: invalid substring index (start+len > length).\n", __func__);
        return NULL;
    }

    char* sub = calloc (1, len + 1);

    /* validate allocation */
    if (!sub) {
        fprintf (stderr, "%s() error: memory allocation failed.\n", __func__);
        return NULL;
    }

    memcpy (sub, src + start, len);
    // sub[len] = '\0';             /* by using calloc, sub is filled with 0 (null) */

    return sub;
}

int main (int argc, char **argv) {

    if (argc < 4 ) {
        fprintf (stderr, "error: insufficient input, usage: %s string ss_start ss_length\n", argv[0]);
        return 1;
    }

    char* test = argv[1];           /* no need to allocate test, a pointer is fine  */

    size_t ss_start  = (size_t)atoi (argv[2]);      /* convert start & length from  */
    size_t ss_lenght = (size_t)atoi (argv[3]);      /* the command line arguments   */

    char* sub = substr (test, ss_start, ss_lenght);

    if (sub)                                        /* validate sub before use  */
        printf("\n sub: %s\n\n", sub);

    if (sub)                                        /* validate sub before free */
        free(sub);

    return 0;
}

<强>输出

$ ./bin/str_substr test 1 2

 sub: es

如果选择无效的start / len组合:

$ ./bin/str_substr test 1 4
substr() error: invalid substring index (start+len > length).

验证所有内存释放

$ valgrind ./bin/str_substr test 1 2
==13515== Memcheck, a memory error detector
==13515== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==13515== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==13515== Command: ./bin/str_substr test 1 2
==13515==

 sub: es

==13515==
==13515== HEAP SUMMARY:
==13515==     in use at exit: 0 bytes in 0 blocks
==13515==   total heap usage: 1 allocs, 1 frees, 4 bytes allocated
==13515==
==13515== All heap blocks were freed -- no leaks are possible
==13515==
==13515== For counts of detected and suppressed errors, rerun with: -v
==13515== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)

答案 4 :(得分:0)

在C语言中,字符串函数很快就会进入内存管理。所以以某种方式子字符串的空间需要存在并传递给函数,否则函数可以分配它。

const char source[] = "Test";
size_t start, length;

char sub1[sizeof source];
substring1(source, sub1, start, length);
// or
char *sub2 = substring2(source, start, length);
...
free(sub2);

代码需要指定当1)start索引大于其他原始字符串的长度时发生的情况,以及2)length类似地超过原始字符串。这些是未完成OP代码的两个重要步骤。

void substring1(const char *source, char *dest, size_t start, size_t length) {
  size_t source_len = strlen(source);
  if (start > source_len) start = source_len;
  if (start + length > source_len) length = source_len - start;
  memmove(dest, &source[start], length);
  dest[length] = 0;
}

char *substring2(const char *source, size_t start, size_t length) {
  size_t source_len = strlen(source);
  if (start > source_len) start = source_len;
  if (start + length > source_len) length = source_len - start;
  char *dest = malloc(length + 1);
  if (dest == NULL) {
    return NULL;
  }
  memcpy(dest, &source[start], length);
  dest[length] = 0;
  return dest;
}

通过在memmove()中使用memcpy()substring1(),代码可以使用相同的目标缓冲区作为源缓冲区。 memmove()定义明确,即使缓冲区重叠。

substring1(source, source, start, length);