views:

1012

answers:

6

After reading the faq's and everything else I can find, I'm still confused. If I have a char pointer that is initialised in this fashion:

char *s = "Hello world!"

The string is in read-only memory and I cannot change it like this:

*s = 'W';

to make "Wello world!". This I understand, but I can't, for the life of me, understand how to make it NOT read-only. Do I have to use an array instead of a pointer? Like here?

This is my code:

char *s = str;
char *e = s;
while (*e != '\0')
e++;
e--;
char *temp;
//Swop the string around
while (s <= e) {
    *temp = *s;
    *s = *e;
    *e = *temp;
    e--;
    s++;
}

The error message is just a segmentation fault. Apologies in advance if this is a really stupid question.

Thanks so much for all the help. After taking all your advice, I get this:

void something(char * str) {
    char *store = str;
    char *s = new char[strlen(str) + 1]; //Allocate memory. Nice one.
    strcpy(s, str);
    char *e = new char[strlen(str) + 1];
    strcpy(e, str);
    while (*e != '\0')
     e++;
    e--;
    char temp; //no longer a pointer
    while (s <= e) {
     cout << *e;
     temp = *s;
     *s = *e;
     *e = temp;
     e--;
     s++;

    }
    delete [] e;
    delete [] s;        
}

however, the deletes at the end of the function seem to be causing their own segmentation faults. Why?

For interest's sake: The faults were due to accessing the e and s pointers after they were incremented. A much simpler solution followed from that:

void something(char * str) {
    char *s = new char[strlen(str) + 1];
    strcpy(s, str);
    char temp;
    int j = strlen(str) - 1;
    for (int i = 0; i <= strlen(str)/2; i++) {
     cout << s << endl;
     temp = s[i];
     s[i] = s[j];
     s[j] = temp;
     j--;
    }
    delete [] s;
}
+5  A: 

The easiest way to modify it is to create an array for your storage, and then copy the string into it.

For example:

char buf[128];
const char *src = "Hello World";
strncpy(buf, src, 127); // one less - we always 0-terminate
buf[127] = '\0';

// you can now modify buf
buf[0] = 'W';

The reason your code doesn't work is that you haven't allocated any memory for the copy of the string - you've just made a second pointer to the same read-only memory. (And then tried to copy it? I'm not quite sure what the rest of the code is doing.) You need to get some non-read-only memory somewhere, and it's much easier to use the standard library to copy it into that new memory, rather than writing the loop yourself.

In the case when you don't know the length of the string beforehand, you can also use malloc (or, even better, do what drschnz's answer says and use new char[]):

const char *src = "Hello world";
char *buf = malloc(strlen(src) + 1);   // or = new char[strlen(src) + 1];
strcpy(buf, src);
// you can now modify buf
// later, you need to free it
free(buf);                             // or delete [] buf;

Also, if you're using C++, you can just use a std::string:

std::string myString("Hello world");
myString[0] = "W";

Hope that helps.

Jesse Rusak
`char *buf[128];` gives an array of 128 pointers, not an array of 128 characters, in your first example block. Remove the asterisk and you're good.
Head Geek
There's a typo in your example: "char *buf[128];" should be "char buf[128];" (ie you've got an array of pointers to char's, instead of an array of char's)
therefromhere
you beat me by 1 second ><
therefromhere
Ah, good catch. I'll fix it, thanks.
Jesse Rusak
Thanks a bunch... This worked. But now the delete statements themselves are causing a seg fault. Do you maybe know why?
pypmannetjies
Yes - the problem is that you can't move s and e around (s++, e++, etc) and then delete them. You have to delete the original pointers returned by new.
Jesse Rusak
Also, you can't compare s to e directly with <=, because they don't point into the same array. It would probably be easier overall to use indexes into s and e, rather than moving them around.
Jesse Rusak
@jder, I agree. Indexes would probably simplify this code quite a bit (for a newbie to read, anyway). Better variable names would, too. ;P
strager
Thanks for all the help.
pypmannetjies
Be aware that strncpy does not terminate your string with a zero if the source string has 128 or more characters!
jn_
As an aside .... I'm going to add that "Arrays are evil". You already had a weird crash because of them. new[] and delete[] are on the list of Things-You-Shouldn't-Be-Using-in-C++, Use std::vector and std::string instead. See: http://www.parashift.com/c++-faq-lite/containers.html for details.
Jimmy J
@jn right - I always forget that. Good old c. I'll fix it inline
Jesse Rusak
+6  A: 

