I am writing a code to reverse a string in place:
void ReverseInPlace(char * x)
{
char * end = x;
int i;
i = 0;
char temp;
while(*end)
{
++end;//point tol end
++i;//count the lenght
}
--end;
printf("%s",end);
printf("%d",i);
while(i)
{
temp = *end;
printf("%s","\n");
printf("%c",temp);
end--;
x++;
i--;
}
printf("%s","\n");//blank line
printf("%s","\n");//blank line
printf("%s","-----");//blank line
printf("%s",x);//print original
}
Here are my confusions:
Even though I am able to print the characters in reverse, I want to reverse the string without using an array
I get error when I try to do the following:
*x = temp;
You say you are doing this:
ReverseInPlace("123456789");
You need to do something like this:
char str[] = "123456789";
ReverseInPlace(str);
Doing it the latter way allocates storage which you can modify, as opposed to modifying a literal string, which is illegal.
My proposal
#include <stdio.h> // printf
#include <string.h> // strlen
void swap(char* a , char* b)
{
char tmp;
tmp=*a;
(*a) = (*b);
(*b) = tmp;
}
void ReverseInPlace(char * x)
{
char * end = x;
int i,j,length;
char temp;
length = strlen(x);
//swap 1st with last, then 2nd with last-1, etc. Till we reach the middle of the string.
for(i=0,j=length-1 ; i<j ; ++i,--j)
swap( &x[i] , &x[j]);
}
main (){
char str[] = "123456789";
ReverseInPlace(str);
//ReverseInPlace("1234"); // this would lead to error, because "1234", can not be modified
printf("%s\n",str);
}
After middle is reached, you would swap elements which were already swapped by previous iterations. For illustration:
char* x = "1234";
1 2 3 4
4 2 3 1
4 3 2 1 // already reversed!
4 2 3 1
1 2 3 4 // if we continue till i==length-1 && j=0 , then we just get where we started
char *p="abcd123"; // ........string literal stored in read only memory mostly in text segment(where the code is stored) so you should never change value here as it may crash your program by writing onto code. Here p is pointing to address which maybe in text segment.
char q[]="abcd1234"; //..... the values can be changed and array size can be modified but address cant be changed because the array is nothing but a constant pointer. That's the problem with your code .....you are calling the function and parameter is a string literal whereas it should be a constant pointer. Further the storage class here is stack so u can modify the values.
Related
This question already has answers here:
Can a local variable's memory be accessed outside its scope?
(20 answers)
Closed 3 years ago.
I initially wrote a perfectly working C code for converting a positive integer (say 12345) to a string (i.e. "12345") and print it. The logic is simple: extracting the digits of the integer one-by-one by taking modulo 10 and reducing the integer by a factor of 10 each time (this occurs in the itos() function). The final string needs to hold the extracted digits in reverse order so as to match the original integer (cf. reverse() function).
#include <stdio.h>
#include <string.h>
char *reverse (char *s);
char *itos (int n);
int
main ()
{
int n = 12345;
char *s = itos (n);
printf ("%s", s);
return 1;
}
char *
itos (int n)
{
int d = 0;
char s[100];
int i = 0;
do
{
d = n % 10;
s[i] = (d + '0');
n = n / 10;
i++;
} while (n != 0);
s[i] = '\0';
return reverse(s);
}
char *
reverse (char *s)
{
int i = 0;
int j = strlen (s) - 1;
char temp;
while (i < j)
{
temp = s[i];
s[i] = s[j];
s[j] = temp;
i++;
j--;
}
return s;
}
You may compile it here. This works perfectly fine, but something strange happens if I return s instead of reverse(s) from the char* itos() function. That is:
#include <stdio.h>
#include <string.h>
char *itos (int n);
int
main ()
{
int n = 12345;
char *s = itos (n);
printf ("%s", s);
return 1;
}
char *
itos (int n)
{
int d = 0;
char s[100];
int i = 0;
do
{
d = n % 10;
s[i] = (d + '0');
n = n / 10;
i++;
} while (n != 0);
s[i] = '\0';
return s;
}
I'd have expected it to simply print the string "54321" instead of "12345". However, in the output screen, I simply get a (null). There might be a logical error in the char* itos() function, as in I might have done some illegal pointer operation, but I can't really locate the source of error.
I did try debugging by inserting print statements in several parts of the code. What I noticed is if I print the string s within the itos() function just before the return statement then it works fine and prints the string "54321". However, the print statement in the main() function still outputs a (null), which probably implies that there is some mismanagement of memory when the itos() function returns a character pointer to the main() function. Could someone please clarify?
Inside function itos you declared the variable s which is an array of 100 chars. This array is allocated somewhere after the start of the function and deallocated just before the function returns to its caller.
So when you put a printf inside itos, the variable s will still be a valid storage and this is why it worked as you intended. However as soon as itos returns, the memory reserved to the variable s should be considered deallocated thus can not be expected to still contain the data it had when itos was executing, creating undefined behavior if you rely on that.
Probably once deallocated, the memory pointed by s had its values modified, causing your function to not being able to proper convert the string back to a number.
char s[100] is only valid within itos. When the function returns, the memory becomes invalid, and accessing it through the pointer you returned is undefined behavior. You could instead allocate the memory dynamically, like this:
char* s = malloc(100);
When you do it this way, you need to free it when you're done with it (such as after the printf in main), like this:
free(s);
So, would defining a global character array be the way to go?
This would also work. Generally, it has its advantages and disadvantages over dynamically allocating the memory. For instance, it would occupy the memory at all times even if your function isn't using it. But to make up for it, the function runs faster because it doesn't have to dynamically allocate/delete the memory every time the function runs. But on the other hand it increases the startup time because it will zero-initialize the array. And of course you couldn't run it multiple times in parallel with one global array. But for this small program, none of those matter much.
I got the char array "anana" and I am trying to get a "B" into the beginning in the char array so it spells "Banana" but I cannot wrap my head around how to construct a simple while loop to insert the B and then move every letter one step to the right
Assuming:
char array[7] = "anana";
Then:
memmove(array+1, array, 6);
array[0] = 'B';
The memmove function is specifically for cases where the data movement involves an overlap.
You can use a more traditional approach using...
#include <stdio.h>
int main()
{
char s[] = "ananas";
char b[7] = "B";
for(int i = 0; i < 7; ) {
char temp = s[i++];
b[i] = temp;
}
printf("%s", b);
return 0;
}
Please follow these steps:
Create a new array of size 7 (Banana + terminator). You may do this dynamically by finding the size of the input string using strlen().
Place your desired character say 'B' at newArray[0].
Loop over i=1 -> 7
Copy values as newArray[i] = oldArray[i-1];
I made a small function that fills an allocated block of memory containing every position of a given char within a given string and returns a pointer to the memory block.
The only problem with this function is that there is no way to check the size of the memory block; so I also made a function that counts the occurrence of a given char within a string.
Here is an example of use:
/*count occurences of char within a given string*/
size_t strchroc(const char *str, const char ch)
{
int c = 0;
while(*str) if(*(str++) == ch) c++;
return c;
}
/*build array of positions of given char occurences within a given string*/
int *chrpos(const char *str, const char ch)
{
int *array, *tmp, c = 0, i = 0;
if(!(array = malloc(strlen(str) * sizeof(int)))) return 0x00;
while(str[c])
{
if(str[c] == ch) array[i++] = c;
c++;
}
if(!(tmp = realloc(array, i * sizeof(int)))) return 0x00;
array = tmp;
return array;
}
int main(void)
{
char *str = "foobar foobar"; //'o' occurs at str[1], str[2], str[8], and str[9]
int *array, b = 0, d;
if(!(array = chrpos(str, 'o'))) exit(1); //array[0] = 1, array[1] = 2, array[2] = 8, array[3] = 9
/*
* This is okay since I know that 'o'
* only occures 4 times in str. There
* may however be cases where I do not
* know how many times a given char
* occurs so I figure that out before
* utilizing the contents of array.
* I do this with my function strchroc.
* Below is a sample of how I would
* utilize the data contained within
* array. This simply prints out str
* and on a new line prints the given
* char's location within the str
* array
*/
puts(str);
while(b < (int) strchroc(str, 'o')) //loop once for each 'o'
{
for(d = 0; d < (b == 0 ? array[b] : array[b] - array[b - 1] - 1); d++) putc((int) ' ', stdout);
printf("%d", array[b]);
b++;
}
}
Output:
foobar foobar
12 89
My only concern is that if one of these two functions fail, there is no way for the data to be used correctly. I was thinking about making the number of occurrences of char within the string an argument for chrpos but even then I would still have to call both functions.
I was wondering if anybody had any suggestions for a way to do this so that I only need one function to build the array.
The only way I can think of is by storing the number of char occurrences into array[0] and having array[1] through array[char_occurences] holding the positions of char.
If anybody has a better idea I would greatly appreciate it.
As stated in my comment the first thing is to save up the data anyway, in case you can't shrink the allocated memory :
if (!(tmp = realloc(array, i * sizeof(int))))
return array;
return (tmp); //array = tmp; is useless
If you want to protect a bit more your strchroc function add a if (!str) return 0; at the beginning.
You can change your function so that it also "returns" the number of occurrences found. While we cannot actually return multiple values from a function in C, we can pass a pointer as a parameter and have the function write down a value using that pointer:
int *chrpos(const char *str, char ch, int *found) {
/*
...
*/
*found = i;
return array;
}
Note that you don't need the const modifier for ch.
AFunc changes what was sent to it, and the printf() outputs the changes:
void AFunc ( char *myStr, int *myNum )
{
*myStr = 's';
*myNum = 9;
}
int main ( int argc, char *argv[] )
{
char someString = 'm';
int n = 6;
AFunc(&someString, &n);
printf("%c" "%d", someString, n);
}
But what if the string was more than one char? How would the code look differently? Thanks for any help.
If it were a "string" instead of a char, you would do something like this:
#include <stdio.h>
void AFunc (char *myStr, int *myNum) {
myStr[0] = 'p'; // or replace the lot with strcpy(myStr, "pax");
myStr[1] = 'a';
myStr[2] = 'x';
myStr[3] = '\0';
*myNum = 9;
}
int main (void) {
char someString[4];
int n = 6;
AFunc(someString, &n);
printf("%s %d", someString, n);
return 0;
}
which outputs:
pax 9
A "string" in C is really an array of characters terminated by the \0 (NUL) character.
What the above code does is to pass in the address of the first character in that array and the function populates the four characters starting from there.
In C, a pointer to char isn't necessarily a string. In other words, just because you have char *x;, it doesn't mean that x is a string.
To be a string, x must point to a suitably allocated region which has a 0 in it somewhere. The data from the first character that x points to and up to the 0 is a string. Here are some examples of strings in C:
char x[5] = {0}; /* string of length 0 */
char x[] = "hello"; /* string of length 5, the array length being 6 */
char *x = "hello"; /* string of length 5. x is a pointer to a read-only buffer of 6 chars */
char *x = malloc(10);
if (x != NULL) {
strcpy(x, "hello"); /* x is now a string of length 5. x points
to 10 chars of useful memory */
}
The following are not strings:
char x[5] = "hello"; /* no terminating 0 */
char y = 1;
char *x = &y; /* no terminating 0 */
So now in your code, AFunc's first parameter, even though is a char * isn't necessarily a string. In fact, in your example, it isn't, since it only points to a memory that has one useful element, and that's not zero.
Depending upon how you want to change the string, and how the string was created, there are several options.
For example, if the myStr points to a writable memory, you could do something like this:
/* modify the data pointed to by 'data' of length 'len' */
void modify_in_place(char *data, size_t len)
{
size_t i;
for (i=0; i < len; ++i)
data[i] = 42 + i;
}
Another slightly different way would be for the function to modify data until it sees the terminating 0:
void modify_in_place2(char *data)
{
size_t i;
for (i=0; data[i]; ++i)
data[i] = 42 + i;
}
You are only dealing with chars and char pointers. None of the char pointers are valid strings as they are not null terminated.
Try defining a string and see what it looks like.
But what if the string was more than one char? How would the code look
differently? Thanks for any help
Ofcourse, you would modify the other characters as well, but in the exact same way you did the first time.
Declare a char array and pass its address
Modify values at those address
A char array would be a more clear term for a string.
I'm starting to learn C by reading K&R and going through some of the exercises. After some struggling, I was finally able to complete exercise 1-19 with the code below:
/* reverse: reverse the character string s */
void reverse(char s[], int slen)
{
char tmp[slen];
int i, j;
i = 0;
j = slen - 2; /* skip '\0' and \n */
tmp[i] = s[j];
while (i <= slen) {
++i;
--j;
tmp[i] = s[j];
}
/* code from copy function p 29 */
i = 0;
while ((s[i] = tmp[i]) != '\0')
++i;
}
My question is regarding that last bit of code where the tmp char array is copied to s. Why doesn't a simple s = tmp; work instead? Why does one have to iterate through the array copying index by index?
Maybe I'm just old and grumpy, but the other answers I've seen seem to miss the point completely.
C does not do array assignments, period. You cannot assign one array to another array by a simple assignment, unlike some other languages (PL/1, for instance; Pascal and many of its descendants too - Ada, Modula, Oberon, etc.). Nor does C really have a string type. It only has arrays of characters, and you can't copy arrays of characters (any more than you can copy arrays of any other type) without using a loop or a function call. [String literals don't really count as a string type.]
The only time arrays are copied is when the array is embedded in a structure and you do a structure assignment.
In my copy of K&R 2nd Edition, exercise 1-19 asks for a function reverse(s); in my copy of K&R 1st Edition, it was exercise 1-17 instead of 1-19, but the same question was asked.
Since pointers have not been covered at this stage, the solution should use indexes instead of pointers. I believe that leads to:
#include <string.h>
void reverse(char s[])
{
int i = 0;
int j = strlen(s) - 1;
while (i < j)
{
char c = s[i];
s[i++] = s[j];
s[j--] = c;
}
}
#ifdef TEST
#include <stdio.h>
int main(void)
{
char buffer[256];
while (fgets(buffer, sizeof(buffer), stdin) != 0)
{
int len = strlen(buffer);
if (len == 0)
break;
buffer[len-1] = '\0'; /* Zap newline */
printf("In: <<%s>>\n", buffer);
reverse(buffer);
printf("Out: <<%s>>\n", buffer);
}
return(0);
}
#endif /* TEST */
Compile this with -DTEST to include the test program and without to have just the function reverse() defined.
With the function signature given in the question, you avoid calling strlen() twice per line of input. Note the use of fgets() — even in test programs, it is a bad idea to use gets(). The downside of fgets() compared to gets() is that fgets() does not remove the trailing newline where gets() does. The upsides of fgets() are that you don't get array overflows and you can tell whether the program found a newline or whether it ran out of space (or data) before encountering a newline.
Your tmp array was declared on stack and so when your method completes, the memory used to hold the values will be freed because of scoping.
s = tmp means that s should point to the same memory location as tmp. This means that when tmp is freed, s will still be pointing to a now possible invalid, freed memory location.
This type of error is referred to as a dangling pointer.
Edit: This isn't a dangling modifier as pointed out in the comments of this answer. The issue is that saying s = tmp only changes what the parameter points to, not what the actual array that was passed.
Also, you could perform your reverse with a single pass and without allocating a whole array in memory by just swapping the values in place one by one:
void reverse(char s[], int slen) {
int i = 0; // First char
int j = slen - 2; // Last char minus \n\0
char tmp = 0; // Temp for the value being swapped
// Iterate over the array from the start until the two indexes collide.
while(i < j) {
tmp = s[i]; // Save the eariler char
s[i] = s[j]; // Replace it with the later char
s[j] = tmp; // Place the earlier char in the later char's spot
i++; // Move forwards with the early char
j--; // Move backwards with the later char
}
}
Because both s and tmp are memory addressees. If you s = tmp, both pointers would point to the same array.
Suppose that we have
char s[] ="ab";
/*
* Only for explanatory purposes.
*
*/
void foo(char s[]){
char tmp [] = "cd";
s= tmp;
}
foo(s);
after s= tmp you would have
s[0] : 'c'
s[1] : 'd'
s[2] : '\0'
Even though both arrays have the same data, a change in tmp, will affect both of them, because both arrays are actually the same. They both contain data that´s in the same memory address. So by changing any position of the tmp array, or destroying the tmp array, s would be affected in the same way.
By looping over the array, what you are doing is moving a piece of data from one memory address to another.
In my copy of K & R, pointers are explained in chapter 4. A quick glance through the first pages may be of help.
To round out the discussion here are two other possible ways to reverse as string:
void reverse(char string1[], char string2[])
{
int i = 0, len = 0;
while(string2[len] != '\0') // get the length of the string
len++;
while(len > 0)
{
string1[i] = string2[len-1]; // copy the elements in reverse
i++;
len--;
}
string1[i] = '\0'; // terminate the copied string
}
Or recursively:
void reverse (const char *const sPtr)
{
//if end of string
if (sPtr[0] == '\0')
{
return;
}
else //not end of the string...
{
reverse(&sPtr[1]); //recursive step
putchar(sPtr[0]); //display character
}
}
because tmp is a pointer, and you need to get a copy, not a "link".
In case of s=tmp, the value of tmp which is the also the beginning address of the array, would get copied to s.
That way both s and tmp will point to the same address in memory, which I think is not the purpose.
cheers
Try experimenting and see what happens when you do things like this:
void modifyArrayValues(char x[], int len)
{
for (int i = 0; i < len; ++i)
x[i] = i;
}
void attemptModifyArray(char x[], int len)
{
char y[10];
for (int i = 0; i < len; ++i)
y[i] = i;
x = y;
}
int main()
{
int i = 0;
char x[10];
for (i = 0; i < 10; ++i)
x[i] = 0;
attemptModifyArray(x, 10);
for (i=0; i < 10; ++i)
printf("%d\n", x[i]); // x is still all 0's
modifyArrayValues(x, 10);
for (i=0; i < 10; ++i)
printf("%d\n", x[i]); // now x has 0-9 in it
}
What happens when you modify the array directly in attemptModifyArray, you are just overwriting a local copy of the address of the array x. When you return, the original address is still in main's copy of x.
When you modify the values in the array in modifyArrayValues, you are modifying the actual array itself which has its address stored in modifyArrayValues local copy of x. When you return, x is still holding on to the same array, but you have modified the values in that array.
There's an interesting sub-thread in this thread about arrays and pointers
I found this link on wikipedia with a peculiar code snippet showing just how 'plasticine' C can be!
/* x designates an array */
x[i] = 1;
*(x + i) = 1;
*(i + x) = 1;
i[x] = 1; /* strange, but correct: i[x] is equivalent to *(i + x) */
Of course what's even more confusing in C is that I can do this:
unsigned int someval = 0xDEADD00D;
char *p = (char *)&someval;
p[2] = (char)0xF0;
So the interchangibility of pointers and arrays seems so deep-set in the C language as to be almost intentional.
What does everyone else think?
---Original Post---
s and tmp are both pointers so doing s = tmp will simply make s point at the address where tmp lives in memory.
Another problem with what you outlined is that tmp is a local variable so will become 'undefined' when it goes out of scope i.e when the function returns.
Make sure you thoroughly grasp these three concepts and you won't go far wrong
Scope
The difference between the stack and the heap
Pointers
Hope that helps and keep going!
A very straight forward answer would be -
both s and tmp are pointers to a memory location and not the arrays themselves.
In other words, s and tmp are memory addresses where the array values are stored but not the values themselves.
And one of the common ways to access these array values are by using indices like s[0] or tmp[0].
Now, if you will try to simply copy, s = tmp, the memory address of tmp array will be copied over to s. This means that, the original s array will be lost and even s memory pointer will now point to tmp array.
You will understand these concepts well with due time so keep going through the book.
I hope this elementary explanation helps.