views:

260

answers:

6
// The first example:
char text[] = "henri";
char *p;
p = text;
*(p + 1) = 'E'; // Output = hEnri
// Now If we want to remove the "e" ie hnri, we would go for?????
     *(p + 1)=????? 

The obvious answer is to copy the rest of the array "back" one position. But this seems... unpleasant. Surely there is some better way?

+1  A: 

You'd have to move all the remaining characters up one. There's no way to say "skip this char".

while (*p)
{
    *p = *p++;
}

edit: ugh, how did I make that mistake? Now fixed. p will still be non-null at the end of the string, but *p will not.

Vicky
Two bugs here: 1. `while(p)` will continue looping until p is NULL, you want `while(*p)` 2. Even then, you need to copy the ending `'\0'`
BlueRaja - Danny Pflughoeft
@BlueRaja: the *p was a complete brainfade, thanks. But you don't need to copy the terminating 0, that will be done by the last iteration of the loop.
Vicky
Doesn't `*p=*p++` cause undefined behaviour because you modify `p` and read it (not just to determine the value to store) without an intervening sequence point? Moreover I can't see a way of ordering the modifications that would do the right thing (it definitely doesn't work with `gcc`). If you change it to `*p=*(p+1);++p;` it does work, though.
Mike Dinsdale
@Mike Dinsdale: I thought the assignment was a sequence point, so it would read, modify, increment. If it doesn't work in gcc I must be wrong though - I didn't actually try it.
Vicky
@Vicky: according to the answers to [this question](http://stackoverflow.com/questions/1860461/why-is-i-i-1-unspecified-behavior) the assigment isn't a sequence point for non-user-defined types. I'm not sure it would help even if it was, though, because even though the increment would then be guaranteed to happen before the assignment, it's undefined whether it happens before `*p` (the first arg of `=`) is evaluated (if we can believe [Wikipedia](http://en.wikipedia.org/wiki/Sequence_point) ;))
Mike Dinsdale
and even if it did turn out to be defined, could it really work? For example, if it was equivalent to `char q=*p;p++;*p=q` it would overwrite memory indefinitely with the first letter in the string, or if it was equivalent to `char q=*p;*p=q;p++` it won't do anything...
Mike Dinsdale
+2  A: 
*++p = 'h';
puts(p);
John Kugelman
Well in that case, why not `p = "hnri"; puts( p );``
anon
Because I'm pretty sure that's not the overly-clever answer the interviewer is looking for. -shrug-
John Kugelman
@Neil: Because that won't work if we try to print `text` :)
BlueRaja - Danny Pflughoeft
A: 

move the characters down by 1 after it (including the null at the end)

you'll end up with two nulls at the end, by the way.

glowcoder
A: 

This is not copying the array. I'm moving the data within the same array.

for (p = text + 1; *p != '\0'; ++p) {
  *p = *p + 1;
}

A hackier solution is to put the 'h' where the 'e' was, and return a pointer to the new beginning.

*p + 1 = *p;
p = p + 1;
Adrian McCarthy
+4  A: 

Copying is the best solution and really the only solution when you're removing elements from an array. (You can special case the element at the end of the array, but that's it.) I don't understand why it's so terrible.

You need to do one of two things. Both involve some copying.

1) Copy the 'n' to where the 'E' is, the 'r' to where the 'n' is, the 'i' to where the 'r' is, and then null terminate it.

2) Copy the 'h' to where the 'E' is and then always use the pointer to the 'h's new location. This may not always be an option.

Evan Shaw
You may as well copy pointers and save a bit of cycles if the string is very long and allocated on the heap. moving pointers is more efficient (like the linked list for example)
the_drow
A: 

This should do it:

memmove(p+1, p+2, 4);

We move four characters because we want to copy the null terminator as well.

fsmc