Try:

char src[] = "Hello world";
src[6]     = 'W';

-- // or

char   buffer[] = "Hello world";
char*  src      = buffer;
src[6]          = 'W';

If you want to copy a string into a buffer then use strcpy() or strncpy()

char   buffer[20];
char const* s = "Hello World"

strcpy(s,buffer);

If you must write your own string copy then it should look like this:

char   buffer[20];
char const* s = "Hello World";

// OK this is not the perfect solution but it is easy to read.
for(int loop = 0;s[loop] != '\0';++loop)
{
    buffer[loop] = s[loop];
}
buffer[loop] = '\0';
Martin York
Remo.D
char const
strager
The question was originally tagged C++. Someone changed it.
pypmannetjies
Martin York
@strager: I like 'loop'. It makes it easy to search for in the source. Have you ever tried to find all the occurrence of the variable 'i'! The use of identifiers like 'i' is a hangover from the days when you only had 8 characters for an identifier. I recommend you have something more descriptive.
Martin York
@pypmannetjies: I re-tagged it as C. There is no C++ knowledge required for this subject.
Martin York
+2  A: 

The pointer is not read-only. (the string data itself is, but a pointer pointing to it can be modified freely) However, assigning a character to a pointer doesn't do what you expect.

In general, the only thing you can assign to a point is an address. You can't assign values, only the address of values.

String literals (like "hello world") are the one exception, because strings are special. If you assign one of those to a pointer, you get a pointer to that string. But in general, you assign addresses to pointers.

The other point is that characters in C++ are integral datatypes. They can be treated as integers with no casting required. I can do int i = 'W' and the compiler won't complain.

So what happens if you assign 'W' to a pointer? It takes 'W' as an integer value, and assumes that this is an address. 'W' has the ASCII value 127, so you are effectively setting your pointer to point to address 127, which doesn't make sense.

I don't see how that has much to do with your code though. The problem there seems to be that temp doesn't point to valid data. You declare a pointer, which points to some undefined address. And then you say "wherever it points to, I want to write the value that s points to. The following should work somewhat better:

char temp; // not a pointer. We want a character to store our temporary value in
while (s <= e) {
    temp = *s; // note, no * on temp.
    *s = *e;
    *e = temp; // note, no * on temp.
    e--;
    s++;
}

However, if str points to a string literal, like "hello world", then this won't be legal, because the string data itself is read-only. The compiler may not enforce it, but then you've ventured into undefined-behavior land. If you want to modify the string, copy it into a local buffer, as one of the other answers showed.

You seem a bit confused about the semantics of pointers. Assigning an address (or something that can be converted to an address, like an integer) to a pointer makes the pointer point to that address. It doesn't modify the pointed-to data. Declaring a pointer doesn't mean that it will point to anything meaningful. If you want to store a char, declare a char variable. A pointer doesn't store data, it just points to data allocated elsewhere.

edit Comments and fixes to your updated code:

void something(const char * str) { // let the function take a pointer to a non-modifiable string, so add the const. Now it's clear that we're not allowed to modify the string itself, so we have to make a copy.
    char *s = new char[strlen(str) + 1]; // Since the original string is const, we have to allocate a copy if we want to modify it - in C, you'd use malloc(strlen(str)) instead
    strcpy(s, str);
    char *e = s; // make e point to the start of the copied string (don't allocate two copies, since e and s are supposed to work on the same string
    while (*e != '\0') { // add braces so it's clear where the loop starts and ends.
        e++;
    }
    e--;

    while (s <= e) { // the loop condition wouldn't work if s and e pointed to separate copies of the string
        cout << *e; // why? I thought you just wanted to reverse the string in memory. Alternatively, if you just want to print out the string reversed, you don't need to do most of the rest of the loop body. In C, you'd use printf instead of *e
        char temp = *s; // might as well declare the temp variable when you need it, and not before
        *s = *e;
        *e = temp;
        e--;
        s++;

    }
}

