Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » Strings...The mystery

This thread is locked; no one can reply to it. rss feed Print
 1   2 
Strings...The mystery
wit
Member #858
November 2000
avatar

OK. I'll try to explain my problem in a short way. I have a function in my game which asks the players name. It has a char array of 25 chars which is modified by inputing the name. The function returns the array. Another function which calls that function calls also strcpy() to copy the returned string to another string.
Nothing seems to work. The string for players name contains (null).
Hmmm...Here is the prototype of code:
a_function(){
...
char * player_name;
...
strcpy(player_name,ask_name());
...
//The player_name has the value null here
}

ask_name(){
char str[25]="Put you name here ";
...
//Modify str array here
...
return(str);
}

Daniel Micheal
Member #472
June 2000

wit, in C remember that strings are simply arrays of characters which are terminated by a null character of the value 0. If you have a variable called "char *player_name" that simply means its pointing to an array of characters. Its known as a dynamic array, as opposed by "char player_name[255]" which is static, and the length is non-changable. You cannot write past 255 characters.
void ask_player_name(char *playername)
{
/* the string being pointed to by playername can be changed since it doesnt have the 'const' keyword before it and its simply a pointer. We cannot however, change the actual address being pointed to. */
strcpy(playername, "player1");
/* playername = "player1" is invalid since it will try to change address being pointer to by playername to a the address of the string 'player1'. */
}
int any_func(void)
{
char *stringa;
char stringb[64];
/* staticarray[100] can be parsed to a function as *dynarray
staticarray[50][10] can also be parsed as **dynarray
Simply, when refering to an array without the indexing [], it is treated as a pointer.
*/
ask_player_name(stringb);
/* stringa must be initialized first before using it */
stringa = (char *) malloc(64);
if (!stringa)
return SOME_ERROR_CODE;
ask_player_name(stringa);
free(stringa);
}
The advantages of using dynamic strings is becuase you can change the size of it as and when you need it. Its nessasary for anything larger than about 1024 bytes since it is usually all initialized when a function is called. Its therefor quicker to use this malloc/free method than static strings UNLESS they are defined using the 'static' keyword. It simply means its initialized at startup and remains the same once the function call is finished, ie, if you call a function to set a static variable to 5, it will be 5 again when you call it again.
The problem with your code seems that you arent defining a return type for ask_name() and that your not initializing player_name first. If you want to use it like that you need to use malloc/free. If not, declare player_name as char player_name[25]. Doing it like that might better since its a relatively small array.
ps, if you want to avoid potential memory leaks and segmentation faults (writing to memory thats not yours, like a NULL pointer in your case assuming player_name is initialized to NULL) then never do this:
BAD: char *mystring = "a string";
GOOD: char mystring[] = "a string";
BAD: char *mystring = malloc(50);
mystring = "test";
/* Leaked 50 bytes by not freeing memory before derefencing or losing the pointer */
free(mystring); /* Even worse */
GOOD: char *mystring = malloc(50);
strcpy(mystring, "test");
free(mystring);
Hope this helps... :)
CorsairK8

Bruce Perry
Member #270
April 2000

