Solved

Modify a char[] when passed to a function

Posted on 2004-09-26
27
332 Views
Last Modified: 2010-05-18
Ah hello.  Bit like a similar question I have asked, but I need an answer to this soon:

#include <iostream>

using namespace std;

void Test(char* t)
{
}

int main ()
{
    char str[] = "This is a string";
      Test(str);
      cout << str << endl;
   
      return 0;
}

I would like to modify str by passing it to Test.  Now obviously it will not work with the parameters as-is.

What do I need to change Test to be to make the modification to the char[] ?  I know I could do it if str was a char*, but with it being char[] it has confused me.

<Feeling dumb>

TIA
0
Comment
Question by:mrwad99
  • 8
  • 6
  • 5
  • +3
27 Comments
 
LVL 9

Assisted Solution

by:ankuratvb
ankuratvb earned 75 total points
ID: 12155600
Hi,

Nothing needs to be changed.

what is passed to the function is not the
array, but rather a pointer to the array's first element.
When you look at it that way, there's no exception to the
call-by-value rule: a function receives a copy of its pointer
argument, and it could only modify that copy of the pointer, but
it can certainly *use* that pointer (without even modifying it)
to modify what it *points to*, namely something in the caller.

A canonical example is the function getline:

 char line[100];
 int len = getline(line, 100);

This hypothetical (or see K&R for concrete examples) getline
function returns two things: the length of the line it reads (as
its formal return value), and the contents of the line it reads,
which it "returns" by writing it into what appears to be its
array argument.  (Another way of thinking about the situation is
that getline's first argument tells getline where to write the
line.)

It is perfectly legal, quite possible, and most common for a
function to modify an array which seems to have been passed to it
(usually by mentioning the array's name in the function call
argument list).  The only time this wouldn't work would be if the
array were not modifiable, either because it was declared const
or because it is a string literal.  The call to getline above
would work, but these three might not:

 const char constline[100];
 len = getline(constline, 100);

 char *p = "Hello, world!";
 getline(p, 100);

 getline("Hello, world!", 100);

These calls would not work because, again, the arrays are not
modifiable.


Take the following example:

/* prototype for function */
void foo(char *bar);

/* declaration */
char array1[100];

main()
{
 foo(array1);
}

In this example, the function foo() can modify any member of the array
"array1" at will.  You do not have to pass the address of the array
name, because by using the array name, you are actually passing
&array1[0], or in english, the address of the first member of the array,
which for argument's sake is also the address of the array.  This allows
any manipulation of the array inside the function that the function
wishes.  As a matter of fact, passing a copy of an array into a function
is the real challenge, and is usually done in a roundabout sort of way
by placing the array inside a structure and passing the entire structure
as a parameter to the function.  Another method is to declare two
arrays, and before calling the function, copy the first array into the
second by using something like memcpy().


0
 
LVL 19

Author Comment

by:mrwad99
ID: 12155608
#include <iostream>

using namespace std;

void Test(char* t)
{
      t = "TEST";
}

int main ()
{
    char str[] = "This is a string";
      Test(str);
      cout << str << endl;
   
      return 0;
}

But the output is "This is a string" - the change has not been made ?!
0
 
LVL 9

Expert Comment

by:ankuratvb
ID: 12155621
Note:
>>
a function receives a copy of its pointer
argument, and it could only modify that copy of the pointer, but
it can certainly *use* that pointer (without even modifying it)
to modify what it *points to*.
<<
0
 
LVL 19

Author Comment

by:mrwad99
ID: 12155635
>> it can certainly *use* that pointer

like this

void Test(char* t)
{
      strcpy(t, "Changed string");
}

which now does produce "Changed string" in the output

?
0
 
LVL 9

Expert Comment

by:ankuratvb
ID: 12155693
Try and think what you're doing.

In your function what you're doing is:

t="TEST";

