Whilst reading through K&R, I came across the integer to string function. I gave it a quick read, and decided to implement it myself, but instead of printing, it updates a character array.
Here is what I have
void inttostr(int number, char str[]) {
static int i;
if (number / 10) {
inttostr(number / 10, str);
}
str[i++] = number % 10 + '0';
}
It seemed to work for the few integers I gave it, but I have some questions.
I haven't explicitly included the nul byte \0 at the end, so why does the string work fine when printed with printf("%s\n", str);?
I don't think I'm very good at thinking recursively. When I try and step through the program in my mind, I lose track of what is still awaiting execution. Is there a better way of seeing what is happening internally, to help me learn?
Any other suggestions on the code?
I'm using Xcode.
This is not homework. I'm just learning.
Thanks!
You're correct that you're never writing NUL, which is a bug.
In general, you don't have to think through the entire solution. You just have to make sure every step is correct. So in this case, you say:
1 . inttostr(number / 10, str);
will take care of all but the last digit.
2 . Then I will take care of the last one.
You can trace what's happening, though. For e.g. 54321 it looks like:
inttostr(54321, str); // str = ...;
inttostr(5432, str); // str = ...;
inttostr(543, str); // str = ...;
inttostr(54, str); // str = ...;
inttostr(5, str); // str = ...;
str[0] = '5'; // str = "5...";
str[1] = '4'; // str = "54...";
str[2] = '3'; // str = "543...";
str[3] = '2'; // str = "5432...";
str[4] = '1'; // str = "54321...";
Note that when you don't return from any of the functions until you write the first character, then you return in the opposite order from the calls.
The ... signifies that you haven't NUL-terminated. Another issue is that you're using a static variable, so your code isn't reentrant; this means it breaks in certain scenarios, including multi-threading.
To address the reentrancy and NUL issue, you can do something like the code below. This creates a helper function, and passes the current index to write.
void inttostr_helper(int number, char str[], int *i)
{
if (number / 10) {
inttostr_helper(number / 10, str, i);
}
str[(*i)++] = number % 10 + '0';
str[*i] = '\0';
}
void inttostr(int number, char str[])
{
int i = 0;
inttostr_helper(number, str, &i);
}
EDIT: Fixed non-static solution.
I am impressed of the creativity to use recursive, despite that it is not necessary. I think the code should remove statically-allocated i variable because this variable will persist through calls. So the second time you use this function from your code, e.g. from main(), it will not be initiated and will be the same value from previous call. I would suggest using return value as follow:
int inttostr(int number, char *str) {
int idx = 0;
if (number / 10) {
idx = inttostr(number / 10, str);
}
str[idx++] = number % 10 + '0';
return idx;
}
1, Your compiler (especially in debug mode) may have filled str with 0. Unix does this if you allocate memory with new() - But don't rely on this, either set the first byte to \0 or memset everything to 0
2, paper + pencil. Draw a table with each variable across the top and time down the side.
3, Write the simplest, longest version first, then get clever.
I haven't explicitly included the nul byte \0 at the end, so why
does the string work fine when printed
with printf("%s\n", str);?
how is the original char array declared when you call your function the first time? if it is a static char array then it will be filled with 0's and that would explain why it works, otherwise its just "luck"
I don't think I'm very good at thinking recursively. When I try and
step through the program in my mind, I
lose track of what is still awaiting
execution. Is there a better way of
seeing what is happening internally,
to help me learn?
honestly, I am not sure if anybody is good in thinking recursively :-)
Did you try drawing the execution step by step?
Related
There is this problem on LeetCode that I can not get to work in C/C++
The idea is to reverse an array in its place (using no other additional array) using recursion.
The link is : https://leetcode.com/explore/learn/card/recursion-i/250/principle-of-recursion/1440/
The solution is done in Java or Python.
I tried implementing the solution in C but I always get the original array, my code is as follows:
void reverseString(char* s, int sSize){
if(!s)
return;
reverseString(s+1,sSize-1);
s[sSize] = *s;
}
There is something I am not accounting for. Please let me know how would you solve it, and if possible why this is not working. Thanks.
I'll take a stab at this.
The general idea for a recursive solution is for each call to get a pointer to the start of a string, and how many characters to look at, and this walks its way to the middle of the string.
void reverseString(char *start, int n)
{
if (n <= 1) return;
char tmp = start[0];
start[0] = start[--n]; // the nth character is start[n-1]
start[n] = tmp;
reverseString(++start, --n);
}
On each recursive call, the starting string pointer is incremented by one, and the length decreased by two.
FIRST CALL: v v
hello, world
SECOND CALL: ^ ^
The common danger area is making sure it does the right thing with even and odd-length strings.
This method is a bit simpler with just two parameters, and - as some might say - a bit more elegant :-) even if the ++ and -- could be considered tricky (one increment and two decrements).
EDIT: This version is also tail recursive, which can lead to certain optimizations by internally turning it into a loop.
Solution (thank you to the folks in the comments):
void reverse(char * str, int len)
{
char tmp;
if (len <= 1)
return;
tmp = str[0];
len--;
str[0] = str[len];
str[len] = tmp;
str++;
reverse(str, len-1);
}
Call this function with your initial string and 0 as arguments:
char str[] = "Ding dong";
reverse(str, 0, strlen(a));
void reverse_string(char *x, int start, int end)
{
char ch;
if (start >= end)
return;
ch = *(x+start);
*(x+start) = *(x+end);
*(x+end) = ch;
//Function calling itself: Recursion
reverse_string(x, ++start, --end);
}
In this function we are passing the string, the starting index and the ending index.... The recursion will continue till start>=end i.e. till the center of the string appears..... And everytime it will swap the 2 indexes i.e. from the start and from the end...
There is this problem on LeetCode that I can not get to work in C/C++
The idea is to reverse an array in its place (using no other additional array) using recursion.
The link is : https://leetcode.com/explore/learn/card/recursion-i/250/principle-of-recursion/1440/
The solution is done in Java or Python.
I tried implementing the solution in C but I always get the original array, my code is as follows:
void reverseString(char* s, int sSize){
if(!s)
return;
reverseString(s+1,sSize-1);
s[sSize] = *s;
}
There is something I am not accounting for. Please let me know how would you solve it, and if possible why this is not working. Thanks.
I'll take a stab at this.
The general idea for a recursive solution is for each call to get a pointer to the start of a string, and how many characters to look at, and this walks its way to the middle of the string.
void reverseString(char *start, int n)
{
if (n <= 1) return;
char tmp = start[0];
start[0] = start[--n]; // the nth character is start[n-1]
start[n] = tmp;
reverseString(++start, --n);
}
On each recursive call, the starting string pointer is incremented by one, and the length decreased by two.
FIRST CALL: v v
hello, world
SECOND CALL: ^ ^
The common danger area is making sure it does the right thing with even and odd-length strings.
This method is a bit simpler with just two parameters, and - as some might say - a bit more elegant :-) even if the ++ and -- could be considered tricky (one increment and two decrements).
EDIT: This version is also tail recursive, which can lead to certain optimizations by internally turning it into a loop.
Solution (thank you to the folks in the comments):
void reverse(char * str, int len)
{
char tmp;
if (len <= 1)
return;
tmp = str[0];
len--;
str[0] = str[len];
str[len] = tmp;
str++;
reverse(str, len-1);
}
Call this function with your initial string and 0 as arguments:
char str[] = "Ding dong";
reverse(str, 0, strlen(a));
void reverse_string(char *x, int start, int end)
{
char ch;
if (start >= end)
return;
ch = *(x+start);
*(x+start) = *(x+end);
*(x+end) = ch;
//Function calling itself: Recursion
reverse_string(x, ++start, --end);
}
In this function we are passing the string, the starting index and the ending index.... The recursion will continue till start>=end i.e. till the center of the string appears..... And everytime it will swap the 2 indexes i.e. from the start and from the end...
I am trying to create a formatted string , however I do not know why I cannot print global array which I have modified inside the function.Also the strange behavior is that I cannot access only a specific global array (rand_session_key) rest of the other global arrays are behaving as normal(similar operations are being done on them except their size varies) and I can access their value properly. This code is run on an esp32 (DOIT Dev Kit V1) (with Arduino-Core) , when I run this program on my computer (modifying a few functions etc.) the result is what I expect , I think I am overlapping the characters in the memory or accessing it the wrong way , but had it been the case I would not have yielded the expected output on my computer.
I tried to modify my program and made it more verbose. Also I ran the same code (with some obvious modifications to make it run on my computer) , and the result is good as expected.
char persistent_peripheral_id[] = "FRUCTOSE96";
char rand_session_iden[7] = {'\0'};
char rand_session_key[17] = {'\0'};
char rand_session_channel[3] = {'\0'};
char *generate_random_session_identifier(char *rand_session_iden_local)
{
srand(time(NULL));
int counter = 0;
for (counter = 0; counter < 6; counter++)
*(rand_session_iden_local + counter) = (random(10) % ('~' - ' ')) + 'k';
rand_session_iden_local[counter] = '\0';
printf("Identifier : %s\n", rand_session_iden); //acessing global defintion of array everything is good until here
return &rand_session_iden_local[0];
}
char *generate_random_session_key(char *rand_session_key_local)
{
srand(time(NULL));
int counter = 0;
for (counter = 0; counter < 16; counter++)
*(rand_session_key_local + counter) = (random(10) % ('~' - ' ')) + 'b';
rand_session_key_local[counter] = '\0';
printf("Key : %s\n", rand_session_key);//acessing global defintion of array everything is good until here
return &rand_session_key_local[0];
}
char *generate_random_session_channel(char *rand_session_channel_local)
{
srand(time(NULL));
int channel_value = random(100);
sprintf(rand_session_channel_local, "%03ld", channel_value);
printf("Channel : %s\n", rand_session_channel);//acessing global defintion of array everything is good until here
return &rand_session_channel_local[0];
}
void begin_exchange_package()
{
//If this does not works here (observe rand_session_key) , it will not work for sprintf also ??
printf("\n %s-%s-%s-%s \n", (char *)persistent_peripheral_id,
generate_random_session_identifier(rand_session_iden),
generate_random_session_key(rand_session_key),
generate_random_session_channel(rand_session_channel));
//Notice it prints here ????
printf("\n %s \n",generate_random_session_key(rand_session_key));
Serial.println("Done");
//sprintf((char *)plain_text_package, "{\"p\":\"%s\",\"r\":\"%s\",\"k\":\"%s\",\"c\":\"%s\"}", (char *)persistent_peripheral_id,(char *)rand_session_iden, (char *)rand_session_key , (char *)rand_session_channel);
}
void setup()
{
Serial.begin(115200);
begin_exchange_package();
}
void loop()
{
}
The Output is
FRUCTOSE96-tnltkp--094
Where I expected all the 4 arrays to be printed ?? but it does print separately , is my array being terminated in the wrong way ?? also the logic to assign a random character will always yield a printable ASCII Character (I learned this from a forum on esp32's website)
This code ...
sprintf(rand_session_channel_local, "%03ld", channel_value);
... requires rand_session_channel_local to point to an array of at least four characters, because at will print at least three digits plus a string terminator. The array into which it points, rand_session_channel, is only three characters long. The resulting behavior is undefined.
The observed manifestation of the UB is consistent with the global arrays being laid out in memory such that rand_session_key immediately follows rand_session_channel, such that overflowing the latter means that the string terminator is written to position 0 of the former, making it an empty string. Note, however, that you cannot rely on predicting manifestations of UB, nor is it generally of much use to analyze them. Instead, avoid exercising UB.
It's unclear what random function you are using, since the C standard library's does not take an argument, but if the argument to yours specifies an exclusive upper bound then you could just change the sprintf format to "%02ld". Alternatively, increase the size of rand_session_channel to at least 4.
So I've got this here:
#include <stdio.h>
char halloString[] = "Ha::ll::o";
char perfumeString[] = "47::11";
char veryLongString[] = "47::11::GHesd::dghsr::bfdr:hfgd46dG";
char *extract (char *input) {somethinghappenshere}
where extract needs to get all characters after the last double ":" of given input:
"o" for halloString
"11" for perfumeString
"bfdr:hfgd46dG" for veryLongString
In short, my issue is finding the length of the string *input points to. As far as I understand it that won't be happening without making something really sketchy.
Am I correct in assuming the length cannot be acquired in a good way?
And if so would it be a horrible idea to do, for example:
char stringToProcessTemp1[50];
char stringToProcessTemp2[50];
char stringToProcess[50];
for (int i = 0; i < 50; i++) {
stringToProcessTemp1[i] = input + i;
}
for (int i = 0; i < 50; i++) {
stringToProcessTemp2[i] = input + i;
}
for (int i = 0; i < 50; i++) {
if (stringToProcessTemp1[i] == stringToProcessTemp2[i]) {
stringToProcessTemp[i] = stringToProcessTemp1[i];
}
}
Later checking where the first empty index is and saving everything before it as the used String as from my very limited experience in C when you go outside of an array you tend to get different outputs every time therefore making the chance both Temp strings match for an extra element directly after the last one of the original string what I'd consider low enough.
It's honestly the only idea I've got right now.
Finding the length of a string is no problem. strlen will do that for you. However, you don't even need that.
You can use the strstr function to find a substring within a string, in this case "::". When you find one, keep looking right after the last one you found until you don't find it anymore, then the last one you found is the one you want. Then you want the substring that starts right after it.
char *extract(char *input)
{
char *last = NULL, *start = input, *curr;
while ((curr == strstr(start, "::")) != NULL) {
last = curr; // keep track of the last "::" found
start = last + 1; // move the starting string to right after the last "::"
// move up 1 instead of 2 in case of ":::"
}
if (last != NULL) {
last +=2; // We found one; move just past the "::"
}
return last;
}
C strings, which are really only an array of characters, are by definition terminated by '\0'. So, for a well formed C string you can always get the length of the string by using strlen().
If, however, your string is not null-terminated, there is no way to determine it's length, and it is not a C string by definition any more, but just an array of characters.
Ok,I am beginner in C.I was thought that for a array to hold to characters in need to declare it as:
char a[10];
So I will have 10 elements from (0 to 9)
but it is not working.It is giving me unwanted characters.Can you tell me the problem is.My code:
#include<stdio.h>
#include<stdlib.h>
int main(void)
{
printf("%s",rand_string());
}
int rand_string(void)
{
srand(time(NULL));
char a[7];
int e;
int d;
a[0]='l';
a[1]='o';
a[2]='n';
a[3]='g';
a[4]=' ';
d=(rand()%6) + 97;
a[5]=d;
e=(rand()%10) + 48;
a[6]=e;
printf("\n%s\n",a);
return a;
}
I get results like:
long f99
|/
What I expect:
long f9
Ok so in total I have 4 questions:
*How to fix the problem of unwanted characters and why is it giving unwated characters?
*Is my way of generating random numbers with limit ok?
*how to write the first 4 letters "long" in one line rather that for each line in an array?
*How to combine 2 strings?
You need to NULL terminate your string. Extend the array by one and add a[7] = 0; in there and you'll be set.
Editorial note: Your program has another big problem in that you are returning a pointer to a local variable. You may want to change rand_string to fill in a buffer provided by main instead. Here's a quick example with both of these modifications:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
void rand_string(char a[8])
{
srand(time(NULL));
int e;
int d;
a[0]='l';
a[1]='o';
a[2]='n';
a[3]='g';
a[4]=' ';
d=(rand()%6) + 97;
a[5]=d;
e=(rand()%10) + 48;
a[6]=e;
a[7]=0;
printf("\n%s\n",a);
}
int main(void)
{
char buffer[8];
rand_string(buffer);
printf("%s", buffer);
return 0;
}
The first question is already answered by Carl Norum.
Is my way of generating random numbers with limit ok?
Yes, but defining a function would be nice, wouldn't it? Calling like a[0] = randomBetween(97, 102); is much more readable though.
EDIT: As in a comment above stated: you even could write
a[0] = randomBetween('a', 'f'); Just a little bit more readable ;-)
how to write the first 4 letters "long" in one line rather that for each line in an array?
There is no way, instead you could copy the elements in a loop or using a function like memcpy, strcpy. Taking your question wordly:
a[0] = 'l'; a[1] = 'o'; a[2] = 'n'; a[3] = 'g';
But this is not what you want, I guess :-) See also the strcpy-example below.
How to combine 2 strings?
Again, either using a loop or the functions mentioned above:
char *first = "Hello ";
char *second = "World";
char combined[12];
int currentIndex = 0, i = 0;
// copy characters from "first" as long we did not find a \0
while(first[i] != 0)
combined[currentIndex++] = first[i++];
i = 0;
// copy characters from "second" as long we did not find a \0
while(second[i] != 0)
combined[currentIndex++] = second[i++];
// finally don't forget to null-terminate!
combined[currentIndex] = 0;
Using e.g. strcpy is much easier ;-)
char *first = "Hello ";
char *second = "World";
char combined[12];
strcpy(combined, first);
strcpy(&combined[6], second);
What are we doing here? The first strcpy-call copies simply "first" to "combined". But the second calls seems to be interesting. There we copy "second" to the 7th position (start counting from 0, therefor 6). At this position was the \0-character after the first function call. But we don't want the string to end here, so we override it with the first character of the second string. One nice thing is that strcpy automatically copies the terminating \0 at the end. Quite simple, isn't it?