> char * player_name;
This creates a pointer which can be used to point to a char or an array of chars (ie. a string), but it does not allocate room for the chars themselves. Do this:
char player_name[25];
> strcpy(player_name,ask_name());
Fine.
> //The player_name has the value null here
You're lucky you got away with it :-) I suspect you're using DJGPP and running in a Windows DOS box - plain DOS and any other platform would have segfaulted or something.
> ask_name(){
Woah! Don't forget to specify what type the function is.
char *ask_name() {
> char str[25]="Put you name here ";
static char str[25];
strcpy(str,"Put your name here");
Don't forget the 'r' on 'your'.
Unless you declare it static, the string's memory will no longer be allocated on return. You'd get away with it (since it was on the stack), but it's a bad habit and is not guaranteed to work on all platforms.
By declaring it static, you're telling the compiler to put it somewhere safe so that it will retain its value from one call to the next. Hence you will want to reinitialise it each time, but typing ="..." will only initialise it the first time.
> //Modify str array here
No problem.
> return(str);
> }
Fine.
There is however another method you may find more useful in this case. You could do the following:
void ask_name(char *name) {
strcpy(name,"Put your name here");
...
// modify name here - you can access it like an array, using name[0], name[1], etc.
...
// Do not type "return name;"
}
a_function() {
...
char player_name[25];
...
ask_name(player_name); /* you are passing a pointer to the string. ask_name() cannot modify the parameter itself (ie. the pointer), but it can change what the pointer refers to (ie. the string). */
...
}

--
Bruce "entheh" Perry [ Web site | DUMB | Set Up Us The Bomb !!! | Balls ]
Programming should be fun. That's why I hate C and C++.
The brxybrytl has you.

Bruce Perry
Member #270
April 2000

I KNEW that would happen! Two answers (probably conflicting) at the same time!

Read mine for the quick fix. Read CorsairK8's if you want to know what's going on.

--
Bruce "entheh" Perry [ Web site | DUMB | Set Up Us The Bomb !!! | Balls ]
Programming should be fun. That's why I hate C and C++.
The brxybrytl has you.

ReyBrujo
Moderator
January 2001
avatar

Remember that declaring a variable static is nearly the same than declaring it global. So you can access the variable no matter where. I will recommend CorsairK8's way... just remember to set the pointer to NULL once you have freed the memory with "free". If you want to use BDavis' way, I would declare it global. Statics will only be "recognized" once the function was called, and that can create some problems if you try to access it before it is "created".
If I were you:
#define NAME_LENGTH 24
char *ask_player_name() {
char *player_name;
player_name=(char *)malloc(NAME_LENGTH+1);
if (player_name != NULL) {
// Ask the player to give out his name
// and assing it to player_name
}
return (player_name);
}

void a_function() {
char *name;
// See, no need of strcpying it. Doing that will make
// a very nice buffer overrun cackle
name = ask_player_name();
if (name!=NULL) {
...
...
free (name);
}
return;
}
RB

--
RB
光子「あたしただ…奪う側に回ろうと思っただけよ」
Mitsuko's last words, Battle Royale

amarillion
Member #940
January 2001
avatar

Hehe, strings are always a bit tricky in C. This it the one and only thing BASIC is actually better at (or at least easier).

vpenquerch
Member #233
April 2000

Just a bit of clarification:

char *blah="hello, world";

is correct: this is a pointer that points to a string constant, and this data can not be modified.

char blah[222]="hello, world";

is correct and is a pointer pointing to a non constant (eg you can strcpy to it) portion of memory.

tell me if a goofed ;)

zaphire
Member #1,273
September 2000
avatar

Aside: you can also find the apstring class, by searching for it on the 'net. This class allows you to implement strings very easily. (of course, this is only for C++) You could probably find some structs for C somewhere on the net, that could make the job of handling string a bit easier...

vpenquerch
Member #233
April 2000

or the stl string, if you want something portable and widely used.

wit
Member #858
November 2000
avatar

Thanks a lot guys!

Daniel Micheal
Member #472
June 2000

Strings are very simple, and I think that there is no need for a library to handle string manipulation. It only slows things down when you can handle the string with the standard rtl string functions (string.h). Strings are very important in any kind of programming, you should learn how to use them and understand them since they are a fundamental of the c language. Not knowing how to use strings is like not understanding how to use a 'struct' or use classes.

Amarillion: Basic is not better than C when using string. C allows you to dynamically allocate strings that are very large, and you can use as much memory as the computer has free. BASIC limits you to 64k maximum. There are alot of very bad limits to basic, not to mention that basic is very slow and impossible to create a game with it. There are exceptions, such as using libraries written in C or assembler (directx, and some other qbasic library support this) but the actual game itself is severly limited and can be incredibly slow. Granted, C can give you too much control sometimes and even cause you to crash the computer when doing something incorrectly (its happen to me many times when accessing memory im not allowed to, or dividing by zero, probably two of the most common problems)

