Solved

String trouble

Posted on 2004-09-25
13
222 Views
Last Modified: 2010-04-15
Ah hello.

I would just like clarification on something that has always been a bain to me, strings and pointers (oh yes, that little gem of C programming).  Consider this code:

void F(char* p)
{
      char* p2 = NULL;
      //p = "ANOTHER TEST";
      p2 = strstr(p, "TEST");
      strncpy(p2, "FAKE", 4);
}

int main ()
{
      char str[] = "TEST STRING";
      F(str);
      printf(str);
}

Right.  Obviously F() exists to alter the string passed to it.  Here is my confusion.

1) I can alter the string *permanently* via this code, i.e. the output is not TEST STRING but FAKE STRING since it has been altered in F().  If I can operate on 'p' from within F() like this using strstr and strncpy, why does the line commented out not work ?  Why can I just not change the string directly via assignment ?

2) The line I have commented out, when removed, causes the program to crash with an access violation (in assembly code).  strncpy is what causes the crash, and I cannot see what is going on.  Can you ?

Thanks in advance.
0
Comment
Question by:mrwad99
13 Comments
 
LVL 55

Expert Comment

by:Jaime Olivares
Comment Utility
Hi mrwad99,

Your are passing a pointer by value, so, if you change the pointer inside the function, and alter its contents, pointer will be not be altered at the calling side (main function). 'str' still points to the same memory position ("TEST STRING"). There is a workaround to this. Pass pointer 'by reference' that is, a pointer to a pointer. Take a look to this:

void F(char **p)   /* p is now a pointer to a pointer */
{
     char* p2 = NULL;
     //p = "ANOTHER TEST";   /* now you can make: *p = "ANOTHER TEST" */
     p2 = strstr(*p, "TEST");   /* look change at *p */
     strncpy(p2, "FAKE", 4);
}

int main ()
{
     char *str = "TEST STRING";
     F(&str);  /* Passing by reference, str could point to other string at returning time */
     printf(str);
}



Good luck,
Jaime.
0
 
LVL 19

Author Comment

by:mrwad99
Comment Utility
Hi Jaime,

thanks for the response.  I was however well versed on all that and was aware that I could change the value of the string passed by passing a pointer to a pointer (indeed, the first question I ever asked, http:Q_20424199.html, sorted that out for me !).  

What the problem is is the fact that *I cannot see how*

    p2 = strstr(p, "TEST");
     strncpy(p2, "FAKE", 4);

*does* manage to alter the string, since it too is operating on p, and then a pointer to p (p2).  I did not expect this to work, but was surprised when it did.
0
 
LVL 86

Assisted Solution

by:jkr
jkr earned 20 total points
Comment Utility
Actually, the above code works as advertised (only 'strncpy()' should be called a length argument that takes the trailing NULL in account), no need to pass anything by reference:

#include <stdio.h>

void F(char* p)
{
    char* p2 = NULL;
    //p = "ANOTHER TEST";
    p2 = strstr(p, "TEST");
    strncpy(p2, "FAKE", 5);
}

int main ()
{
    char str[] = "TEST STRING";
    printf(str);
    F(str);
    printf(str);
}

produces:

TEST STRING
FAKE


The crash in the commented line is because you are sssigning a string literal, which is held in a read-only data segment.
0
 
LVL 19

Author Comment

by:mrwad99
Comment Utility
>>  Actually, the above code works as advertised

Yeah I know, that is what I cannot understand

>> ...only 'strncpy()' should be called a length argument that takes the trailing NULL in account)

well I am using it to replace part of the string, so not really here...

>>  strncpy(p2, "FAKE", 5);

if that 5 is a 4, then the output is

TEST STRINGFAKE STRING

which is required.

But I am confused as to *why*

  p2 = strstr(p, "TEST");
    strncpy(p2, "FAKE", 5);

alter the string when simple assignment will not !
0
 
LVL 86

Expert Comment

by:jkr
Comment Utility
>>But I am confused as to *why*
>> [...] alter the string when simple assignment will not !

That's where jaime_olivares has a point about passing a pointer to a pointer. An assignment will only alter the local copy.
0
 

Expert Comment

by:iportu
Comment Utility
When you declare an array with empty brackets, the C compiler allocates enough memory to hold the characters specified (and the NULL terminator), assigning to your variable "str" a pointer to the first character. By trying to copy "ANOTHER TEST" to "TEST STRING" you are overwriting the string's memory locations, "str" is not big enough to hold the string that is being copied to it. In addition, the reason for using strcpy is because C never lets you assign entire arrays. Hope this helps in some way or another.
0
How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

 
LVL 19

Author Comment

by:mrwad99
Comment Utility
Right sorry to keep pushing this but I am not sure that the question has been answered yet.  Let me first just clarify basics about the function F:

void F(char* p)

Right.  This clearly takes a pointer to a char, or a string.  It does not take a pointer to a string, that would be char**, but a string.  Fine.  

Now I appreciate that trying to alter p by assigning to it is futile; it would not alter anything in the calling code.  This is why

p = "Another string"; // or whatever

would fail.

However, it is clear that I can state

char* p2 = strstr(p, "TEST")

which finds the location that "TEST" first appears in the first parameter - i.e. p (which we have said is only a local copy) and then

strncpy(p2, "FAKE", 4);