t is a copy of the address of the first element of the array.You modify the copy to point to where "TEST" is stored but the actual copy still points to the original string i.e. "This is a string".

When you do:

strcpy(t, "Changed string");
or
t[0]='g';

You are modifying the content at the address where the original "This is a string" is stored,so the change is reflected in main() as well.
0
 
LVL 9

Expert Comment

by:ankuratvb
ID: 12155706
0
 
LVL 19

Author Comment

by:mrwad99
ID: 12155745
OK then, I have increased the points to 200 in the hope that you can now answer this related question:
#include <iostream>

using namespace std;

void Test(char* t)
{
      t+=5;
}

int main ()
{
    char* str = "This is a string";
      Test(str);
      cout << str << endl;
      return 0;
}

If I want to get the string str to be "is a string" I know I can use pointer arithmetic to adjust the offset in memory at which the string begins, but again this only adjusts t, not str.  What am I doing wrong here ?

TIA
0
 
LVL 84

Assisted Solution

by:ozo
ozo earned 50 total points
ID: 12155839
void Test(char* t)
{
memmove(t,t+5,strlen(t)-4);
}
0
 
LVL 84

Expert Comment

by:ozo
ID: 12155929
or
 
void Test(char** t){
      *t += 5;
}
Test(&str);

or

char *Test(char *t){
    return t+5;
}
str=Test(str);
0
 
LVL 23

Expert Comment

by:Mysidia
ID: 12156626
Since you are obviously using C++... just use a reference.

Btw...  
  char str[] = "This is a string";
is an error usually, that should be
  char const str[] = "This is a string";
as literals are constants.

str[] cannot be re-assigned either, you need to make it a real non-const pointer to
do that (the value it points to is still const)...
char *str;   or  char const* str;   are pointers that can be re-assigned to point to other
values.

To allow Test() to re-assign the pointer, pass it a reference to the char* pointer instead of
just the pointer itself.   Change of the integral value of a plain pointer passed will not be
carried up to the caller... hence:

#include <iostream>
using namespace std;
                                                                               
void Test(char const* & t)   /* Read from right to left.. a Reference
                                                 to a pointer to a read-only address holding a value of type char
                                                */
{
     t = "TEST";
}
                                                                               
int main ()
{
    char const *str = "This is a string";
     Test(str);
     cout << str << endl;
                                                                               
     return 0;
}
0
 
LVL 23

Expert Comment

by:Mysidia
ID: 12156641
Similarly...
void Test(const char* &t) {
       t += 5;
}

The problem you were having is just this situation, consider the function P

void P(int x) {
  x = x+1;
} /* This function doesn't accomplish anything */


void R(int &x) {
  x = x+1;
} /* This function takes a reference to an integer and increments the integer referred*/


int main() {
   int q = 0;
   P(q);
   cout << q << endl;
   R(q);
   cout << q << endl;
}

/* Of course the reslt is   0    end-line   1   end-line */
0
 
LVL 15

Expert Comment

by:efn
ID: 12156682
char str[] = "This is a string";

This is not an error.  str is not a pointer to the literal string, but a modifiable array initialized from the string.

 char* str = "This is a string";

This is legal, but deprecated in the standard and not a good idea, since the result of trying to modify what str addresses is undefined.  "const char *" or "char const *" would be better.

--efn
0
 
LVL 23

Expert Comment

by:Mysidia
ID: 12156700
>char str[] = "This is a string";
>
>This is not an error.  str is not a pointer to the literal string, but a modifiable array initialized from the string.

No, this declares a pointer to a string literal which is unmodifiable.
0
What Should I Do With This Threat Intelligence?

Are you wondering if you actually need threat intelligence? The answer is yes. We explain the basics for creating useful threat intelligence.

 
LVL 23

Expert Comment

by:Mysidia
ID: 12156729
To be more specific, there are various conventions with respect to string literals.
Attempting to write to them may have undefined behavior... assuming
char str[] = "blah";
gives you something writable will likely reduce portability of your C code.
0
 
