【问题标题】:How to write to char* from a function in C如何从 C 中的函数写入 char*
【发布时间】:2020-01-24 15:45:16
【问题描述】:

我正在努力编写作为参数传递的 char*。我想从函数 write_char() 向 char* 写入一些字符串。使用下面的代码,我遇到了分段错误。

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

void write_char(char* c){
    c = (char*)malloc(11*(sizeof(char)));
    c = "some string";
}

int main(){
    char* test_char;
    write_char(test_char);
    printf("%s", test_char);

    return 0;
}

【问题讨论】:

  • c = malloc(...); c = "some string" 有点类似于x = 5; x = 7。第二个赋值覆盖第一个。在这种情况下,这会导致内存泄漏。此外,您正在分配一个作用域为函数的变量,因此函数返回时该值会丢失。
  • 请注意,"some string" 需要 12 个字符——你没有计算结尾的空值。但是您应该避免将字符串长度硬连接到代码中。超出分配数组的范围写入(如果您将字符串复制到分配的数组,而不是泄漏内存)将是未定义的行为。
  • 您可以使用strdup,这样您就不必担心空值终止您的字符串。

标签: c function reference dynamic-memory-allocation c-strings


【解决方案1】:

您有两个问题(与您尝试做的事情有关,还有其他问题):

  1. C 中的参数是按值传递的,这意味着参数变量(write_char 函数中的c)是来自test_char 中的值的副本 main 函数。修改这个副本(比如给它赋值)只会改变局部变量的值,而不是原来的变量值。

  2. 再次分配给变量会覆盖变量中的当前值。如果你这样做,例如

    int a;
    a = 5;
    a = 10;
    

    您(希望)不会想知道为什么在第二个作业中a 的值被更改为10。变量是指针并不会改变语义。


现在如何解决您的问题... 第一个问题可以通过将函数 return 改为指针来轻松解决。第二个问题可以通过将字符串复制到内存中而不是重新分配指针来解决。

所以我的建议是你编写类似的函数

char *get_string(void)
{
    char *ptr = malloc(strlen("some string") + 1);  // Allocate memory, +1 for terminator
    strcpy(ptr, "some string");  // Copy some data into the allocated memory
    return ptr;  // Return the pointer
}

这可以用作

char *test_string = get_string();
printf("My string is %s\n", test_string);
free(test_string);  // Remember to free the memory we have allocated

【讨论】:

  • 您可以使用char *ptr = malloc(sizeof("some string")); if (ptr != NULL) strcpy(ptr, "some string"); return ptr;,因为sizeof("string literal") 在计数中包含空字节,这与strlen() 不同。
【解决方案2】:

函数内

void write_char(char* c){
    c = (char*)malloc(11*(sizeof(char)));
    c = "some string";
}

参数c是函数的局部变量。在函数内更改它不会影响原始参数,因为它是按值传递的。那就是该函数处理原始参数的副本。

你必须通过指向它的指针通过引用传递参数。

该函数也有内存泄漏,因为首先为指针分配了已分配内存的地址,然后重新分配了字符串文字 "some string" 的第一个字符的地址。

如果您想创建字符串文字的副本,那么您需要的是以下内容

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

void write_char( char **s )
{
    const char *literal = "some string";
    *s = malloc( strlen( literal ) + 1 );

    if ( *s ) strcpy( *s, literal );
}

int main( void )
{
    char *test_char = NULL;

    write_char( &test_char );

    if ( test_char ) puts( test_char );

    free( test_char );
}    

程序输出是

some string

不要忘记动态分配一个足够大的字符数组来存储字符串文字的终止零。

当不再需要分配的数组时,你应该释放分配的内存。

如果你只想用字符串字面量的地址初始化一个指针,那么就不需要动态分配内存。

你可以写

#include <stdio.h>

void write_char( char **s )
{
    *s = "some string";
}

int main( void )
{
    char *test_char = NULL;

    write_char( &test_char );

    puts( test_char );
}    

