如何将字符串从函数传递给main?

时间:2014-08-15 14:21:16

标签: c function

我尝试寻找解决方案,但未能找到解决方案。是否可以返回一个字符串?我想将一个字符串从下面的函数传递回main。我想传递listofdeatils字符串。

这是我的代码:

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

void readFile();

int main()
{
    readFile(); 
    getchar();
    printf("%s \n", listofdetails[1]);
    getchar();

    return 0;
}

void readFile()
{
    int i;
    FILE *fp;
    fp = fopen("specification.txt", "r");

    char ** listofdetails; 
    listofdetails = malloc(sizeof(char*)*6);

    for(i=0;i<6;i++)
    {
        listofdetails[i] = malloc(sizeof(char)*40);
        fgets(listofdetails[i], 40, fp);
        printf("%s \n", listofdetails[i]);
        free(listofdetails[i]);
    }

    free(listofdetails);
    fclose(fp);
}

6 个答案:

答案 0 :(得分:1)

试试这个:

void readFile(char listofdetails[6][40]);

int main()
{
    char listofdetails[6][40]; 
    readFile(listofdetails); 
    printf("%s \n", listofdetails[1]);
    return 0;
}

void readFile(char listofdetails[6][40])
{
    int i;
    FILE *fp;
    fp = fopen("specification.txt", "r");

    for(i=0;i<6;i++)
    {
        fgets(listofdetails[i], 40, fp);
        printf("%s \n", listofdetails[i]);
    }

    fclose(fp);
}

答案 1 :(得分:1)

所以只需返回字符串。显然,在函数中不要free()

而不是void,您的函数可以返回char**,或者您可以更改为分配所有字符的单个数组并将其索引为矩阵。

答案 2 :(得分:1)

有几个选择:

第一个选项 - 您可以直接返回listOfDetails

#define NUM_DETAILS    6
#define DETAIL_LENGTH 40
...
char **readFile( void )
{
  char **listOfDetails = 
    malloc( sizeof *listOfDetails * NUM_DETAILS ); // note operand of sizeof

  if ( listOfDetails )
  {
    ...
    listOfDetails[i] = 
      malloc( sizeof *listOfDetails[i] * DETAIL_LENGTH ); // note operand of sizeof
    ...
  }

  return listOfDetails;
}

int main( void )
{
  ...
  char **details = readFile();
  if ( details )
  {
     ...

    for ( int i = 0; i < NUM_DETAILS; i++ )
    {
      free( details[i] );
    }
    free( details );
  }
}

这可能是你目前写的最直接的路径。请注意,在malloc来电中,我使用sizeof *listOfDetailssizeof *listOfDetails[i]。表达式*listOfDetails的类型为char *,因此sizeof *listOfDetails等同于sizeof (char *)。同样,表达式*listOfDetails[i]的类型为char,因此sizeof *listOfDetails[i]等同于sizeof (char)

如果您决定更改listOfDetails的类型,这有助于最大限度地减少维护难题。阅读IMO也更容易一些。

请注意,main现在负责释放由readFile分配的内存;理想情况下,您希望避免像这样分担内存管理责任,但有时它无法帮助您。

第二个选项 - 您可以将listOfDetails作为参数传递给您的函数:

void readFile( char ***listOfDetails ) // note extra level of indirection
{
   ...
   *listOfDetails = malloc( sizeof **listOfDetails * NUM_DETAILS );
   if ( *listOfDetails )
   {
     ...
     (*listOfDetails)[i] = 
       malloc( sizeof *(*listOfDetails)[i] * DETAIL_LENGTH ); // parens are required!
     ...
   }
}

int main( void )
{
  char **listOfDetails
  ...
  readFile( &listOfDetails ); // note & operator; I want readFile to modify the value
                              // stored in listOfDetails, so I must pass a 
                              // pointer to it; since the type of listOfDetails
                              // is char **, the resulting pointer will have type
                              // char ***, which is seen in the declaration of
                              // readFile above.
  ...
  // free as above
}

此选项与第一个选项之间没有太大区别,只是它增加了额外的间接级别(有些人会觉得这是令人反感的)。您仍然有共同的内存管理责任。请注意,表达式(*listOfDetails)[i]中需要括号;我们不想直接下标listOfDetails,我们想要下标指向的内容。由于[]的优先级高于一元*,因此我们必须明确地将运算符和操作数分组。

第三个选项 - 创建单独的函数来分配和释放内存,同时从main调用:

char **createDetailList( size_t numDetails, size_t detailLength )
{
  char **details = malloc( sizeof *details * numDetails );
  if ( details )
  {
    for ( size_t i = 0; i < NUM_DETAILS; i++ )
    {
      details[i] = malloc( sizeof *details[i] * detailLength );
    }
  }
  return details;
}

void freeDetailList( char **detailList, size_t numDetails )
{
  for ( size_t i = 0; i < numDetails; i++ )
    free( detailList[i] );
  free( detailList );
}