LVL 15

Assisted Solution

by:efn
efn earned 50 total points
ID: 12156745
From the 1999 C standard, ISO/IEC 9899:1999, section 6.7.8, Example 8:

"The declaration

char s[] = "abc", t[3] = "abc";

defines &#8216;&#8216;plain&#8217;&#8217; char array objects s and t whose elements are initialized with character string literals.

This declaration is identical to

char s[] = { 'a', 'b', 'c', '\0' },
t[] = { 'a', 'b', 'c' };

The contents of the arrays are modifiable.

On the other hand, the declaration

char *p = "abc";

defines p with type &#8216;&#8216;pointer to char&#8217;&#8217; and initializes it to point to an object with type &#8216;&#8216;array of char&#8217;&#8217; with length 4 whose elements are initialized with a character string literal. If an attempt is made to use p to modify the contents of the array, the behavior is undefined."

A clarification:  when I wrote "deprecated in the standard," I was referring to the C++ standard.  The form without const is not deprecated in the C standard.
0
 
LVL 15

Expert Comment

by:efn
ID: 12156751
Sorry, all that &#8216 and &#8217 stuff is supposed to be quotation marks.
0
 
LVL 9

Expert Comment

by:ankuratvb
ID: 12157291
>No, this declares a pointer to a string literal which is unmodifiable.

Incorrect.efn is right about the array initialization.

mrwad99,

t+=5;

Note again what you have- a copy of the pointer,you are changing where this pointer points to ,but since this is a copy,the original still points to the original string.

Consider this:
char str[]="This is a string";

char *p,*copyofp;
p=&str[0];copyofp=&str[0];

Now if you do, copyofp+=5;

copyofp now points to "is a string" but p still points to the original "This is a string".

0
 
LVL 8

Expert Comment

by:ssnkumar
ID: 12157837
I think he is not able to appreciate the fact that, all the elements of array are passed to the function, instead only the address of the first element is passed.
If he understands this and also that C uses call-by-value, then he will definitely gets cleared!

-ssnkumar
0
 
LVL 19

Author Comment

by:mrwad99
ID: 12163345
Right sorry about the delay, we are obviously in different time zones and I have only just got back to *my code* to try out the suggestions.

Firstly, an appology.

People have posted several comments about references; I already knew all of this/the stuff about char** etc but wondered how to do it with pure C - i.e. no references.  When I test this code I use cout just because the majority of code I write is C++ hence I always have some C++ project set up in VC++ quick to hand.  However I knew this was related more to C hence asked it here.  Sorry again.

Why am I asking this ?  Well, I am writing something in C that needs to be able to modify strings read from a file.  Now since I am reading lines into a char[], I wanted to be able to pass each read line to a function that would strip a certain amount of characters from the front of the string, i.e. by adjusting the offset at which the pointer starts.  Now when I decided to write a function to do this, the trouble started :)

As correctly noted by ozo, I can do this _when str is a char*_  However when I do this passing str as a char[], it does not like it.

void Test(char** t)
{
      *t+=4;
}

int main ()
{
          char str[] = "This is a string";
      Test(str);
      return 0;
}
cannot convert parameter 1 from 'char [17]' to 'char ** '

Grrrrrrrrrrrrrr.  

Now I am thinking that I cannot do this in the way I want to.  Is that correct ?

(Points increased to 300 as there has been a lot of useful info here).
0
 
LVL 23

Accepted Solution

by:
Mysidia earned 100 total points
ID: 12165090
Try:

void Test(char** t)
{
     *t+=4;
}

int main ()
{
      char str[] = "This is a string", *p = str;
     Test(&p);   /* Address of p... this is the C way to do it
                           Note that you can't modify the base address of char str[]="Blah";
                            Test(&str) would result in an invalid program and
                             therefore undefined behavior */

     printf("%s\n", p);
     return 0;
}