【讨论】:

    【解决方案3】:

    在 C 中,您需要将指针传递给指针。您的 malloc 调用试图更改正在传入的变量的值,但它实际上只是一个副本。你传入的真实变量不会改变。

    另外,将字符串复制到 char* 的方式不是使用赋值...这是一些修改后的代码:

    #include<stdio.h>
    #include<string.h>
    #include<stdlib.h>
    
    void write_char(char** c){
        size_t len = strlen("some string");
        *c = (char*)malloc(len + 1); // + 1 for null termination
        strncpy(*c, "some string", len);
    }
    
    int main(){
        char* test_char;
        write_char(&test_char);
        printf("%s", test_char);
    
        return 0;
    }
    

    【讨论】:

    • 为什么是strncpy()?您已经小心地避免设置您为其分配空间的空字节(因此您创建的不是可靠的字符串)。使用strcpy(*c, "some string"); 是绝对安全的;如果您必须使用strncpy(),那么strncpy(*c, "some string", len + 1); 将是正确且安全的。你也可以使用memmov(*c, "some string", len + 1);(或memcpy()而不是memmove())。
    • @JonathanLeffler 注意:“strcpy(*c, "some string"); 非常安全”--> 如果*c == NULL,所有各种复制功能都会受到影响。
    • @JonathanLeffler 我认为使用strncpy 只是一种良好的肌肉记忆,不应气馁。显然,它必须与正确的参数一起使用。
    • 你是对的,@chux — 代码没有测试分配是否成功。在分配成功的情况下,strcpy() 是完全安全的。
    • @SergeyA strncpy(*c, "some string", len); 在这里肯定是错误的 *c 不指向 string :没有 null 字符。 JL 关于需要 +1 的评论在这里很重要。
    【解决方案4】:

    C 中的字符串赋值与大多数现代语言非常不同。如果您声明 char * 并在同一语句中分配一个字符串,例如,

    char *c = "some string";
    

    这很好用,因为编译器可以决定为该字符串分配多少内存。不过,在那之后,您通常不应该使用= 更改字符串的值,因为这种用法主要用于常量字符串。如果您想特别清楚,请使用const 声明它。您需要使用strcpy。即使那样,如果您打算更改它,您仍希望避免使用设置字符串声明大多数字符串,就像我上面所说的那样。这是一个例子:

    char *c;
    c = malloc(16 * sizeof(char));
    strcpy(c, "Hello, world\n");
    

    如果您传递一个指向将重新分配它的函数的指针,或者甚至首先是malloc,您将需要一个指向指针的指针,否则main 中的字符串不会被更改。

    void myfunc(char **c) {
        char *tmp = realloc(*c, 32 * sizeof(char));
        if(tmp != NULL) {
            *c = tmp;
        }
    }
    
    char *c = malloc(16 * sizeof(char));
    strcpy(c, "Hello, world\n");
    myfunc(&c);
    

    【讨论】:

    • 我不同意“不过,在那之后,你不能用=改变字符串的值”。当你有char *c时,你可以在随时通过c = "another string"; 更改它指向的字符串——这会更改存储在指针变量中的指针。您不应该修改字符串文字;大多数情况下,尝试这样做会导致崩溃。如果你有char c[] = "some string";,那么你不能通过赋值来改变字符串。
    • 如果tmp == NULL*c 不会被重新分配。调用代码知道realloc()失败的能力。
    • @chux 是的,它不是万无一失的代码或任何东西,只是用来演示。
    • char *c = "some string"; 当然很好,因为您使用 char 文字初始化非常量 char 指针。
    • @SergeyA 可能不是最佳实践,但肯定是相当普遍的用法。
    【解决方案5】:
    char* test_char="string"; // initialize string at the time of declaration
    
    
    void write_char(char* c){
        c = (char*)malloc(11*(sizeof(char)));
    
    }
    
    int main(){
        char* test_char="strin";
        write_char(test_char);
        printf("%s", test_char);
    
        return 0;
    }
    

    【讨论】:

    • 请在尝试回答问题之前拨打tour。抱歉,您的“回答”和您的评论毫无意义。
    • 你的write_char 函数仍然没有多大意义。
    猜你喜欢
    • 2011-06-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-12
    • 2014-02-20
    • 1970-01-01
    • 2013-06-24
    相关资源
    最近更新 更多