为什么strcpy不起作用但直接分配有效?

时间:2017-02-10 02:40:47

标签: c strcpy dynamic-tables

在以下代码中:

 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct
{
    char** tab;
    int n;
}slist;

void print(slist* p);
void add(slist* p, const char* s);
void add(slist* p, const char* s)
{
    if(p->n==0)
    {
        p->tab=(char**)malloc(sizeof(char**));
    }
    strcpy(p->tab[p->n],s);
    p->n=p->n+1;
}
void print(slist* p)
{
    int i;
    printf("[");
    for(i=0;i<p->n;i++)
        printf(" %s",p->tab[i]);
    printf(" ]");
}
int main()
{
    char s1[25] = "Picsou";
    char s2[25] = "Flairsou";
    slist* p = (slist*)malloc(sizeof(slist));
    p->n=0;
    p->tab=NULL;
    add(p,s1);
    add(p,s2);
    print(p);
    return 0;
}

函数add()不起作用,但如果我将其更改为:

void add(slist* p, const char* s)
{
    if(p->n==0)
    {
        p->tab=(char**)malloc(sizeof(char**));
    }
    p->tab[p->n]=s;
    p->n=p->n+1;
}
它看起来效果很好。在第一种情况下,输出仅为" ["; 在第二种情况下,它应该是:" [ Picsou Flairsou ] "。 我不明白为什么。 我也试过这个:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct
{
    char** tab;
    int n;
}slist;

void print(slist* p);
void add(slist* p, const char* s);

void print(slist* p)
{
    int i;
    printf("[");
    for(i=0;i<p->n;i++)
        printf(" %s",p->tab[i]);
    printf(" ]");
}
void add(slist* p, const char* s)
{
    slist* tmp = (slist*)malloc(sizeof(slist));
    tmp->tab=(char**)malloc(sizeof(char*)*(p->n+1));
    int i;
    for(i=0;i<p->n;i++)
         tmp->tab[i]=(char*)malloc(sizeof(char));
    strcpy(tmp->tab[p->n],s);
    tmp->n=p->n+1;
    p = tmp;
}
int main()
{
    char* s1 = "Picsou";
    char* s2 = "Flairsou";
    slist* p = (slist*)malloc(sizeof(slist));
    p->n=0;
    p->tab=NULL;
    add(p,s1);
    add(p,s2);
    print(p);
    return 0;
}

1 个答案:

答案 0 :(得分:2)

这里有很多错误,这对于处理指针的人来说很常见。从哪里开始...我只是按照代码中出现的顺序排列。

  1. 这不是&#34;列表&#34;。

    它是一个阵列......有点像。如果你说&#34; list&#34;对于C程序员,他们会认为你的意思是一个链表。

  2. 数组分配不正确。

    if(p->n==0)
    {
        p->tab=(char**)malloc(sizeof(char**));
    }
    

    这里你已经分配了足够的存储单个指针。第二次拨打add时,您将在最后访问内存。您也错误地投射了结果(在C中,您不会从malloc投射返回值)。此外,您向读者提供了令人困惑的信息,因为您打算分配一个数组,该数组将包含char*类型的元素, NOT char**

    您必须允许阵列在需要时动态扩展(目前不适合您的能力 - 可能会在几天内尝试)或设置最大大小。让我们这样做。

    const int MAX_SIZE = 100;
    if( p->n==0 )
    {
        p->tab = malloc( MAX_SIZE * sizeof(char*) );
    }
    else if( p->n == MAX_SIZE )
    {
        printf( "Maximum size exceeded!\n" );
        return;
    }
    

    如果您愿意,可以使用calloc代替malloc。它将在分配后对块进行零初始化:calloc( MAX_SIZE, sizeof(char*) )

  3. 复制到未初始化的指针。

    strcpy(p->tab[p->n],s);
    

    您为tab分配了内存,但是您没有分配其元素指向的内存,这里您有未定义的行为(最有可能导致分段错误,但可以执行任何操作)。

    确保您有一个有效的指针,它指向的位置为您复制到其中的数据保留了足够的存储空间:

    p->tab[p->n] = malloc( strlen(s) + 1 );
    strcpy( p->tab[p->n], s );
    
  4. 存储可能无效的指针。

    您的选择&#34;效果非常好&#34;用途:

    p->tab[p->n]=s;
    

    但是,这个工作的唯一原因是因为这些指针在你使用&#34; list&#34;的整个时间内仍然有效。 (但实际上程序没有&#34;工作&#34;因为我在第2号中突出显示的原因。

    有时我们希望在程序中使用这种行为,并设计我们的数据结构来索引它们不拥有的指针。但更常见的是,特别是初学者,你最好复制数据(而不是简单地复制指针)。因此,您将使用我在上面第3条中建议的方法。

  5. 没有评论!!

    以下代码有很多问题,我不会将它拆开或解释它们。

    void add(slist* p, const char* s)
    {
        slist* tmp = (slist*)malloc(sizeof(slist));
        tmp->tab=(char**)malloc(sizeof(char*)*(p->n+1));
        int i;
        for(i=0;i<p->n;i++)
             tmp->tab[i]=(char*)malloc(sizeof(char));
        strcpy(tmp->tab[p->n],s);
        tmp->n=p->n+1;
        p = tmp;
    }
    

    但是,您似乎尝试执行与realloc类似的操作。这是我在第2号中提到的选项,我说你可能还没准备好。但无论如何要阅读它:realloc