0

I am new to this site, so I greatly apologize if I do anything wrong with this first post. The way I've written my code (including codes made for reusability), I must use char* arrays. I am converting the passed in char* into all lower-case letters. The problem I am facing deals with my freeing of newString2. Because i'm assigning it to my newString, freeing it would definitely lose the information I assigned to newString, thus losing the data assigned to convertedKey. I am in a hole as to finding out how to successfully free allocated memory, but also return the data of the converted string through my function argument. Is that possible? The following is my current function for converting to lower-case. I am also very new to strdup, so I'm sure I'm not giving it any justice.

void convertKeyCasing (ListElementPtr key, ListElement *convertedKey)
{
    ListElementPtr newString = (ListElementPtr) malloc (sizeof (ListElement));
    int i = 0;
    char ch;
    char *string = key->data.key.key;

    char *newString1 = strdup (string);

    while (isalpha (key->data.key.key[i]) != false)
    {
        ch = tolower(key->data.key.key[i]);

        newString1[i] = ch;

        ++i;
    }

    char* newString2 = strdup (newString1);

    newString->data.key.key = newString2;

    *convertedKey = *newString;

    key = convertedKey;

    free(newString2);
    free (newString1);
    free (newString);
}

My structs in list include:

typedef struct KEY_DATATYPE
{
    char* key;
    int length;
}KEY_DATATYPE;

typedef struct DATATYPE
{
    Q_DATATYPE data;

    KEY_DATATYPE key;

    // maintained so that it still works with list
  union
  {
    char charValue;
    unsigned int intValue;
    float floatValue;
  };
  unsigned short whichOne;
} DATATYPE;

typedef struct ListElement* ListElementPtr;
typedef struct ListElement
{
    DATATYPE data;
    ListElementPtr psNext;
    ListElementPtr psPrev;
} ListElement;

And in my driver, I'm creating a ListElementPtr and assigning it a key value, for example,

ListElementPtr listPtr;
listPtr->data.key.key = "Hello";

Thank you again for the help!

  • Related: There is nothing special about pointers that makes them an exception to the pass-by-address mantra for out-parameters in C. If you need to pass something by address to modify the caller side, then that is exactly what you do: pass the address and make the parameter a formal pointer type of the underlying data type. In this case, you need a *pointer-to-pointer*. I.e `ListElementPtr* key` . I'm certain this is replicated many times over as a duplicate question, and once I find one, I'll link it. – WhozCraig Nov 27 '13 at 09:12
  • @DavidHeffernan Thank you very much for the feedback. I am currently testing this code, and I do see the leaked memory. I am a current student learning C, so I greatly appreciate hearing back from you. I will delete this function, research, and reattempt it. –  Nov 27 '13 at 09:19
  • @WhozCraig Thank you very much for the feedback.I will attempt a new function using **. Your answer really helped a lot. Thanks again. –  Nov 27 '13 at 09:20
  • @user3040968 You don't need a double pointer here. You are not attempting to create a new element. You want to modify the existing one. – David Heffernan Nov 27 '13 at 09:21
  • @DavidHeffernan you're correct. It was the `key = convertedKey` that made me double-take. – WhozCraig Nov 27 '13 at 09:22
  • @user3040968 seriously look at David's answer. – WhozCraig Nov 27 '13 at 09:22

1 Answers1

1

There are simply lots and lots of errors in your code. You leak memory, and setup pointers to memory that you then free. Rather than trying to explain all the errors, I'll attempt to show you how to write the code most simply.

Essentially, from what I can tell, you simply want to convert everything in key->data.key.key to lower case. There is as such no need for any dynamic allocation. You can perform the modifications in-place like this:

void convertKeyToLower(ListElementPtr key)
{
    char *ch = key->data.key.key;
    while (*ch != 0)
    {
        *ch = tolower(*ch);
        ch++;
    }
}

In fact this function is still badly designed. It is mixing together two distinct aspects: list elements, and string handling. I would split the string handling into a separate function:

void convertStringToLower(char *str)
{
    while (*str != 0)
    {
        *str = tolower(*str);
        str++;
    }
}

Or perhaps like this:

void convertStringToLower(char *str)
{
    for(int i = 0; str[i]; i++)
        str[i] = tolower(str[i]);
}

Once you have this at your disposal you can simply write:

convertStringToLower(element->->data.key.key);

And any time that you need to convert a string, in-place, to lower-case, you have the function ready and available.


From your update to the question, it is now clear why you are getting the segmentation fault that you report in the comments. You wrote:

listPtr->data.key.key = "Hello";

This makes listPtr->data.key.key point to a string literal. And string literals are not modifiable. Attempting to do so invokes undefined behaviour (UB) and a typical manifestation of that UB is a segmentation fault. That happens because compilers typically store string literals in read-only memory.

You need to decide whether or not your elements are going to store literals or modifiable strings. You cannot really hope to mix the two. If you attempt to mix the two then you won't be able to determine how to deallocate your elements. That's because you need to free a modifiable string, but you must not attempt to free a literal.

So your only reasonable option is to use modifiable strings always, and always free them when you free your elements. So replace the line of code above with:

listPtr->data.key.key = strdup("Hello");
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • I can see that I am very off with this code, and I am sorry for posting such poorly written code. After testing this function, I seem to hit a seg fault on the line, *ch = tolower(*ch); would this have to do with my key not being initialized correctly? –  Nov 27 '13 at 09:30
  • 1
    Don't be sorry. We all have to start somewhere. I was bewildered by C pointers when I started too. We all were! But yes, if `*ch` gives you a seg fault then that means that `key->data.key.key` is invalid. I can't see that part of your code though so I've no idea what is wrong. – David Heffernan Nov 27 '13 at 09:32
  • I edited my original post to hold my structs and driver initialization. I feel that typing ListElementPtr listPtr does not give it enough information to allocate space for the actual key. Is that the case in this situation? –  Nov 27 '13 at 09:49
  • My updated answer explains the seg fault. Note that `KEY_DATATYPE` seems pointless. You can replace that with `char*` surely? Why store the length separately. C strings are null-terminated. Don't try to go against the system. – David Heffernan Nov 27 '13 at 09:52
  • That is a very good point, thank you. Also, I want to thank you for going into such detail about this question. We do not go over these topics in such detail during class, so this was a huge help to me. I didn't know how simple implementing these functions could be. May I take written notes on your examples? They're really helping me understand the material much better. I managed to edit my test driver, and it seems to be working as of now. Thank you again for your time and help. Also, thank you for explaining in detail. It filled in the blanks that class did not. –  Nov 27 '13 at 10:14
  • Do what you like with my answer. Take whatever notes you feel like. Glad to help. You may consider accepting the answer if you feel it answers the question you asked. – David Heffernan Nov 27 '13 at 10:17
  • 1
    Got it! Thanks for such a great first time on this site. –  Nov 27 '13 at 10:19