wit
Member #858
November 2000
avatar

Ahmm.By the way when we are talking about structures...
when I have a structure like this:
struct SCORE
{
char name[25];
int credits;
};
struct SCORE *high_scores[10];
Is this array of pointers pointing at the same place?
I ask it because when I modify the values stored in high_scores[0] the other nine pointers seem to point at the same place...They give the same value back. Example:
high_scores[0]->credits=18;
printf("%d",high_scores[5]->credits);
\\Guess what it writes on the screen. -> 18
I really need the high_scores[] array to be a pointer array. Is there a way to let them point each at its own place in memory?
OR AM I DOING SOMETHING WRONG COMPLETELY????

WIT

Jason Heim
Member #484
June 2000

wit,
again, you're misusing pointers. you don't need the '*' where you declare that array. by default, i'll bet that the array is initialized to zero, and i'm surprised that when you go modify it, you're not getting a seg fault.
try:

struct SCORE high_scores[10];

and then to modify an entry:

high_scores[0].credits = 10;

if you want it to be an array of pointers, then you must allocate a SCORE struct to each pointer:

struct SCORE * high_scores[10];
for(i=0;i<10;i++){
high_scores[i] = (struct SCORE *)malloc(sizeof(struct SCORE));
}

after that, each entry holds a unique pointer to memory that is SAFE to use :)
although, i don't understand why you need an array of pointers. if you need a pointer to satisfy some other type of function that needs a pointer, you can use the '&' modifier to get the address of anything to pass.
for instance, if you want to set all the bytes of one of your structures to zero, you can do this:

struct SCORE zero_me;
memset(&zero_me, 0, sizeof(struct SCORE));

or if you want to set just the name to all blanks, you can do this:

memset(&(zero_me.name), ' ', sizeof(zero_me.name));

you have to remember that a pointer is just four bytes of memory, like a long int. all it stores is an address, and until you set that address to something useful with 'malloc' or by assigning it to allocated storage, it will most likely point to storage that you don't want to use.
here's yet another way to look at what you're asking for. it's valid code, but i don't recommend doing it this way, just as a demonstration:
[i]
struct SCORE * high_scores[10];
struct SCORE hs0;
struct SCORE hs1;
struct SCORE hs2;
struct SCORE hs3;
struct SCORE hs4;
struct SCORE hs5;
struct SCORE hs6;
struct SCORE hs7;
struct SCORE hs8;
struct SCORE hs9;
high_scores[0] = &hs0;
high_scores[1] = &hs1;
high_scores[2] = &hs2;
high_scores[3] = &hs3;
high_scores[4] = &hs4;
high_scores[5] = &hs5;
high_scores[6] = &hs6;
high_scores[7] = &hs7;
high_scores[8] = &hs8;
high_scores[9] = &hs9;
[i]
the above is valid because you have an array of ten pointers. then you have ten structures statically allocated. then you assign the address of these ten structures to the ten 'slots' in the array. now, all the pointers hold addresses of valid storage of the right size. however, it's a terribly ugly way to code it :)
good luck!
jase

Jason Heim
Member #484
June 2000

aw hell, i messed up a bit. this line:

memset(&(zero_me.name), ' ', sizeof(zero_me.name));

is wrong, the '&' is not necessary. please ignore that line, it's a bad example of what i was trying to show. here are two ways to code it correctly:

/* hard way */
memset(&(zero_me.name[0]), ' ', sizeof(zero_me.name));
/* easy way */
memset(zero_me.name, ' ', sizeof(zero_me.name));

what's worse though is that by setting all the bytes of a character array to non-NULL, i'm creating an unterminated string. you shouldn't set strings like i did in the above at all. i apologize for the confusion, i get overzealous in my wordy explanations :(
jase

wit
Member #858
November 2000
avatar

Thanks again!

ReyBrujo
Moderator
January 2001
avatar

