c语言 - 为什么 strcpy 不起作用,但直接分配工作?



在以下代码中:

#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;
}

它似乎运行得很好。在第一种情况下,输出仅为" [";在第二种情况下,它应该是:CCD_ 3。我不明白为什么。我也试过这个:

#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. 这不是一个"列表">

    这是一个数组。。。有点。如果你对C程序员说"列表",他们会认为你指的是链表。

  2. 数组分配不正确

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

    这里已经分配了足够的内存来存储一个指针。第二次调用add时,您将访问末尾的内存。您还错误地强制转换了结果(在C中,您没有强制转换malloc的返回值)。此外,您还向读取器提供了令人困惑的信息,因为您打算分配一个数组,该数组将包含类型为char*NOTchar**的元素。

    您必须允许阵列在需要时动态扩展(现在不适合您的能力,可能几天后再尝试),或者设置最大大小。让我们这样做吧。

    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. 存储可能无效的指针

    您的"效果非常好"的替代方案使用:

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

    然而,这之所以有效,唯一的原因是这些指针在您使用"列表"的整个时间内都保持有效(但实际上,由于我在数字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

最新更新