Just for reference, and in response to the comments about C vs C++, here's how I'd write a function to reverse a string in C++:

std::string revert_string(const std::string& str) {
  return std::string(str.rbegin(), str.rend());
}

Or to revert the string in-place:

std::string revert_string(const std::string& str) {
  std::reverse(str.begin(), str.end());
}
jalf
I AM confused :) I am just starting with C++, but I did try changing temp earlier, and that did not solve the problem, so I assumed it was correct. But thank you, it seems to be working now.
pypmannetjies
Terminology here seems to be confused. Strings are not special when pointed to. It's just one of the things you can grab the pointer of at run time. The compiler cannot convert int to int* without a (nasty) cast. (You should be using ptrdiff_t or whatever if you do do that.)
strager
Sorry, I didn't mean strings were special, but simply that string literals are treated somewhat differently, and so they're the only thing that look like a value, and can be assigned to a pointer. With everything else, you explicitly need to take the address of the value.
jalf
@jalf, Not functions. ;P I think the OP wants a C solution (he doesn't seem to be dealing with C++), so replace new with delete. Also, you need to free your temporary with delete[] (or free). You can also cache strlen() and do e = e + length - 1; in place of the first loop.
strager
I tried to stick as closely as possible to the code in his question. (which uses new, and doesn't delete. I assume he has a delete further down). I didn't cache strlen for the same reason.
jalf
On new/delete, read the OP's edit: "After taking all your advice, I get this." He probably grabbed the new/delete from one of the answers here. I really suggest going with malloc/free. Another note: the cout line was also added in an answer.
strager
I agree, but until the OP clarifies any language requirements, I want to make as few changes as possible. I've added comments pointing out what the C equivalents of new and cout are
jalf
Also keep in mind that the original question was tagged with C++, not C. (That might just be because the OP didn't make the distinction though)
jalf
It's C++, sorry if it appears as C. Is it the c-style strings? I am readily admitting my noobness here :)
pypmannetjies
Yeah, you don't often see C-style strings in C++. Also the C++ standard library would let you do the entire function as a one-liner. But when people are learning, and so reimplement functionality instead of using the standard library, it can be hard to tell them apart.
jalf
I added a few "real-world" C++ examples at the bottom of my answer. I assume you want to stick with the basics, but it does show a big difference between C and C++. In C++, you don't often need to fiddle with pointers, or even write explicit loops. So code with lots of those often looks very C-like.
jalf
+1  A: 

Technically, what you have is more correctly written like that:

const char *s = "Hello world!"

What you actually want to have is something like that:

char s[] = "Hello world!"

Following few lines may help you understanding more:

const char *p = "Hello World";
char q[] = "Hello World";
printf("%d %d", sizeof(p), sizeof(q));
// p[0] = 'W' // INVALID
q[0] = 'W'; // valid
Thanks, but I know this. I need to know if there's another way than using an array.
pypmannetjies
What's the difference between a pointer, a string, and an array?
jleedev
@jleedev, A pointer points to something. A string is an array of characters. An array (the data type) is an implicit pointer (does not *exactly* behave like a pointer) to an array of data.
strager
A: 

Your deletes generate faults, because you changed the pointer. You should save the original location of the new, and delete[] it. You try to delete a location that isn't in the allocation table. If you want to change the pointer value, make another char *temp = t; and use it to iterate over the string.

SurDin
A: 

There#s a function "strdup()" to make a copy of a string...it makes sure you don't forget the "+1" in your malloc.

char* source = "Hello World";
char* dest = strdup(source);
Jimmy J
Not standard C (but is POSIX). However, I think the OP is looking for a concept solution and not a real-world solution. He's trying to learn why something acts as it does.
strager
Ok, it's posix. Still, it's the sort of thing you should write yourself if it's not available. Writing out the code manually every time isn't good.
Jimmy J