To make things easy, don't put * in front of the structure and use it as an array.

Ah, Sikobabel, the "hard form" isn't that hard. In fact, it's easier to explain someone that, to access a determinated position of a string, you must use the

&string[0], &string[1], &string[2]...

Once he gets used to it, then you can explain that "&string[0]" and "string" are the same grin

Finally, you can explain that string[2] and *(string + 2) are the same, but some compilers make better code with the second one wink

RB

--
RB
光子「あたしただ…奪う側に回ろうと思っただけよ」
Mitsuko's last words, Battle Royale

Bruce Perry
Member #270
April 2000

Does anyone know if DJGPP GCC and/or Ming GCC recognise the optimisation in the first one?

--
Bruce "entheh" Perry [ Web site | DUMB | Set Up Us The Bomb !!! | Balls ]
Programming should be fun. That's why I hate C and C++.
The brxybrytl has you.

mingles
Member #1,002
February 2001

Hmmmmmmmmm thats a tuffy!

I suggest that you take a giant dump on your keyboard that should fix it.

If not then piss between the keys and lick my mammas arse!

Jason Heim
Member #484
June 2000

mingles,

%hanks a lo%!, now my k#yboard do#sn'% s##m %o work %h# way i% us#d to!

plus, %h# %as%# on my %ongu# is v#ry disgus%ing...

pl#as# %ry %o b# mor# h#lpful n#x% %im#

:)

Jason Heim
Member #484
June 2000

rey,
true, but once you go working cross-platform, you have to be careful with that notation. the following cannot all be manipulated the same on all platforms:

char label[10];
char * pointer;
char * pointer2;

for instance, on some platforms with strict compilers you cannot say:

pointer = label + 2;

and the really strict ones won't even let you do:

pointer = label;

but every compiler i've seen will let you use the 'hard way' notation:

pointer = &label[0];
pointer = &label[2];

and every compiler will let you treat these the same:

pointer = &pointer2[0];
pointer = pointer2;

the only difference is that 'label' is treated by some compilers as, well, a label instead of a 4-byte storage unit for pointers (or 8, 16 bytes on 64, 128-bit systems). as such, occasionally compilers will get fussy about how they are used.
how's that for nit-picky? forgive me... it's what i do for a living :)
jase

mingles
Member #1,002
February 2001

I am writing to you to apologise from the heart of my bottom for posting such bad language on this forum (especially to WIT)

I believe that this is the correct code to type in your program:
a_function(){
...
char * player_name;
...
strcpy(player_testicles,ask_name());
...
//The player is a fucknut
}

ask_name(){
char str[25]="Put you name here ";
ps WIT is fucknut

wit
Member #858
November 2000
avatar

To mingles:
Why dont you fuck a horse?
WIT
P.S> Can you stop with this shit?

Bruce Perry
Member #270
April 2000

And back to my question:

Does anyone know if DJGPP GCC and/or Ming GCC recognise that string[2] can be optimised to *(string+2) ?

That's Ming short for MingW32, not Mingles.

--
Bruce "entheh" Perry [ Web site | DUMB | Set Up Us The Bomb !!! | Balls ]
Programming should be fun. That's why I hate C and C++.
The brxybrytl has you.

Tom St Denis
Member #972
February 2001

Um string[2] and *(string+2) are the same thing. They should compile to similar if not identical code.

Bruce Perry
Member #270
April 2000

They should, but do they?
Say string points to 0x100.
MOV 0x100[2],0
MOV 0x102,0
Sorry for the sloppy syntax. Basically I mean that the assembler can refer to element 2 in an array based at the constant address 0x100, or it can refer to address 0x102 directly. The question is whether the compiler realises that the first instruction can be replaced with the second one, which could be more efficient.

--
Bruce "entheh" Perry [ Web site | DUMB | Set Up Us The Bomb !!! | Balls ]
Programming should be fun. That's why I hate C and C++.
The brxybrytl has you.

 1   2 


Go to: