LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (https://www.linuxquestions.org/questions/programming-9/)
-   -   C: pointers, strings, splitting headache (https://www.linuxquestions.org/questions/programming-9/c-pointers-strings-splitting-headache-66660/)

notsoevil 06-19-2003 01:36 PM

C: pointers, strings, splitting headache
 
Okay, I am totally new to C and I think my main problem is that I can see how to solve my problems SO easily in Perl, but I am just getting confused on things.

Like, for instance, I am just trying something simple here and trying to send a string to a function, have that string split on a delimter, then return a string telling me the parts.

Below is one of 20-25 different ways I've tried things. Looking at it for me, it seems it should work. It does compile, but all I get is a segfault. I know I must be way off, but either I am just looking at this the wrong way or I'm just an idiot.

Code:

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

char rollDice ( char pool[] );

int main() {
  char test[] = "4d10";
  char myResults;
 
  myResults = rollDice(test);
 
  printf(myResults);
 
  return 0;
}

char rollDice ( char pool[] ) {
  char *dieNumber, *dieSides;
  dieNumber = strtok(pool,"d");
  dieSides = strtok(NULL,"d");
 
  char dieResults[50];
  sprintf(dieResults,"dieNumber is %s \ndieSides is %s\n",dieNumber,dieSides);
  return dieResults;
}

Now, I can write this so that rollDice prints the results, but I get all screwed up when I try to return the results to main.

dorian33 06-19-2003 01:44 PM

dieResults is declared in the function - so the memory for it is allocated when the function starts and deallocated when function exists
solutions:
1. declare it with 'static' prefix
2. declare it as global variable, outside of any function (including main function)
3. declare it inside main() and pass as argument to modified rollDice function

notsoevil 06-19-2003 01:50 PM

I can't create and return its value from rollDice?

How do I create functions to simply return strings, then? Do I have to create every string variable globally? Seems kind of odd.

Like, what if I just want to have a function that returns a string, that's it (psuedo-code below):

main () {
char mainStr[] = sayYay();
}

sayYay {
char myStr[] = "Yay!";
return myStr[];
}

Is the above not possible without a global variable?


kev82 06-19-2003 02:11 PM

the problem is C doest really have strings as you understand them, it just has arrays which are essentially pointers. when you return dieResults, you are reurning a pointer to where your array(string) is stored but because it is deleted when the function exits, the pointer now points to something completely random and is known as a wild pointer.

you can never return a pointer to a local variable(which is what your doing) because the variable will not exist once the function is left. the best(IMHO) solution as dorian33 suggests is to declare it in main and pass a pointer to it.

making the variable static is a nice hack but for someone else reading the code it might be confusing, and i wouldnt use that technique in an arbitrary function for returning a string.

i wouldnt declare the value as a global variable because it will just pollute the namespace without good cause.

if you dont mind trying c++ then the std::string class is just what your after.

example passing pointer to function:

void sayHi(char *y)
{
sprintf(y, "Hello\n");
}

int main()
{
char x[50];

sayHi(x);
printf("%s", x);
return 0;
}

Hko 06-19-2003 03:41 PM

C is more like assembly/machine-code then perl actually, though the syntax may look more like perl then assembly.

When you have:
Code:

char str[] = "Aloha";
then the variable str does not contain the string "Aloha", but a physical memory addres where "Aloha" is stored, that is why it is called a pointer (well not really physical, because the kernel memory management sits in between, faking physical addresses to you program, but this 100% transparent, so your program doesn't even know that). The size of the variable str is 4 bytes, even if the string contains a whole book. 4 bytes is enough to store a memory address.

If you declare a local variable "char str[10];" then the array of 10 char's and the 4 bytes of the pointer are stored on the stack and they are disposed of when the funtion returns. Now if you return str then you actually return the 4 bytes containing an address where the string was (note past tense) stored while your function was running.

If you don't want to declare the string to be 'static' or globally you can declare the string in the main function, pass the pointer as an argument to the function, and fill it with char's in your function. You cannot change a variable belonging to main() in the function by passing it as an argument, because that will be only a local copy of it. The good news is that you don't need to change it, because it is an address. But because the adsress now is known by the function, the function can write a string to the memory space the pointer points to. So the function writes the string directly into the memory that belongs to main(), and now there's no need to even return something.
Code:

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

void sayYay (char *myStr) {
        strcpy(myStr, "Yay!");
}

int main ()
{
        char mainStr[] = "Aloha";
        printf("The string is: %s\n", mainStr);
        sayYay(mainStr);
        printf("The string now is: %s\n", mainStr);
        return 0;
}

Another option is to create ("allocate") the (memory for) the string inside the function dynamically. Then you really create the string inside the function, which is maybe more like what you were trying to do. This gets more complex though, because memory you allocate dynamically must be freed explicitly. If not, you program will eat ("leak") more and more memory as it creates strings (and/or other things) while it runs, eventually occupying all memory and swap your computer has until the kernel thinks it's been enough and kills your program. After the memory it freed, you cannot use the pointer anymore. This will cause more or less the same problem you have now (segfaults I guess).
Code:

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

char *sayYay (void) {
        /* declare a local string, i.e. a pointer to char's */
        char *myStr;

        /* Ask for memory from the system: 10 times the
        * size of a char. This will allow strings of up
        * to 9 char's. (we need to reserve 1 char for
        * the terminating nul character '\0'.
        * The address of the memory block to hold 10 char's
        * is returned by malloc().  It needs a cast to a
        * char pointer type because malloc() can also allocate
        * memory of other types than char's.
        */
        myStr = (char *) malloc(10 * sizeof(char));

        /* Write "Yay!" to the memory block (the actual string) */
        strcpy(myStr, "Yay!");

        /* return the string (the address of it, that is)
        * the allocated memory it points to remains, as it not on
        * the stack, but on the "free-store" or "heap".
        */
        return myStr;
}

int main ()
{
        /* Declare pointer to char */
        char *mainStr;

        /* There no valid string yet. Not an empty string, not
        * even room to store one!
        */

        /* Let the function create and fill the string */
        mainStr = sayYay();

        /* now there is a valid string pointed to by
        * mainStr, and we can print it.
        */
        printf("The string is: %s\n", mainStr);

        /* Free the memory allocated by the function, here,
        * in main() pointed to by mainStr.
        */
        free(mainStr);

        /* Here there's no string anymore. Though we still have
        * a pointer to char containing a memory address, the
        * memory it points to, is not ours anymore...
        * If we now try to use it, the program will segfault. (try it).
        */

        return 0;
}


notsoevil 06-19-2003 04:04 PM

Thanks for all the help. I was able to get what I wanted, so now I am moving on. I can read and read all the C material I want (like the K&R book I am reading now), but I want to try and apply as I go along, otherwise it's not going to click for me.

If anyone is curious, for now I am just trying to roughly translate this Perl sub into C:

Code:

  sub RollDice {
    my $self = shift;
          my ($pool,$mod,$tn,$flag) = @_;
          my ($number,$sides) = split(/d/,$pool);
          my ($count,@rolls,$showroll,$total);
          for (1..$number) {
                  my $roll = int(rand($sides) + 1);
            push(@rolls,$roll);
          }
          $showroll = join(', ',@rolls);
 
          if ($flag =~ /t/ || $mod){
                  foreach my $roll (@rolls){
                          $total += $roll;
                  }
                  $total += $mod;
          }
          if ($tn){
                  foreach my $roll (@rolls){
                    if ($roll >= $tn){
                            $count++;
                    }
                  }
          }
          my $rmessage = "<font size=-1 color='blue'><i>Rolled: $pool";
          $rmessage .= ($mod) ? "$mod." : ".";
          $rmessage .= ($tn) ? " Target: $tn." : "";
          $rmessage .= ($count) ? " Succeed: $count." : "";
          $rmessage .= ($total) ? " Total: $total." : "";
          $rmessage .= " ($showroll).</i></font>";
          return $rmessage;
  }

So, now that I can get past the problem above, I now need to find a comparable C way of doing Perl's rand(). And it doesn't look like C's rand() is what I want. Any suggestions?


All times are GMT -5. The time now is 01:13 AM.