void readFile( char **listOfDetails )
{
  ...
  fread( listOfDetails[i], DETAIL_LENGTH, fp );
  ...
}

int main( void )
{
  char **listOfDetails = createDetailList( NUM_DETAILS, DETAIL_LENGTH );
  ...
  readFile( listOfDetails ); // note no & operator in this case
  ...
  freeDetailList( listOfDetails, NUM_DETAILS );
}

这样,main负责分配和解除分配数组。将内存管理代码分解为单独的函数有两个目的:一,它减少了main中的混乱;二,它允许我们重用这段代码。假设您想要出于某种原因从多个文件中读取多个详细信息列表:

int main ( void )
{
   char **list1 = createDetailList( NUM_DETAILS, DETAIL_LENGTH );
   char **list2 = createDetailList( NUM_DETAILS, DETAIL_LENGTH );
   ...
   readFile( list1, "file1.txt" ); // Note that we're passing the name of the file
   readFile( list2, "file2.txt" ); // as an argument; the definition of readFile
                                   // will need to change accordingly.
   ...
   freeDetailList( list1, NUM_DETAILS );
   freeDetailList( list2, NUM_DETAILS );
}

你也可以创建不同大小的列表(10条记录,每条80个字符,比方说),尽管你需要以某种方式将这些信息传递给readFile函数(通常作为函数参数)。

这是有效的,因为您提前知道您的阵列需要多大。如果提前知道您需要读取多少详细记录或每个明细行的最大长度,那么这种方法将不起作用,并且您将重新分配内存为你进入readFile功能。

请注意,在这种情况下,我不希望readFile修改listOfDetails,因此我没有传递指向它的指针。

第四个选项 - 如果您的阵列大小固定且不是非常大(这里似乎就是这种情况),请不要理会动态内存管理。在main中将其声明为常规数组,并将其作为参数传递给函数:

void readFile( char (*listOfDetails)[DETAIL_LENGTH], size_t numDetails ) // or char listOfDetails[][DETAIL_LENGTH]
{
  ...
  for(i=0;i<numDetails;i++)
  {
    fgets(listofdetails[i], DETAIL_LENGTH, fp);
    printf("%s \n", listofdetails[i]);
  }
  ,,,
}

int main( void )
{
  char details[NUM_DETAILS][DETAIL_LENGTH];
  ...
  readFile( details, NUM_DETAILS ); // note no & operator; when an array expression
                                    // appears in most contexts, it will be converted
                                    // ("decay") to a pointer to the first element.  
                                    // In this case, details will be converted to a
                                    // pointer to an array of char, as you can see in
                                    // the declaration of the readFile function.
                                    // Note that I'm also passing the number of rows 
                                    // explicitly.
  ...
}

优点:没有内存管理hijinks,没有多重解除引用等。主要缺点是它只能用于列数为DETAIL_LENGTH的数组;例如,你不能用它来读取6x50阵列。如果您只使用一个阵列,这不是问题。

指针不知道它们是指向数组中的单个对象还是一系列对象,因此通常需要将行数指定为单独的参数。

您可以使用NUM_DETAILS常量,但理想函数应该通过参数列表和返回值进行通信;他们不应该依靠全局变量或魔术常数间接地共享状态。这将函数与较大的程序“分离”,使其更容易重用。例如,我将您的readFile函数定义为

void readFile( char **listOfDetails, 
               size_t numDetails, 
               size_t detailLength, 
               const char *filename )
{
  FILE *fp = fopen( filename, "r" );
  if ( fp )
  {
    for( size_t i = 0; i < numDetails; i++ )
    {
      fread( listOfDetails[i], detailLength, fp ); // no error checking for brevity
    }
  }
  fclose( fp );
}

此功能不依赖于任何共享信息;它可以很容易地被任何其他程序重复使用,或者用于在同一程序中读取不同大小的多个列表。

请注意,在所有四种情况下,我都用符号常量替换文字640。这有两个目的:首先,符号名称比原始数字更有意义;第二,如果您决定要阅读7个详细记录,或者记录现在需要80个字符长,那么您需要做的就是更改常量的定义 - 您不必深入挖掘代码并替换640的每个实例。

答案 3 :(得分:0)

您可以将功能修改为

char ** readFile()

然后在函数定义的末尾添加

return listofdetails;

如下所述,这项工作规定listofdetails的大小是固定的,因此您可以编写一个释放已分配内存的对应函数。

答案 4 :(得分:0)

在C语言中,您应该将字符串视为字符的指针,继续终止&#39; \ 0&#39;字符。

然后你基本上有两种方法可以返回一个&#39;字符串&#39;主要的。

  1. 让readFile返回一个char *(或char **,如果它应该是一个字符串列表)
  2. 将存储容器传递给readFile,例如readFile(char ** strVal)并将其设置在函数内。

答案 5 :(得分:-3)

将字符串传递给函数并使用strcpy()将字符串复制到该字符串 另一种方法是返回字符串并使返回类型为char *