指向C的指针

pha*_*aoh 0 c unix pointers

我试图理解指针的指针是如何工作的,我得出了这个例子,它编译得很好.但是,当它执行时,我得到一个分段错误.PS:我不想f1()回来char *.

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

int f1(char **str_);

int main(int argc, char **argv)
{
    char *str = NULL;

    f1(&str);     
    printf("str : %s\n", *str);

    str = realloc(str, (size_t) 0);   
    assert(str == NULL);

    return 0;
}

int f1(char **str_)
{
    if ((*str_ = realloc(*str_, sizeof(char) * 12)) == NULL)
    {
        fprintf(stderr,"realloc() failed\n");
        exit(3);
    }

    (*str_) = "hello there";

    return 0;
}
Run Code Online (Sandbox Code Playgroud)
  • 代码有什么问题?

Jon*_*ler 9

您需要在编译器中打开更多警告或获得更好的编译器.当我编译你的代码时,我收到警告:

mem.c: In function ‘main’:
mem.c:12: warning: format ‘%s’ expects type ‘char *’, but argument 2 has type ‘int’
mem.c:12: warning: format ‘%s’ expects type ‘char *’, but argument 2 has type ‘int’
mem.c: At top level:
mem.c:7: warning: unused parameter ‘argc’
mem.c:7: warning: unused parameter ‘argv’
Run Code Online (Sandbox Code Playgroud)

我不太清楚为什么第12行的警告会重复,但是MacOS X 10.7上的GCC 4.2和4.6.1都给了它两次.当您不使用命令行参数时,警告argc和使用argv是一个很好的理由int main(void); 他们不是一个大问题.

有关警告(实际上,错误)%sintVS char *充分考虑到您的崩溃-这是没有,不过,随着代码的唯一问题.事实上,还有:

  1. 初期内存泄漏,
  2. 实际泄漏,
  3. 你(偶然)依赖未定义的行为的地方,和
  4. 您依赖于实现定义的行为的地方,可能与您的期望不符.

您的代码注释:

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

int f1(char **str_);

int main(int argc, char **argv)
{
    char *str = NULL;

    f1(&str);     
    printf("str : %s\n", *str);

    str = realloc(str, (size_t) 0);   /* (3) Undefined behaviour */
    assert(str == NULL);              /* (4) Implementation-defined behaviour */

    return 0;
}

int f1(char **str_)
{
    if ((*str_ = realloc(*str_, sizeof(char) * 12)) == NULL) /* (1) Incipient leak */
    {
        fprintf(stderr,"realloc() failed\n");
        exit(3);
    }

    (*str_) = "hello there";  /* (2) Actual leak */

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

按编号顺序讨论这些:

  1. 如果内存重新分配失败,则会出现初期泄漏. *str是唯一存储指向已分配内存的指针值的地方,如果realloc()失败,则返回0(空指针),但不释放旧内存,但不再有指向旧内存的指针.

    修复:

    char *new_mem = realloc(*str, 12);
    if (new_mem == 0)
        ...error handling...
    *str = new_mem;
    
    Run Code Online (Sandbox Code Playgroud)

    经验法则:不要将返回值realloc()赋给作为第一个参数的变量.

  2. 实际的泄漏是因为您将指针指向一个字符串常量,指针指向新分配的内存.最简单的解决方法是使用strcpy(),但需要#include <string.h>在执行此操作时添加.您通常也会确保为要复制的字符串分配足够的空间,从而导致:

    char const hello[] = "hello there";
    char *new_mem = realloc(str, sizeof(hello));
    //char *new_mem = realloc(str, strlen(hello)+1);
    if (new_mem == 0)
        ...handle error...
    *str = new_mem;
    strcpy(new_mem, hello);
    
    Run Code Online (Sandbox Code Playgroud)

    在这个例子中,我可以使用,sizeof(hello)因为字符串的大小包括null终止符,并且因为实际的数组定义在范围内.如果要复制的字符串作为指针传递给函数,那么替代使用strlen(hello)+1是正确的(并且使用sizeof()不正确),即使它需要运行时计算长度而不是如图所示的编译时计算.

  3. 由于(2)处的内存泄漏,出现了未定义的行为.你尝试realloc()一个字符串常量,而不是返回的指针realloc().这会导致不确定的行为; 它可能崩溃(因为realloc()尝试将控制信息写入只读内存)或者它可能只是搞乱了内存系统,导致一段时间后崩溃.

    对此的修正是对项目(2)的更正.

  4. 出现了实现定义的行为,因为C标准说:

    §7.20.3内存管理函数1:[...]如果请求的空间大小为零,则行为是实现定义的:返回空指针,或者行为就像大小是非零值一样,但返回的指针不得用于访问对象.

    您断言您的实现将选择第一个选项,但它可能会选择第二个选项.修复是删除无根据的断言.

因此,代码中总共有5个问题,其中只有一个编译器可能会帮助您.


Ray*_*oal 6

第一行将main()变量设置str为NULL并将指针传递给它f1.

f1跑得很好.结果f1str内部的变量main现在是一个指向内存空间的指针,该空间包含字符串(文字)"hello there".

你的下一行printf,segfaults.为什么?因为您正在尝试打印*str(请注意这里的星号!!)作为字符串(格式说明符%s).什么*str时候被称为字符串?任何"地址"都可能被"地狱"表示(至少在32位机器上).令人怀疑的是,地址在您的流程空间中.

经典的段错误.

尝试传球str,而不是*strprintf.

这将有效,请参阅http://codepad.org/Mh00txen

代码还有其他问题,例如f1中12个字符的realloc除了导致内存泄漏之外什么都不做,因为你立即重新指定该指针指向字符串文字,但这不是你的段错误的原因.