Go Premium for a chance to win a PS4. Enter to Win

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 376
  • Last Modified:

Modify a char[] when passed to a function

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
mrwad99
Asked:
mrwad99
  • 8
  • 6
  • 5
  • +3
5 Solutions
 
ankuratvbCommented:
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
 
mrwad99Author Commented:
#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
 
ankuratvbCommented:
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
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
mrwad99Author Commented:
>> 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
 
ankuratvbCommented:
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
 
mrwad99Author Commented:
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
 
ozoCommented:
void Test(char* t)
{
memmove(t,t+5,strlen(t)-4);
}
0
 
ozoCommented:
or
 
void Test(char** t){
      *t += 5;
}
Test(&str);

or

char *Test(char *t){
    return t+5;
}
str=Test(str);
0
 
MysidiaCommented:
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
 
MysidiaCommented:
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
 
efnCommented:
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
 
MysidiaCommented:
>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
 
MysidiaCommented:
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
 
efnCommented:
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
 
efnCommented:
Sorry, all that &#8216 and &#8217 stuff is supposed to be quotation marks.
0
 
ankuratvbCommented:
>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
 
ssnkumarCommented:
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
 
mrwad99Author Commented:
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
 
MysidiaCommented:
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
 
ssnkumarCommented:
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
 
ankuratvbCommented:
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
 
mrwad99Author Commented:
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
 
ssnkumarCommented:
Hi mrwad99,

   Did you try the code that I posted?

-ssnkumar
0
 
mrwad99Author Commented:
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
 
mrwad99Author Commented:
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
 
mrwad99Author Commented:
Right, I split the points amongst everyone who responded there, as useful information was given by all.

Many thanks :)
0

Featured Post

Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

  • 8
  • 6
  • 5
  • +3
Tackle projects and never again get stuck behind a technical roadblock.
Join Now