to copy the word "FAKE" into the location pointed to by p2 (which is a location in p (which in itself is a local copy)).

*And this actually alters the parameter in the calling code, i.e. str !*

*So my overall conclusion is that I cannot alter a string passed like this by assignment (because it is only a local copy), but for some reason there is magic in strstr and strncpy that somehow does allow the actual variable passed in to be altered !*

Now I am not sure my ideas and statements here are correct; all I know is that the code I have above works and I am not sure why !  Can someone now explain what is going on please ?
0
 
LVL 1

Assisted Solution

by:Feldspar
Feldspar earned 60 total points
Comment Utility
The pointer p is a local copy, however the contents of that string are not local.  I think that's what you're missing here, you dont get a local copy of the string, just a local copy of the pointer to it.  The local p will point to the same location in main memory that your string str[] in main points to.  


memory:
[    TEST STRING    ]
      ^ str[] points to here
      ^ p = str[] so it also points here

now your code:

char* p2 = NULL;
p2 = strstr(p, "TEST");

so :
[    TEST STRING    ]
      ^ str[] points to here
      ^ p also points here
      ^ p2 points here too

strncpy(p2, "FAKE", 4);
will alter this part of memory, which is your only copy of the actual text.

I hope this helps
0
 
LVL 3

Accepted Solution

by:
CmdrRickHunter earned 80 total points
Comment Utility
to expand on feldstar, lets take a look at the more useful example... the one that doens't work

in main:
 char str[] = "TEST STRING"
 F(str)

at this point, str "points" to "TEST STRING", which we will say is located at memory address 1000.  this means the actual value of str is not "TEST STRING", but 1000 - the memory address of what it is pointing at.

now in F:
 p = str = 1000   <-- because str was passed in
 p = "ANNOTHER TEST";

lets say the phrase "ANNOTHER TEST" resides in memory address 2000.  main::str (the variable named str, within the main() function) is 1000 (pointing to "TEST STRING").   But since we just assigned p to point at "ANNOTHER TEST", p now equals 2000.  This just made p look somewhere else, instead of what you expected: change the text at 1000 to become "ANNOTHER TEST"

then when F ends, str is still pointing at 1000.  the strncpy(p2...) still occured, so the string at memory address 1000 reads "FAKE STRING".


The best way I've found to understand how pointers work is to treat them just as they are: they are a numerical value of the memory address of something else (the object they point at).  They get passed by value (copied), just like anything else... but its the numerical value that gets copied, not the stuff they point at.  After getting past that hurdle, it makes a fair bit of sense
0
 
LVL 19

Author Comment

by:mrwad99
Comment Utility
Feldspar/CmdrRickHunter:

Thanks a lot for that.  I think that has sorted out what is going on now.  One last question though:

    char* p2 = NULL;
    p = "ANOTHER TEST"; //*
    p2 = strstr(p, "TEST");
    strncpy(p2, "FAKE", 4);

with the previously commented line uncommented (//*) this crashes because p2 now points to an area of memory (i.e. p) that is, as jkr said, a string literal, and trying to assign to this is causing the crash ?  Correct ?
0
 
LVL 9

Assisted Solution

by:ankuratvb
ankuratvb earned 20 total points
Comment Utility
Yes.Because a string literal may be stored in non-modifiable or read only memory and trying to change it will cause the program to crash.

If you're still having problems, see these:

http://www.experts-exchange.com/Programming/Programming_Languages/C/Q_20593673.html

http://www.experts-exchange.com/Programming/Programming_Languages/C/Q_20704933.html

0
 
LVL 8

Assisted Solution

by:ssnkumar
ssnkumar earned 20 total points
Comment Utility
> p = "ANOTHER TEST"; //*
Here the parser parses and places the string literal "ANOTHER TEST" in the symbol table.
So, it will be part of the code segment!
So, by the above statement, p will get an address of the code segment.
Now by the statement:
> p2 = strstr(p, "TEST");
p2 will get an address on the code segment (since p is having an address from symbol table)
The next statement:
> strncpy(p2, "FAKE", 4);
is trying to change the value of Symbol Table, which is READONLY from the perspective of your code. Hence it raises a segmentation fault.

So, to correct that code, either you have to get an address from the stack, data segent or heap.
If you use an array and copy the initial string using strcpy(), then it will work.
Or it you allocate memory by using malloc() or calloc() and then copy the string to the place by using strcpy(), then also your code will work.

Hope this helps.

-ssnkumar
0
 
LVL 19

Author Comment

by:mrwad99
Comment Utility
OK, points were split with CmdrRickHunter getting the accepted answer for his explanation.  Others received points for the relavant discussion about why char* cannot be modified.

Many thanks to all :)
0

Featured Post

How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

Join & Write a Comment

Preface I don't like visual development tools that are supposed to write a program for me. Even if it is Xcode and I can use Interface Builder. Yes, it is a perfect tool and has helped me a lot, mainly, in the beginning, when my programs were small…
This is a short and sweet, but (hopefully) to the point article. There seems to be some fundamental misunderstanding about the function prototype for the "main" function in C and C++, more specifically what type this function should return. I see so…
The goal of this video is to provide viewers with basic examples to understand and use pointers in the C programming language.
Video by: Grant
The goal of this video is to provide viewers with basic examples to understand and use for-loops in the C programming language.

762 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

7 Experts available now in Live!

Get 1:1 Help Now