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.
Related
I am trying to reverse a string (character array) using the following code, but when I attempt to print the string, the value of null. This is a homework assignment, but I am trying to learn so any help would be appreciated.
void input_reverse_string(const char* inputStr, char* reverseStr)
{
int i = 0;
int length = 0;
for (; *(inputStr++) != '\0'; i++)
{
length++;
}
while (*inputStr)
{
*reverseStr = *inputStr;
inputStr++;
reverseStr++;
}
const char* chr_ptr = &inputStr[length - 1];
printf("I see a %s\n", *chr_ptr);
*reverseStr = '\0';
printf("%d", length);
/* return reverseStr; */
}
Several things are out of order:
That's a strange way of computing the length of a string. You are using an index variable that you don't need, and incrementing 3 things at the same time, it's unneeded to say the least.
After calculating the length, and incrementing the inputStr pointer up to its end, you don't reset the pointer, so it still points to the end of the string (actually, one after the end!).
Inside the while you are advancing both pointers (inputStr and reverseStr) in the same direction, which can't possibly be right if you want to reverse the string.
The correct way to do this would be:
Compute the length of the string. Either use strlen() or do it by hand, but you really only need to increment one variable to do this. You can avoid incrementing inputStr, just use a temporary pointer.
Start from inputStr + length and walk backwards. Either use a pointer and do -- or just index the string).
Here's a working example:
void reverse_string(const char* inputStr, char* reverseStr) {
unsigned len = 0;
int i;
while (inputStr[len])
len++;
for (i = len - 1; i >= 0; i--) {
reverseStr[len - i - 1] = inputStr[i];
}
reverseStr[len] = '\0';
}
int main(void) {
char a[6] = "hello";
char b[6];
reverse_string(a, b);
puts(b);
return 0;
}
Output:
olleh
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.
this is my first post in Stack Overflow. If I am missing anything, please let me know and I will try to add more information.
The function below is only supposed to use pointer operations and not array operations, as part of an assignment.
I have this function in C that is part of a larger program:
void reverseString(char strings[NUM_STRINGS][STRING_LENGTH])
{
int i, j;
char *ptr; //Declare pointer variable.
for (i = 0; i < NUM_STRINGS; i++)
{
ptr = strings[i];
do { //Here, we ignore the null terminators in the char array.
ptr++;
} while (*ptr != '\0');
ptr--; //Iterate the pointer variable once downward.
j = i;
while (strings[j] < ptr) //While loop for reversing the string
{
//printf("ptr: %d\n", ptr);
//printf("strings[j]: %d\n", strings[j]);
char temp = *strings[j];
*strings[j++] = *ptr;
*ptr-- = temp;
}
}
}
What it is supposed to do is accept a 2D char array with 4 strings and with each string holding up to 32 bytes of text. Then, it reverses each string in-place in the array. For example, if I input the four strings:
Hello
World
Good
morning
It is supposed to then return:
olleH
dlroW
dooG
gninrom
However, what ends up happening is that only the first and last characters of each string are reversed. For example:
oellH
dorlW
dooG
gorninm
I have tried different solutions such as using i instead of j in the while loop or using prefix ++ instead of suffix, but nothing has worked yet. Any pointers as to what I should be looking for?
Thank you.
The solution is below. As described in my comment below, I added a secondary pointer instead of using strings[j] in the loop. The pointer now references to the beginning of the string instead of the entire array of strings.
void reverseString(char strings[NUM_STRINGS][STRING_LENGTH])
{
int i, j;
char *ptr; //Declare pointer variable.
char *ptr2;
for (i = 0; i < NUM_STRINGS; i++)
{
ptr = strings[i];
ptr2 = strings[i];
do { //Here, we ignore the null terminators in the char array.
ptr++;
} while (*ptr != '\0');
ptr--; //Iterate the pointer variable once downward.
j = i;
while (ptr2 < ptr) //While loop for reversing the string
{
char temp = *ptr2;
*ptr2++ = *ptr;
*ptr-- = temp;
}
}
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char * reverse(char *string);
int main(int argc, char *argv[])
{
char array[10];
array[0] = 'a';
array[1] = 'b';
array[2] = 'c';
array[3] = 'd';
array[4] = 'e';
printf("1%s\n",array);
char *p = reverse(array);
printf("4%s\n",p);
printf("5%s\n",array);
}
char * reverse(char *string)
{
int size = strlen(string);
char reversed[size];
int i;
int j = 0;
for(i = size-1; i >= 0; i--)
{
reversed[j] = string[i];
j++;
}
printf("2%s\n",reversed);
string = reversed;
printf("3%s\n",string);
return reversed;
}
This code basically just initializes an array of values and passes it into a method that reverses these values.
I am not sure if this is the best way to execute the task, since I am new to pointers and arrays in C.
But the real question is this:
Can anyone figure out why in this line
printf("4%s\n",p);
if you remove the preceding '4', so it looks like so
printf("%s\n",p);
the line won't print at all?
You are returning a pointer to local variable(reversed) in the function reverse the question should actually be: Why did it work in the first place?.
This code string = reversed; will only copy the pointer, and again the local copy of the pointer so it has no effect outside the function.
To reverse a string you don't need additional memory - this can be done in-place.
Strings in C must end with the null character. You're using strlen on a non null-terminated string.
Furthermore, you just a very lucky person, because there is a serious problem with you code: you forget to add \0 symbol at the end of string.
UPD: the main problem is with code line char reversed[size];.
It's a regular local variable, it has automatic duration, which means that it springs into existence when the function is called and disappears when the function returns (see this link).
You need to change it to:
char *reversed = malloc((size+1)*sizeof(char));
UPD-2: another bug fixing will be:
1) add array[5] = '\0'; after all other array initializing lines
2) add reversed[j] = '\0'; after for...loop:
for(i = size-1; i >= 0; i--)
{
reversed[j] = string[i];
j++;
}
reversed[j] = '\0';
UPD-3: But in general it will much more correctly initialize your string in appropriate way:
char array[10] = "abcde";
What is a way in C that someone could find the length of a character array?
I will happily accept pseudo-code, but am not averse to someone writing it out if they'd like to :)
Provided the char array is null terminated,
char chararray[10] = { 0 };
size_t len = strlen(chararray);
If you have an array, then you can find the number of elements in the array by dividing the size of the array in bytes by the size of each element in bytes:
char x[10];
int elements_in_x = sizeof(x) / sizeof(x[0]);
For the specific case of char, since sizeof(char) == 1, sizeof(x) will yield the same result.
If you only have a pointer to an array, then there's no way to find the number of elements in the pointed-to array. You have to keep track of that yourself. For example, given:
char x[10];
char* pointer_to_x = x;
there is no way to tell from just pointer_to_x that it points to an array of 10 elements. You have to keep track of that information yourself.
There are numerous ways to do that: you can either store the number of elements in a variable or you can encode the contents of the array such that you can get its size somehow by analyzing its contents (this is effectively what null-terminated strings do: they place a '\0' character at the end of the string so that you know when the string ends).
Although the earlier answers are OK, here's my contribution.
//returns the size of a character array using a pointer to the first element of the character array
int size(char *ptr)
{
//variable used to access the subsequent array elements.
int offset = 0;
//variable that counts the number of elements in your array
int count = 0;
//While loop that tests whether the end of the array has been reached
while (*(ptr + offset) != '\0')
{
//increment the count variable
++count;
//advance to the next element of the array
++offset;
}
//return the size of the array
return count;
}
In your main function, you call the size function by passing the address of the first element of your array.
For example:
char myArray[] = {'h', 'e', 'l', 'l', 'o'};
printf("The size of my character array is: %d\n", size(&myArray[0]));
You can use strlen
strlen(urarray);
You can code it yourself so you understand how it works
size_t my_strlen(const char *str)
{
size_t i;
for (i = 0; str[i]; i++);
return i;
}
if you want the size of the array then you use sizeof
char urarray[255];
printf("%zu", sizeof(urarray));
If you want the length of the character array use sizeof(array)/sizeof(array[0]), if you want the length of the string use strlen(array).
There is also a compact form for that, if you do not want to rely on strlen. Assuming that the character array you are considering is "msg":
unsigned int len=0;
while(*(msg+len) ) len++;
using sizeof()
char h[] = "hello";
printf("%d\n",sizeof(h)-1); //Output = 5
using string.h
#include <string.h>
char h[] = "hello";
printf("%d\n",strlen(h)); //Output = 5
using function (strlen implementation)
int strsize(const char* str);
int main(){
char h[] = "hello";
printf("%d\n",strsize(h)); //Output = 5
return 0;
}
int strsize(const char* str){
return (*str) ? strsize(++str) + 1 : 0;
}
You can use this function:
int arraySize(char array[])
{
int cont = 0;
for (int i = 0; array[i] != 0; i++)
cont++;
return cont;
}
By saying "Character array" you mean a string? Like "hello" or "hahaha this is a string of characters"..
Anyway, use strlen(). Read a bit about it, there's plenty of info about it, like here.
Well, 11 years later, I run into this issue with a college assignment. The solution I found, worked without having to alter the function signatures that the assignment was asking for.
In my case, I had to make a function that returns the item index if the item existed or depending on if the itemPrefix (e.g. 'B' for Banana) already exists or not in the character array itemPrefixes to avoid passing duplicate prefixes.
So, I had to use a for loop (or while loop). The problem was that the assignment had given me specific signatures for each function and for that specific function it didn't allow me to pass the count variable that was on the main() function as an argument.
I had to improvise.
Both the ways mentioned above didn't work. strlen() didn't work as intended since there was not a '\0' end character that strings have. The sizeof() method also didn't work, because it returned the size of the pointer of the character array that was passed in as an argument instead of the number of elements.
So, this is the function I came up with. A simple while loop that checks whether the current character is NULL (or 0).
void charArrLength(char array[]) {
int arrLength = 0;
while (array[arrLength] != 0) {
arrLength++; //increment by 1
}
printf("Character array has %d elements", arrLength);
}
For this to work though, in the main() function, you need to declare your character array as a character pointer and then allocate the memory that you need based on the number of items that you ultimately wish to have inside your array.
void charArrLength(char array[]) {
int arrLength = 0;
while (array[arrLength] != 0) {
arrLength++;
}
printf("Character array has %d elements", arrLength); //should give 33
}
int main() {
char *array; //declare array as a pointer
int arraySize = 33; //can be anything
array = (char*) malloc(arraySize * sizeof(char));
charArrLength(array);
free(array); //free the previously allocated memory
}
Below you will see how I utilised this function in my assignment.
First, here is the above function tailored to my needs.
int isItemExists(char itemPrefixes[], char itemPrefix) {
int count = 0; //declare count variable and set to 0
int itemIndex = -1; //declare item index variable and set it to -1 as default
while (itemPrefixes[count] != 0) {
count++;
}
for (int i = 0; i < count; i++) {
if (itemPrefix == itemPrefixes[i]) {
itemIndex = i; //if item exists, set item index to i
}
}
return itemIndex;
}
Then, how I declared the itemPrefixes array in main() function and how I allocated the needed memory based on n (the number of items the user would like to add to itemPrefixes array).
char *itemPrefixes;
int n = 0; //number of items to be added variable
printf("> Enter how many items to add: ");
scanf("%d", &n);
//allocate n * size of char data type bytes of memory
itemPrefixes = (char*) malloc(n * sizeof(char));
And finally, here is how that function was used after all.
do {
printf("\n\n> Enter prefix for item %d: ", i + 1);
scanf(" %c", &itemPrefix);
//prompt the user if that itemPrefix already exists
if (isItemExists(itemPrefixes, itemPrefix) != -1) {
printf("\nItem prefix already exists! Try another one.\n");
}
} while (isItemExists(itemPrefixes, itemPrefix) != -1);
Also, in the end of the code I free the previously allocated memory.
free(itemPrefixes);
To clear this out, again, this could be much easier if the conditions were different. The assignment was strict about not passing n as an argument. Nevertheless, I hope I help someone else that might be looking for this in the future!
Just for the sake of it, if anybody sees this and has something simpler to suggest, feel free to tell me.