I can't emphasize this enough.
char str[]  declares it in such a way that str is only an rvalue and not an lvalue, and in
particular that it is not a pointer that you can re-assign.  This is a separate issue from
the target space being writable or not.

As for ISO 1999 C; I don't believe there are many compilers out there
that claim to fully support those new extensions, C89 is still much more widely used.

Note that...

int main()
{
   char str[] = "This is a string";

   str = "blah blah blah";
   
}

is invalid.
BUT

int main()
{
    char *str = "This is a string";

    str = "blah blah blah";
}

is quite ok.
0
 
LVL 8

Assisted Solution

by:ssnkumar
ssnkumar earned 50 total points
ID: 12166342
Hi,

    Look at the code below. It does work as you have explained.
    Note that it doesn't use pointers.
     
-ssnkumar

void Test(char str[], int count)
{
        char temp[20];

        memset(temp, 0, sizeof(str));
        strcpy(temp, (str + count));
        memset(str, 0, sizeof(str));
        strcpy(str, temp);
}

main()
{
        char str[] = "This is a String";

        printf("Old String is => \"%s\"\n", str);

        Test(str, 4);

        printf("Old String is => \"%s\"\n", str);
}
0
 
LVL 9

Expert Comment

by:ankuratvb
ID: 12166529
Please understand that the equivalence of arrays and pointers exist only when you pass arrays as arguments to a function.

Otherwise,the two are distinct concepts.

>>
?  Well, I am writing something in C that needs to be able to modify strings read from a file.  Now since I am reading lines into a char[], I wanted to be able to pass each read line to a function that would strip a certain amount of characters from the front of the string, i.e. by adjusting the offset at which the pointer starts.  Now when I decided to write a function to do this, the trouble started :)
<<

If you want to do:

str="is a string";
in the function,

whats the problem with

strcpy(str,"is a string");


Also,i'd recommend you go through the C FAQ regarding the concept of pointers and equivalence of pointers and arrays.

http://www.eskimo.com/~scs/C-faq/s6.html
0
 
LVL 19

Author Comment

by:mrwad99
ID: 12167943
Thanks all.

>> whats the problem with

>> strcpy(str,"is a string");

Sometimes I have a string that maybe "This is a string" othertimes I may have "This is another string".

Say I want to be able to reduce both to "string" - I know that in the first case I have to offset the pointer by 9 chars; in the second 16. This offset varies, so I cannot simply do a strcpy without first adjusting pointer offsets etc...but that would just be overkill when a simple offset adjustment works.

Anyway I will try the suggestions later on GMT and get back then.

Thanks again.
0
 
LVL 8

Expert Comment

by:ssnkumar
ID: 12168007
Hi mrwad99,

   Did you try the code that I posted?

-ssnkumar
0
 
LVL 19

Author Comment

by:mrwad99
ID: 12168604
Hi ssnkumar,

Unfortunately where I am at the moment I have no access to a compiler, which is why I often post in questions but do not give specific feedback to code suggestions on my questions.

When I get home this evening I will try your code and post back ASAP.

:)
0
 
LVL 19

Author Comment

by:mrwad99
ID: 12173356
Right then people.

Well, you folks certainly know your stuff, and I am humbled to be in your company.  I have tried all the code and it all works, many thanks.

I will post more tomorrow and close this question then as I am in a rush now but I just wanted to acknowledge all the effort.

:)
0
 
LVL 19

Author Comment

by:mrwad99
ID: 12200773
Right, I split the points amongst everyone who responded there, as useful information was given by all.

Many thanks :)
0

Featured Post

How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

This tutorial is posted by Aaron Wojnowski, administrator at SDKExpert.net.  To view more iPhone tutorials, visit www.sdkexpert.net. This is a very simple tutorial on finding the user's current location easily. In this tutorial, you will learn ho…
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 structures 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.

708 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

16 Experts available now in Live!

Get 1:1 Help Now