简体   繁体   中英

Memory allocation for dynamic array

I am stuck in memory allocation while solving the exercise.

Exercise requirement: Create a function that splits a string of characters depending on a separator. The second argument is a unique character separator. The function should return an array that contains a string wrapped between separator.

Example:

Input: "abc def gh-!" && "-"
Output: ["abc def gh", "!"]

Code:

#include<stdlib.h>
  typedef struct s_string_array {
    int size;
    char** array;
  } string_array;


string_array* my_split(char* a, char* b) {

  //Memory allocation part:

  string_array*ptr=(string_array*)malloc(sizeof(string_array));
  int count=0;
  for(int i=0;a[i]!='\0';i++)
  {
   if(a[i]==b[0])
   {
    count++;
   }
  }
  ptr->size=count+1;
  ptr->array=malloc(sizeof(char*)*ptr->size);
  int size2=0;
  int q=0;
  for(int i=0;i<ptr->size;i++)
  {
      for(;a[q]!=b[0];q++)
      {
       size2++;
      }
      ptr->array[i]=malloc(sizeof(char)*(size2+2));
      ptr->array[i][size2+1]='\0';
      q+=2;
  }

  //Filling the array:

  int c=0;
  for(int i=0;a[i]!='\0';i++)
  {
    if(a[i]!=b[0]){
     for(int j=i,r=0;a[j]!=b[0];j++,r++)
     {
      ptr->array[c][r]=a[j];
     }
     c++;
    }
  }
  return ptr;
}

It is giving me an error. Could somebody explain what I am doing wrong?

I think you are heading in the wrong direction. When I read this:

The function should return an array that contains a string

it tells me that the function shall return a char-pointer to a zero-terminated char array (ie a C style string).

In other words - what you are trying to return is way to complex.

Something like this should do:

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

char* my_split(const char* str, const char sep)
{
    const char* p = str;
    size_t len = 0;
    size_t words = 1;

    // Count the number of chars to copy and the number of words
    while (*p)
    {
        if (*p == sep) ++words; else ++len;
        ++p;
    }

    if (len == 0)
    {
        char* out = malloc(3);
        assert(out);
        strcpy(out, "[]");
        return out;
    }

    // Calculate required memory
    size_t total =
                    1 +              // termination
                    2 +              // [ and ]
                    2 * words +      // " and " around each word
                    2 * (words-1) +  // the , and space between words
                    len;             // the strings

    char* out = calloc(total, 1);
    assert(out);
    strcpy(out, "[\"");
    size_t index = 2;
    p = str;
    while(*p)
    {
        if (*p == sep)
        {
            // Word finished prepare the next word (if any)
            strcat(out, "\"");
            ++index;
            if (*(p+1))
            {
                strcat(out, ", \"");
                index +=3;
            }
        }
        else
        {
            out[index] = *p;
            ++index;
        }
        ++p;
    }
    strcat(out, "\"]");

    // Verify the malloc size
    assert((total-1) == strlen(out));

    return out;

}

int main()
{
  char* input1 = "abc def gh-!";
  char* output1 = my_split(input1, '-');
  printf("%s\n", output1);
  free(output1);

  char* input2 = "a-b-c-d-e-f-g";
  char* output2 = my_split(input2, '-');
  printf("%s\n", output2);
  free(output2);

  return 0;
}

Output:

["abc def gh", "!"]
["a", "b", "c", "d", "e", "f", "g"]

For starters this function declaration:

string_array* my_split(char* a, char* b) {

does not make great sense. The first parameter shall have the qualifier const because the passed string is not being changed in the function and the second parameter shall not be a pointer. Also it is senseless to return from the function a pointer to an object of the type string_array .

The function shall be declared at least like

string_array my_split( const char *s, char c ) {

This loop

  int count=0;
  for(int i=0;a[i]!='\0';i++)
  {
   if(a[i]==b[0])
   {
    count++;
   }
  }

does not count the number of sub-strings separated by the character b[0] because such characters can follow each other without intermediate other characters. So for example this string

"---A-"

has only one sub-string "A" provided that the separator is '-' .

It is unclear why ptr->size is set to the value count+1 .

As the source string can start from the separate character then this loop

  for(;a[q]!=b[0];q++)
  {
   size2++;
  }

will not iterate as a result this memory allocation

  ptr->array[i]=malloc(sizeof(char)*(size2+2));

does not make sense.

And you have to reinitialize the variable size_t to zero within the outer loop

  int size2=0;
  int q=0;
  for(int i=0;i<ptr->size;i++)

Pay attention to that functions that allocate memory can fail. That is they can return NULL. You have to process such situations.

Here is a demonstrative program that shows how the function can be implemented. The returned structure contains three data members: the total number of sub-strings in the string, the actual number of extracted sub-string and the pointer to the allocated memory for sub-string.

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

struct substring_array 
{ 
    size_t total;
    size_t actual;
    char **p; 
} split( const char *s, char c )
{
    struct substring_array a = { .total = 0, .actual = 0, .p = NULL };
    
    //  count the total number of substrings
    for ( const char *t = s; *t; )
    {
        while ( *t == c ) ++t;
        
        if ( *t )
        {
            ++a.total;
            while ( *++t != '\0' && *t != c );
        }
    }
    
    //  extract substrings
    int success = 1;
    while ( success && *s )
    {
        while ( *s == c ) ++s;
        
        if ( *s )
        {
            const char *first = s;
            
            while ( *++s != '\0' && *s != c );
            
            size_t len = s - first;
            
            char *current = malloc( len + 1 );
            success = current != NULL;
            
            if ( success )
            {
                char **tmp = realloc( a.p, ( a.actual + 1 ) * sizeof( char * )  );
                
                success = tmp != NULL;
                
                if ( success )
                {
                    a.p = tmp;
                    strncpy( current, first, len );
                    current[len] = '\0';
                    a.p[a.actual++] = current;
                }
                else
                {
                    free( current );
                }
            }
        }
    }
    
    return a;
}

int main(void) 
{
    const char *s = "abc def gh-!";
    char c = '-';
    
    struct substring_array a =split( s, c );
    
    if ( a.total == a.actual )
    {
        printf( "There were extracted all %zu substrings\n", a.total );
    }
    else
    {
        printf( "There were extracted %zu substrings of total %zu substrings\n", 
                a.actual, a.total );
    }
    
    for ( size_t i = 0; i < a.actual; i++ ) puts( a.p[i] );

    while ( a.actual != 0 ) free( a.p[--a.actual] );
    free( a.p );
    
    return 0;
}

The program output is:

There were extracted all 2 substrings
abc def gh
!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM