There is pointer to string let say: char *p = "abcdef"
I want to delete some of the chars.
Let say every second char, so i want to get *p="ace"
my algorithm is something like:
int i=1,j=1
for(;p != '\0';p++,i++)
if (i % 2 ==0)
*(p - j++)= *p
*(p-j)='\0'
This algorithm is find the every second char of course but not matter how I try to write the "delete" process or there are compilation errors or the string is unchanged.
I start to believe there is no way to solve that issue without any malloc help.
Again i need to do it on O(n) without any other STRINGS arrays.
p != '\0' should be *p != '\0'(or *p != 0 or *p).
i should be initialized to 0. (You are keeping the wrong ones.)
j should be initialized to 0. (Off by one error.)
j needs to be incremented each time p is incremented.
Your code would be more readable and less fragile if you avoided offsets relative to an unrelated pointer.
void filter_inplace(char* src) {
char* dst = src;
for (size_t i=0; src[i]; ++i) {
if (i % 2 == 0)
*(dst++) = src[i];
}
*dst = 0;
}
Alternative:
void filter_inplace(char* src) {
char* dst = src;
while (1) {
if (!*src) break;
*(dst++) = *(src++);
if (!*src) break;
src++;
}
*dst = 0;
}
Of course, you can't do the following because p points to read-only memory:
char* p = "abcdef"; # XXX Should be "const char*".
filter_inplace(p); # XXX Overwrites read-only memory.
You could do the following:
char p[] = "abcdef";
filter_inplace(p);
You could do the following:
char* p = strdup("abcdef");
filter_inplace(p);
free(p);
I start to believe there is no way to solve that issue without any malloc help. Again i need to do it on O(n) without any other STRINGS arrays.
Kind of true. At least it is true that it can't be done without another string. Even if you used malloc (or strdup) for getting memory I would still consider it another string.
So as long a you initialize the char pointer like:
char *p = "abcdef";
it can not be done. You can't change any character in the string "abcdef".
If the above code was changed to
char p[] = "abcdef";
you would be able to do what you a trying. But even this could be considered as using another string as you have both the initializer string and char array.
Related
EDIT: I think I've understood how this concept works, this is my code
void delete_duplicate(char* str) {
if (str == NULL) {
exit(1);
}
for (size_t i = 0; str[i] != 0; i++) {
if (str[i] == str[i + 1]) {
str[i] = '\0';
}
}
}
int main(void) {
char str[] = "hhhhhhhheeeeeeeeyyyyyyyyyyy";
delete_duplicate(str);
return 0;
}
the output string is "00...0h0...000e0...000y" (with lots of zeros). if the string is "abbbb", the string becomes "a" and not "ab".
I was thinking about an algorithm for this exercise:
given a string, for example "ssttringg" the output must be "string". the function has to remove one character if and only if the current character and the next character are the same.
the exercise expects a function declared like this:
extern void delete_consecutive(char* str);
this is my algorithm:
loop: if the current character is equal to the next character, increment length by 1, and repeat until zero terminator is reached.
critical part: dynamic memory allocation.
allocate enough memory to store the output. Why did I say this is the critical part? because I have to do it in a void function, therefore I can't create a second string, allocate enough memory with malloc, and then return it at the end. I need to edit the original string instead. I think I can do it by means of a realloc, and by decrementing the size of the string. But I don't know how to do it, and more important: I don't know if it's legit to do it, without losing data. I think I'm overcomplicating the homework, but it's a void function, therefore I can't simply create a new string and copying characters in it, because I can't return it.
if it's not clear, I'll edit the question.
This pattern is called a retention scan (also a reduction scan, depending on your perspective), and is very common in algorithms that dictate discarding characters whilst keeping others, based on some condition. Often, the condition can change, and sometimes even the methods for starting the scan are somewhat altered
In it's simplest form, it looks something like this, as an example: An algorithm used to discard all but numeric (digit) characters:
Start with reader r and writer w pointers at the head of the string.
Iterate over the string from beginning to terminator using r. The r pointer will always be incremented exactly once per iteration.
For each iteration, check to see if the current character at r satisfies the condition for retention (in this case, is the character a digit char?). If it does, write it at w and advance w one slot.
When finished. w will point to the location where your terminator should reside.
#include <ctype.h>
void delete_nondigits(char *s)
{
if (s && *s)
{
char *w = s;
for (char *r = s; *r; ++r)
{
if (isdigit((unsigned char)*r))
*w++ = *r;
}
*w = 0;
}
}
Pretty simple.
Now, the algorithm for in-place consecutive-run compaction is more complicated, but has a highly similar model. Because retention is based on a prior-value already-retained you need to remember that last-kept value. You could just use *w, but the algorithm is actually easier to understand (and advance w) if you keep it in a separate memo char as you're about to see:
Start with reader r and writer w pointers as we had before, but starting at second slot of the string, and a single memo char variable c initialized to the first character of the string.
Iterate over the string using r until termination encounter. The r pointer will always be incremented exactly once per iteration.
For each iteration, check to see if the current character at *r is the same as the memo character c If it is, do nothing and continue to the next iteration.
Otherwise, if the character at *r is different than the memo character c, save it as the new value for c, write it to *w, and advance w one slot.
When finished, terminate the string by setting a terminator at *w.
The only hitch to this algorithm is supporting a zero-length string on inception, which is easily circumvented by checking that single condition first. I leave actually implementing it as an exercise for you. (hint: only do the above if (s && *s) is true).
You can modify the string passed as a parameter if it is modifiable.
Example:
void deleteFirstChar(char *str)
{
if(str && *str)
{
memmove(str, str + 1, strlen(str));
}
}
//illegal call
//string literals cannot be changed
void foo(void)
{
char *str = "Hello";
deleteFirstChar(str);
}
//legal call
void bar(void)
{
char str[] = "Hello";
deleteFirstChar(str);
}
This isn't an answer, but consider this code:
char str[] = "strunqg";
printf("before: %s\n", str);
modifystring(str);
printf("after: %s\n", str);
where the "modifystring" function looks like this:
void modifystring(char *p)
{
p[3] = 'i';
p[5] = p[6];
p[6] = '\0';
}
This is totally "legit". It would not work, however, to call
char *str = "strunqg";
modifystring(str); /* WRONG */
or
modifystring("strunqg"); /* WRONG */
Either of these second two would attempt to modify a string literal, and that's not copacetic.
void delete_duplicate(char* str)
{
if(str && *str)
{
char *writeTo = str, *readFrom = str;
char prev = 0;
while(*readFrom)
{
if(prev != *readFrom)
{
prev = *readFrom++;
*writeTo++ = prev;
}
else
{
readFrom++;
}
}
*writeTo = 0;
}
}
int main(void)
{
char str[] = "hhhhhhhheeeeeeeeyyyyyyyyyyy";
delete_duplicate(str);
printf("`%s`\n", str);
return 0;
}
I have to do an exercise and I have this structure given:
typedef struct {
char *str;
unsigned int len;
} String;
My Task is to write a String Concat which concats "Kartoffel" and "puffer" to "Kartoffelpuffer" (potato fritter).
String concat(String l, String r)
Both Strings l and r should not be changed after running the function.
First I created the two Strings in the main:
String1 *l = malloc(sizeof(String1));
String1 *r = malloc(sizeof(String1));
(*l).str = malloc(sizeof("Kartoffel"));
(*r).str = malloc(sizeof("puffer"));
(*l).str = "Kartoffel";
(*r).str = "puffer";
(*l).len = 9;
(*r).len = 6;
Then I wrote the concat function:
String1 concat(String1 l, String1 r) {
unsigned int i = 0;
String1 *newStr = malloc(sizeof(String1));
/* +1 for '\0' at the end */
newStr->str = malloc(l.len + r.len + 1);
newStr->str = l.str;
/* The following line is not working */
newStr->str[l.len] = *r.str;
newStr->len = l.len + r.len;
return *newStr;
}
What Im trying to do is working with pointer arithmetic.
When there is a pointer which points to the beginning of a storage area like char *str, it should be possible to move the pointer with a[b] or *((a) + (b)) right? When I run the code I get Segmentation fault (I hope its the right translation. Original: "Speicherzugriffsfehler").
If someone could give me a hint I would be thankful. PS: Sorry for my English.
First, (*l).str = "Kartoffel"; makes (*l).str point to the "Kartoffel" string literal, meaning that the original memory allocated to (*l).str with malloc() is lost. Same for (*r).str = "puffer";. One of the things you can do to avoid this is copy the string into the allocated buffer by looping over the characters in a for loop (since you can't use string.h).
Then, in your concat() function, you do the same thing. You allocate the memory for newStr->str with malloc() (properly allocating an extra char for the null-terminator), but on the next line you re-assign that pointer to point to l.str, which is still pointing to the string literal. Then, with newStr->str[l.len] = *r.str; you are attempting to modify the string literal, which in C is undefined behavior.
The way to fix this could be, again, to copy the two strings into the buffer allocated with newStr->str = malloc(l.len+r.len+1);.
After allocating memeory to newStr and newStr->str
Two pointers could be used. char *to, *from;
Set the pointers with to = newStr->str; and from = l.str;
copy the characters with *to = *from;
Advance the pointers with to++; and from++;
Repeat until *from == 0
Set from with from = r.str;
to does not need to be reset as it is correctly positioned at the end of newStr->str.
Repeat the copy of characters.
Repeat advancing the pointers.
Set a terminating 0 with *to = 0;
Thank you very much for your help!
I wrote another method to copy the string as you guys said.
char * copyStr (char * dest,char * src){
unsigned int index;
for (index = 0; src[index] != '\0'; index++) {
dest[index] = src[index];
}
dest[index] = '\0';
return dest;
}
And I edited my concat like that:
String1 concat (String1 l, String1 r){
String1 *newStr = malloc(sizeof(String1));
newStr->str = malloc(l.len+r.len+1);
copyStr(newStr->str,l.str);
copyStr((newStr->str+l.len),r.str);
newStr->len = l.len+r.len;
return *newStr;
}
with newStr->str+l.len the pointer will be moved. If l.len is 9, the pointer will point to the 10th byte, which is the end of the first string l. So the the String r will be copied in the memory storage behind the first string l.
Let's say I have a char *str and I want to assign it characters one by time using using pointers and incrementing ?
I've done :
char *str;
char c = 'a';
*str++ = c;
But it doesn't work.
How can I do that ?
str is just a pointer. It doesn't point anywhere valid (especially not to some memory you could write to). A simple possibility would be to have it point to an array:
char buf[1024] = {0}; // room for 1024 chars (or 1023 + a 0 byte for a string)
char *str = buf;
char c = 'a';
*str++ = c;
char *str is a pointer to a char (or an array of chars), however, you never assigned it. As has been mentioned earlier a char * basically says "go there" but there is no there there, you never gave it a value. You first need to use malloc to create space to put things in. Here's an example
char *str = malloc(sizeof(char)*10) //allocate space for 10 chars
char c = 'a';
str[0] = c;
no error check was made to malloc which you should do in your own program. You can also do it as such
char str[10];
char c = 'a';
str[0] = c;
however with this method you will be restricted to 10 chars and you cannot change that amount, with the previous method you can use realloc to get more or less space in your array.
But it doesn't work.
char* str;
... is not initialized to anything, therefore dereferencing it is to undefined behaviour. If it where initialized, then in expression *str++ = c; str++ is a post-increment operator, which returns a copy of the pointer whilst incrementing the original. The effect is that the copy points to the previous, and therefore what is pointed to by the previous pointer is assigned c.
To which part that doesn't work are you referring?
EDIT:
As mentioned in one of the comments, a copy is not really returned but the value is increment in place after having been evaluated.
As a variable with automatic storage duration the pointer str has indeterminate value. If even it had the static storage duration its value would be NULL. So you may not use such a pointer to store data.
What you mean can look for example the following way
#include <stdio.h>
int main( void )
{
char s[11];
char *p = s;
while (p != s + sizeof( s ) / sizeof( *s ) - 1 ) *p++ = 'a';
*p = '\0';
puts(s);
return 0;
}
The program output is
aaaaaaaaaa
Here in the program the pointer p of the type char * is initialized by the address of the first character of the array s.
Thus this statement used in the loop
*p++ = 'a';
fills sequentially the array with the character 'a'.
The next example is more interesting
#include <stdio.h>
char * copy_string(char *dsn, const char *src)
{
for (char *p = dsn; (*p++ = *src++) != '\0'; )
{
// empty body
}
return dsn;
}
int main( void )
{
char *src = "Hi QBl";
char dsn[7];
puts(copy_string(dsn, src));
return 0;
}
The program output is
Hi QBl
Here is a demonstration of a function that copies one character array containing a string into another character array using pointers.
I know that this strcpy function below is incorrect, but I cannot seem to figure out the one or two things I need to change to fix it. Any help would be greatly appreciated as I am completely stuck on this:
void strcpy(char *destString, char *sourceString)
{
char *marker, *t;
for(marker = sourceString, t = destString; *marker; marker++, t++)
*t = *marker;
}
Well it depends on your environment..
For example I see a few things I don't like:
You do not check for input parameters to be != NULL. This will cause a *0 access
I see you are not terminating your string with the '\0' character (or 0).. So, after the loop (please intent.) add *t = 0;
strcpy() is a predefined function and you are trying to create your own strcpy function. so, when you compile your program, you are getting conflicting types error. So, first rename your function name.
If you want to implement your own strcpy(), then i would suggest to implement strncpy(). It will copy at-most n-1 bytes from source null-terminated character array to destination character array and also add null character at the end of the destination character array.
void strcpy(char *dest, const char *src, size_t n)
{
if ((dest == NULL) || (src == NULL))
return;
int i;
for(i=0; i<(n-1) && src[i]; i++)
dest[i] = src[i];
dest[i]='\0';
}
It wouldn't let buffer overflow.
Note - My implementation is different from standard library strncpy() implementation. The standard library function strncpy() copies at most n bytes of src. If there is no null byte among the first n bytes of src, the string placed in dest will not be null-terminated.
I know that this strcpy function below is incorrect, but I cannot seem to figure out the one or two things I need to change to fix it.
You only need to add null character at the end of destination array.
void strcpy(char *destString, char *sourceString)
{
char *marker, *t;
for(marker = sourceString, t = destString; *marker; marker++, t++)
*t = *marker;
*t='\0';
}
This is a very simple aproach:
void copy(char * src, char * dst){
while(*src != '\0'){
*dst = *src;
src++;
dst++;
}
*dst = '\0';
}
int main(int argc, char** argv){
char src [] = "hello";
char dst [] = "----";
copy(src, dst);
printf("src: %s\n", src);
printf("dst: %s\n", dst );
}
It's more or less like wildplasser comment. First you iterate over the src pointer. In c, if you have '\0' (in a well formed string) then you can exit because it is the final character. Ok, you iterate over the src pointer and assign the value of src (*src) to the value of dst (*dst) and then you only have to increase both pointers...
It's all
A very easy strcpy function would be:
int strcpy(char *dest,char *source)
{
if (source==NULL)
{
printf("The source pointer is NULL");
return 0;
}
if (dest==NULL)
{
dest=(char*)malloc((strlen(source)+1)*sizeof(char));
}
int i;
for (i=0;source[i]!='\0';i++)
{
dest[i]=source[i];
}
dest[i]='\0';
return 1;
}
You should have no problem copying strings this way. Always use indexes instead of pointer operations, it's easier imo.
If you use an IDE, you should learn to use the debug function to discover the errors and problems, usually when you deal with strings one of the most common RUNTIME problems is the lack of a '\0', which automatically make your string functions go to memory zones where they shouldn`t be.
What I want to do is to iterate through the quote till the end of the quote/(*quote has nothing in it). Is my code valid?
char *quote = "To be or not to be, that is the question.";
for (quote = 0; *quote != NULL; quote++){
*quote = tolower(*quote);
}
You probably need another pointer to traverse the array, otherwise access to your original string will be lost.
And preferably only use NULL for pointers.
Don't use 0 as the initial value, unless you want to use indices instead (see below).
Doing char *quote = will simply make quote point to the read-only literal, instead of copying the string. Use char quote[] = instead.
char quote[] = "To be or not to be, that is the question.";
char *quotePtr;
for (quotePtr = quote; *quotePtr != '\0'; quotePtr++){
*quotePtr = tolower(*quotePtr);
}
Test.
Using indices:
char quote[] = "To be or not to be, that is the question.";
int i;
for (i = 0; quote[i] != '\0'; i++){
quote[i] = tolower(quote[i]);
}
Test.
Consider this as an expansion to the answer given by Dukeling
When you use
char *quote = "Hello World";
This makes a read-only string, means that you can't change its contents in a simpler way.
Here *quote points to 'H'
BUT, you cannot do *quote = 'A';
This will give you an error.
If you wish to make changes to the characters in a string, it is a good habit to use arrays.
char quote[] = "Hello World";
Here also *quote points to 'H'
BUT, in this case *quote = 'A' is perfectly valid.
The array quote will get changed.
You're reassigning quote in your for initializer, which is invalid and will cause an access-violation because you're dereferencing it in the *quote != NULL part.
Semantically NULL and '\0' are equivalent, but syntactically I'd prefer this. Note that by using this approach you keep a pointer to (the start of) the string.
wchar const_t* quote = L"To be or not to be, that is the question.";
for( wchar_t* c = quote; *c != '\0'; c++ ) {
*c = tolower( *c );
}
alternatively using an index:
wchar const_t quote[] = L"To be or not to be, that is the question.";
for( size_t i = 0; i < sizeof(quote); i++ ) {
quote[i] = tolower( quote[i] );
}
(note that the semantics of sizeof will change if the value of quote is not known at compile time)