Thread: Issues with character arraylists

  1. #1
    Registered User
    Join Date
    Oct 2013
    Posts
    29

    Issues with character arraylists

    I'm working on a program right now that requires me to do a couple of things. First, I have a structure with a couple things in it such as a character arraylist, and then information on that list such as size and capacity.
    For one of the functions I have to write that uses that structure, I have to create an instance of that structure, and return a pointer to it.

    If I created a function that that created an instance of the structure and I labeled it *L, and then filled it with the inputed information, to return the pointer to the new structure, would it be as simple as:
    Code:
    return *L;
    And I have another function I have to make that requires me to expand the arraylist, and copy the contents of one character arraylist into another. In order to do so, I thought I would have to do
    Code:
    for (i=0; i<length; i++){
    strcpy(newarray[i], array[i]);
    }
    Would this work? Would I need to treat it similar to that of an integer array and do
    Code:
    for (i=0; i<length; i++){
    newarray[i] = array[i];
    }
    Im sorry I cant just post my code, this is for a programming assignment, and I don't want to do anything that might constitute cheating.

    Edit: I've gone back and added my full code so far.

    Here is the initial structure:
    Code:
    typedef struct ArrayList
    {
     // We will store an array of strings (i.e., an array of char arrays)
     char **array;
     // Size of list (i.e., number of elements that have been added to the array)
     int size;
     // Length of the array (i.e., the array's current maximum capacity)
     int capacity;
    } ArrayList;
    And "Arraylist.h" is what has the structure definition I included above as well as the function prototypes.

    Code:
    //included libraries
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include "ArrayList.h"
    //Create arraylist
    ArrayList *createArrayList(int length){
    //Dynamically allocate space for new ArrayList
        ArrayList *L = malloc(sizeof(ArrayList));
        int i;
        //initialize to NULL
        L->array = NULL;
        if (length >= DEFAULT_INIT_LEN)
        {
            L->array = malloc(sizeof(char*)*length);
            if (L->array != NULL)
                {
                    printf("-> Created new ArrayList of size %d\n",length);
        //initialize pointers in array to NULL
                    for (i=0; i<length; i++){
                        L->array[i] = NULL;
                    }
        //Set size and capacity
                    L->size = 0;
                    L->capacity = length;
                    return *L;
                }
        //In case malloc fails
            else{
                    printf("\n malloc failed.\n");
                return;}
        }
        //if length is smaller than default size
        else
        {
            L->array = malloc(sizeof(char*)*DEFAULT_INIT_LEN);
            if (L->array != NULL)
                {
                    printf("-> Created new ArrayList of size %d\n",DEFAULT_INIT_LEN);
                    L->size = 0;
                    L->capacity = DEFAULT_INIT_LEN;
                    for (i=0; i<DEFAULT_INIT_LEN; i++){
                        L->array[i] = NULL;
                    }
                return *L;
                }
            else{
            printf("\nmalloc failed.\n");
                return;}
        }
    }
    //Destroy arraylist
    ArrayList *destroyArrayList(ArrayList *list){
    int i;
    //free everything in the array first, then free the array itself
    for (i=0; i<list->capacity; i++){
        free(list->array[i]);
    }
    free(list->array);
    free(list->size);
    free(list->capacity);
    free(list);
    //set list to NULL and return list
    list = NULL;
    return *list;
    }
    //Expand arraylist to new size
    ArrayList *expandArrayList(Arraylist *list, int length){
    int i;
    char newarray = malloc(sizeof(int*)*length);
    if (length <= list->capacity)
        return;
    if (*list == NULL)
        return;
    for (i=0; i<length; i++){
    strcpy(newarray[i], list->array[i]);}
    
    //free the orginal array, since it has been copied over to the new array
    for(i=0; i<list->capacity; i++){
            free(list->array[i]);
            }
    free(list->array);
    //set list->array to point to the newly created array
    list->array = &newarray;
    printf("-> Expanded ArrayList to size %d", length);
    list->size = _;
    list->capacity = ___;
    return list->**array;
    }
    //trim the arraylist if it's capacity is larger than its current size
    ArrayList *trimArrayList(ArrayList *list){
    if (list->capacity <= list->size )
        return;
    char newarray = malloc(sizeof(char*)*list->size);
    for (i=0; i<list->size; i++){
        newarray[i] = list->array[i];
    }
    printf("-> Trimmed ArrayList to size %d", list->size);
    }
    And an 'arraylist' is the term my professor used for an array of arrays.
    Last edited by blackfox_1109; 01-31-2014 at 01:12 PM.

  2. #2
    Registered User MutantJohn's Avatar
    Join Date
    Feb 2013
    Posts
    2,665
    As long as you use malloc(), returning a pointer to that allocation is valid. It's because a pointer's value is an address so what you're passing back from your function is a copy of the address. Unlike stack allocations, heap allocations are persistent between function contexts.

    Edit : It would also help to see your structure as well.

  3. #3
    Registered User
    Join Date
    Nov 2010
    Location
    Long Beach, CA
    Posts
    5,909
    Quote Originally Posted by blackfox_1109 View Post
    Im sorry I cant just post my code, this is for a programming assignment, and I don't want to do anything that might constitute cheating.
    Posting more code would be much more useful, and it wouldn't constitute cheating, so long as nobody here gave you a complete answer that you subsequently used. At the very least, showing us the struct and some basic usage/creation would be helpful.

    Quote Originally Posted by blackfox_1109 View Post
    I'm working on a program right now that requires me to do a couple of things. First, I have a structure with a couple things in it such as a character arraylist, and then information on that list such as size and capacity.
    For one of the functions I have to write that uses that structure, I have to create an instance of that structure, and return a pointer to it.
    I don't know what an 'arraylist' is. It is not a standard term used in programming, so a description of it and/or some code (preferably with comments) that illustrate it's purpose and use, would be helpful.

    Quote Originally Posted by blackfox_1109 View Post
    If I created a function that that created an instance of the structure and I labeled it *L, and then filled it with the inputed information, to return the pointer to the new structure, would it be as simple as:
    Code:
    return *L;
    Usually, if you have to create an instance, you're using malloc. Thus, you have something like:
    Code:
    struct some_type *new_some_type(void)
    {
        struct some_type *foo = malloc(sizeof(*foo));
        // maybe initialization of the members
        return foo;  // this returns a pointer to the malloc'ed object
    }
    If you return *foo; you end up returning the object itself, i.e. returning a struct not a pointer to it. This has (at least) two problems. First, you have a memory leak. You malloc memory, then lose the address (you can't get it back from foo in the funciton, once the function returns), so you can never free it. Second, it's less efficient to return a whole struct than simply a pointer to struct, especially when it's a big one.

    Quote Originally Posted by blackfox_1109 View Post
    And I have another function I have to make that requires me to expand the arraylist, and copy the contents of one character arraylist into another. In order to do so, I thought I would have to do
    Code:
    for (i=0; i<length; i++){
    strcpy(newarray[i], array[i]);
    }
    Would this work? Would I need to treat it similar to that of an integer array and do
    Code:
    for (i=0; i<length; i++){
    newarray[i] = array[i];
    }
    I can't answer this one for sure without knowing how newarray and array are declared -- which may or may not include giving us the struct definition. That being said, if newarray and array are just regular char arrays (like you would use for a string), then your first loop is technically correct, though wildly inefficient, while your second loop is closer to the canonical way to copy a string, but may be incorrect as (I'm guessing) it leaves off the null terminator. Of course, if your custom struct doesn't use null terminators, then the first version (strcpy) is wrong. So...care to provide more info?

  4. #4
    Registered User
    Join Date
    Sep 2006
    Posts
    8,868
    Make a little similar code snippet. One that shows what you want to ask a question about, but isn't anything that you are turning in. I'm not quite sure what a "character arraylist", is. C doesn't have "character arraylists". It has character arrays, and it has lists (single and double), and arrays of lists, and structures that might have all of these and more.

  5. #5
    Registered User
    Join Date
    Oct 2013
    Posts
    29
    Since the first thing in the main looks something like
    Code:
    Arraylist *L1 = createarraylist( //the desired arraylist length );
    That is why in my first function I return a pointer to a function with the name *L, because it will just end up getting assigned to *L1, right?
    When I initially tried to compile the createarraylist function (The one that is supposed to return *L), it responded with "error: incompatible types when returning type 'Arraylist'", so I'm not quite sure why it is not working then. If it helps, here is where I first declare *L. Am I doing something wrong?
    [code]
    ArrayList *L = malloc(sizeof(ArrayList));
    [\code]

  6. #6
    Registered User
    Join Date
    Jun 2011
    Posts
    4,513
    The pointer syntax can be confusing at first.

    You use an asterisk when declaring a pointer variable:

    Code:
    int *x;
    But not when using the pointer variable:

    Code:
    x = get_int_pointer();
    In this context, "using" would include returning it from a function (this is clearly illustrated in the code posted by anduril462 in post #3).

    The only time you use an asterisk with a pointer variable (apart from when it's declared) is when you want to "dereference" the pointer (to see what it's pointing at).

  7. #7
    Registered User MutantJohn's Avatar
    Join Date
    Feb 2013
    Posts
    2,665
    Yeah, return L, not *L. When you declare *L, you're declaring a pointer. From this point on, if you write *L, you're dereferencing that pointer which means, because L is an address now (which is what you want to return), *L is what is at that address. It's kinda like a house in the sense that there's a house and it has an address. There's the location and the actual thing at that location. What you want to do is return a copy of that location (L) and not the house (*L).

  8. #8
    Registered User
    Join Date
    Oct 2013
    Posts
    29
    From what I understood, you can declare and use the pointer in the same line if you do
    Code:
    int *foo = malloc(sizeof(int));

  9. #9
    Registered User MutantJohn's Avatar
    Join Date
    Feb 2013
    Posts
    2,665
    You are declaring a pointer and assigning a value to it with that line.

    int *foo is the declaration and = is the assignment operator. foo is now the address of an integer somewhere in your computer's ram. *foo is the integer stored at that address. To return the point, simply return foo.

  10. #10
    Registered User
    Join Date
    Nov 2010
    Location
    Long Beach, CA
    Posts
    5,909
    Quote Originally Posted by blackfox_1109 View Post
    From what I understood, you can declare and use the pointer in the same line if you do
    Code:
    int *foo = malloc(sizeof(int));
    Yes, you understood correctly. The = malloc(...) part is technically called an initializer in this case.

    You seem to have missed one thing though, the fact that I used the variable name in the sizeof expression (with a * in front of it). Instead of using an explicit type, you use the type of the thing pointed to by foo, whatever that type may be (foo is a pointer to int, so *foo is an actual int).
    Code:
    int *foo = malloc(sizeof(*foo));
    Your version (using sizeof(int)) is fine -- it works as intended. The advantage to the version I gave, however, is that you can change the type of foo, and you only need change the declaration part. The sizeof expression will always be correct. If you change foo to a struct some_type, or if you are using a struct or typedef'ed type, and change the name of said struct/type, then you only need change the declaration. Granted, this is a small issue, but it does help make code maintenance easier.


    Now for the code you edited into your OP (I made ArrayList.h as best I could, though I called it arraylist.h and updated the #include directive accordingly):
    First, compile with warning levels at the maximum:
    Code:
    $ make arraylist
    gcc -Wall -ggdb3 -pedantic -std=c99 -O0 -o arraylist arraylist.c -lm -lpthread -lrt
    In file included from arraylist.c:5:0:
    arraylist.h:18:28: error: unknown type name ‘Arraylist’
    arraylist.c: In function ‘createArrayList’:
    arraylist.c:26:13: error: incompatible types when returning type ‘ArrayList’ but ‘struct ArrayList *’ was expected
    arraylist.c:31:13: warning: ‘return’ with no value, in function returning non-void [enabled by default]
    arraylist.c:45:13: error: incompatible types when returning type ‘ArrayList’ but ‘struct ArrayList *’ was expected
    arraylist.c:49:13: warning: ‘return’ with no value, in function returning non-void [enabled by default]
    arraylist.c: In function ‘destroyArrayList’:
    arraylist.c:60:5: warning: passing argument 1 of ‘free’ makes pointer from integer without a cast [enabled by default]
    /usr/include/stdlib.h:488:13: note: expected ‘void *’ but argument is of type ‘int’
    arraylist.c:61:5: warning: passing argument 1 of ‘free’ makes pointer from integer without a cast [enabled by default]
    /usr/include/stdlib.h:488:13: note: expected ‘void *’ but argument is of type ‘int’
    arraylist.c:65:5: error: incompatible types when returning type ‘ArrayList’ but ‘struct ArrayList *’ was expected
    arraylist.c: At top level:
    arraylist.c:68:28: error: unknown type name ‘Arraylist’
    arraylist.c: In function ‘trimArrayList’:
    arraylist.c:93:9: warning: ‘return’ with no value, in function returning non-void [enabled by default]
    arraylist.c:94:21: warning: initialization makes integer from pointer without a cast [enabled by default]
    arraylist.c:95:10: error: ‘i’ undeclared (first use in this function)
    arraylist.c:95:10: note: each undeclared identifier is reported only once for each function it appears in
    arraylist.c:94:10: warning: variable ‘newarray’ set but not used [-Wunused-but-set-variable]
    arraylist.c: In function ‘destroyArrayList’:
    arraylist.c:66:1: warning: control reaches end of non-void function [-Wreturn-type]
    make: *** [arraylist] Error 1
    Issues

    • expandArrayList should have a parameter of type ArrayList (note the uppercase 'L')
    • Some variables are undeclared, others declared and never used -- fix this.
    • If your function is supposed to return something, then it must, under all circumstances, return something of the right type (or just plain return; if you want to leave a void function early, otherwise reaching the end of the function automatically returns). Make sure every code path reaches an appropriate return.
    • Specifically, in a few places (createArrayList and destroyArrayList), you're trying to return the actual struct (using *L or *list) instead of the pointer. We've told you several times how to fix that. If there's an error in a function returning a pointer, a common convention is to return NULL; to signify this. Just a plain return; will return arbitrary garbage values, and may cause your program to crash.
    • In destroyArrayList, you set list to NULL, then return *list. That means you are trying to return the object at address NULL. That is invalid, resulting in undefined behavior, which means anything at all can happen, but likely just a program crash.
    • size and capacity are ints, why are you trying to free them. You only free variables/struct members which were assigned the return value from a malloc call.
    • You must assign the value of malloc to a pointer (expandArrayList). This is a perfect example of why you should use the variable name instead of the type in sizeof. You give sizeof(int*), but are trying to assign to a char.
    • You don't need the & when assigning newarry to array.
    • If you're trying to compare a pointer to NULL, compare the pointer, not the object it points to. That is, in expandArrayList, you should be comparing list == NULL, not *list == NULL.
    • I don't know what _ and __ are for, but they're invalid.
    • I have no idea what you're trying to return from expandArrayList, but list->**array is invalid, and incorrect. Probably you just want return list;.


    Note, you should look into the use of realloc for resizing your "arraylist". It's more efficient and easier than allocating new memory, copying and freeing the old.

    It seems like you lack some fundamentals of working with pointers and dynamic memory. Perhaps go back and re-read your class notes and textbook, and some online resources. Do all the practice problems and tutorials on the basics, working your way up to more complicated stuff. Work through each one as slow as you need to, to make sure you understand it, before you move on.

    This also seems like a case of "write it all and pray it works". That's the wrong approach, especially when you're new. General steps:
    1. Understand the problem
    2. Work out a solution with paper and pencil, in English (or your native tongue)
    3. Turn that solution into pseudo code
    4. Turn that pseudo code into real code. Work in small chunks (5-10 lines at a time). Compile at maximum warning level, and remove all errors and warnings. Test your code to make sure it does what it should. Don't move onto the next chunk until all previous chunks work as expected.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 10
    Last Post: 07-05-2011, 08:21 PM
  2. Character counting issues from Kernighan text
    By Vampireclown in forum C Programming
    Replies: 2
    Last Post: 05-01-2010, 10:29 AM
  3. multi-character character constant error
    By robwhit in forum C Programming
    Replies: 3
    Last Post: 07-15-2007, 12:12 AM
  4. wide character (unicode) and multi-byte character
    By George2 in forum Windows Programming
    Replies: 6
    Last Post: 05-05-2007, 12:46 AM
  5. ArrayLists + Inner Structs
    By ginoitalo in forum C# Programming
    Replies: 5
    Last Post: 05-09-2002, 05:09 AM

Tags for this Thread