Problem with a function similar to strcat() - c++

I tried to write a function similar to strcat() in c++.
Here is the code of this function:
char *mystrcat(char *str1, char *str2){
int i = 0;
char *buffer = str1;
while(*str1){
str1++; i++;
}
i++;
while(*str2){
str1[i] = *str2;
str2++; i++;
}
str1[++i] = '\0';
str1 = buffer;
return str1;
}
The input values for this function are given by this code:
char string1[100], string2[100];
cout << "Enter string 1 ";
cin >> string1;
cout << "Enter string 2 ";
cin >> string2;
mystrcat(string1, string2);
cout << string1 << endl;
When I ran the code and tried to input two strings, it has given me this output:
Enter string 1 qwerty
Enter string 2 asdf
qwerty
Why the first string is displayed only?

For a start, since you're incrementing both str1 and i in the first loop, you're going to move twice as fast through that array as you think. Thats because you're increasing both:
the base of the array; and
the offset from that base.
You're actually lucky to have chosen an even number of characters, otherwise you'd probably keep going beyond the end of str1 (by missing the \0), resulting in all sorts of fun and hilarity :-)
You should increment one or the other, such as with:
char *mystrcat(char *str1, char *str2) {
int i = 0;
while(str1[i] != '\0')
i++;
while(*str2 != '\0')
str1[i++] = *str2++;
str1[i] = '\0';
return str1;
}
The reason why you only get the first string after the concatenation, is the i++ after the first loop (which I've removed from the above code).
When you exit that loop, i is the index of the str1 terminator character so, an i++ at that point will jump over it. That means that you'll append str2 after that terminator:
qwerty<nul>asdf<nul>
Hence the string will only be qwerty because C strings always stop at the first terminator.
I often find it's useful to be able to start with a memory map and variable register (on paper), then run the code in your head, changing variables along the way. It helps to understand how your program will work. You can start with something like:
1 2
Index: 012345678901234567890
str1: qwerty_
str2: asdf_
i: 0
Then, each line of code in your head will result in i changing, or the string memory blocks changing (the _ at the end indicates the string terminator).

Don't increment the string pointers. Do just
while (str1[i]) {
++i;
}
And start with iterating str2 with another variable, from 0.
What you are doing currently is basically: str1[12] = str[6]
When you want: str1[6] = str2[0]

Related

Console couts a memory address instead of string

As I understand that strings can be treated like arrays, so I tried to insert each character of a string by iterating with a while loop. However the final cout pointed to a memory address, not the string I hoped it would print.
int main()
{
int i = 0;
int n = 2;
char input;
string str1[n];
while(i<=n){
cout<<"enter letter: ";
cin>>input;
str1[i] = input;
i++;
}
cout<<"Your word is: "<<str1;
return 0;
}
The output was:
enter letter: a
enter letter: b
enter letter: c
Your word is: 0x7ffd505af1f0
How can I print my string at the end, instead of a pointer to a mem address?
More interestingly, when I adjusted the final line to cout str1[n] instead of str1, the console prints the next character in the alphabet from the last input!
cout<<"Your word is: "<<str1[n];
and the output is
enter letter: a
enter letter: b
enter letter: c
Your word is: d
How is this happening?
When people say that strings are like arrays, they mean specifically "c-strings", which are just char arrays (char*, or char []). std::string is a separate C++ class, and is not like an array. See this question for a definition of C-strings.
In your example, str1 is actually an array of std::strings, and when you print it, you're printing the pointer address.
Below is an example using both C++ std::string, and a C-string, to illustrate the difference. In general, when writing C++, you should prefer std::string.
const int n = 2;
std::string str1; //A c++ std::string, which is not like an array
char cstr1[n+1]; // a c-string, which is array-like
for(int i = 0; i < n; ++i) {
char input = '\0';
cout<<"enter letter: ";
cin>>input;
str1.push_back(input); //Append to c++ string
cstr1[i] = input; //Add to array-like c-string
}
cstr1[n] = '\0'; //Ensure C-string is null-terminated
cout << "As C++: " << str1 << std::endl;
cout << "AS C: " << cstr1 << std::endl;
cout << "C++ can convert to C-string: " << str1.c_str() << std::endl;
Added const to n, to make it valid C++, since you shouldn't create arrays from non-const variables
Most likely you meant either string str1; or char str1[n]; (I suggest the first one, as the latter is a variable-length array supported only by compiler extensions, not a part of C++). string str1[n]; is an array of strings, which in generally decays to a pointer when passed around, so it happened in your case.
Should you decide to go with the std::string I suggest getting rid of i and n and rewriting it to sth like that:
while(str1.size() < 2){
cout<<"enter letter: ";
cin>>input;
str1.push_back(input);
}
Should you decide to stick to C-style char array (char str1[n];) I suggest making n a compile time constant, i.e. by defining it the following way: constexpr int n = 5; (provided you're on C++11 or newer).

How to locate and remove these symbols from my reversed string

This is an oddly specific problem but I need help because I am very confused. I am trying to use pointers to ask a user to input a string and the output will print the reverse. So far I have used a reverse function and applied the pointers. Here's what the code looks like right now:
#include <iostream>
using namespace std;
void reverse(char name[])
{
char *p;
p = name;
while (*p != '\0')
{
++p;
}
while (*p >= 0)
{
cout << *p;
--p;
}
}
int main()
{
char name[100];
cout << "Please enter a string: ";
cin.getline(name, sizeof(name));
cout << "The reverse of the string is: ";
reverse(name);
return 0;
}
When I run the program, it works but there is one problem. For example the inputted string is Stack Overflow, this is the result:
Please enter a string: Stack Overflow
The reverse of the string is: wolfrevO kcatS ►☺ ◄ a
As you can see there are these symbols that show up in the final output. I have tried locating where it comes from and I think it is because of the pointers because when I used an array for the function, it properly printed the reversed string without the symbols. I am asking if there is a way for me to remove these symbols while still using pointers? I have tried multiple variations on making the function with the pointers but the symbols still print at the end.
That garbarge happens because you don't have null terminating character at the beginning of the string, thus you don't terminate when going backwards. I modified your code to keep sentinel zero character at 0-th position, and now your code works without bugs.
Also condition while (*p >= 0) should be replaced with while (*p).
Try it online!
#include <iostream>
using namespace std;
void reverse(char name[])
{
char *p;
p = name;
while (*p != '\0')
{
++p;
}
--p;
while (*p)
{
cout << *p;
--p;
}
}
int main()
{
char name[100];
name[0] = 0;
cout << "Please enter a string: ";
cin.getline(name + 1, sizeof(name) - 1);
cout << "The reverse of the string is: ";
reverse(name + 1);
return 0;
}
Input:
Please enter a string: Stack Overflow
Output:
The reverse of the string is: wolfrevO kcatS
When you use
while (*p >= 0)
{
cout << *p;
--p;
}
you seem to assume that the space just before the beginning of the array is occupied by something negative; this is not a safe assumption, and the loop can iterate past that point, printing whatever binary junk happens to be in that region of memory. I say it can, because dereferencing a pointer into unallocated space like that is undefined behavior. It can do anything; it can terminate the loop so that the program appears to work correctly, it can print gibberish, it can crash you computer.
If you want to stop at the beginning of the given string, look for the beginning of the given string:
do
{
--p;
cout << *p;
}
while (p != name);
You're reading 100 characters into the string, which means there's a chance some trash input buffer values will be read too. This is where the symbols come from. Since you're using char arrays, maybe instead of getline use something like this:
char c = getchar();
int i = 0;
while(c != '\n'){
name[i] = c;
c= getchar();
i++;
}
name[i++] = '\0'
This way you'll only read what you need to read, and will have the terminating character '\0' at the end of the string. Bear in mind there's probably a cleaner solution using getline tho. Either way, the problem is that you're reading more values then you want to read into the char array, and since you're directly accessing memory you need to figure out a way to add a '\0' after the desired string, so the method knows when to stop - I'm guessing char arrays are implemented in such a way to secure this always happens, hence the reason it works with char arrays but not with pointers.

I am trying to count the contents in a char array till the null-termination, but each time I compile i get a number larger than my array

No matter what value I set for the size of my array, the function I wrote returns a value some degree larger.
I've tried, while(*str++) and removing the str++ from inside the while loop, instead of what is there now.
I am using Visual Studio 2019.
int strlen(char* str)
{
int i = 0;
while (*str != '\0')
{
i++;
str++;
}
return i;
}
int main()
{
char line[1];
char* v = line;
char* s = new char[1];
cout << "for s " << strlen(s) << endl;
cout << "for v " << strlen(v) << endl;
}
You are neglecting to null-terminate your strings. Your function is iterating past the end of the array and causing Undefined Behavior. Some string-manipulation functions will put the null at the end, but if you want your string to have a terminal null, you must put it there yourself.
char line[2];
char* v = line;
line[0]='x';
line[1]= '\0';
The contents of your arrays are undefined. You are not filling any of the arrays with any characters, let alone with any null terminators. It is undefined behavior to call strlen() on a character array that is not properly null terminated.

char array/string is empty when sent to function

I have a function that at the moment doesn't do anything because the char array I send as parameter has no value. I can write out the data before the function and get the expected output but if I write out in the function nothing come out even if It's the first thing I do.
I first write out the string and then convert it to a char array. I have also tried with just a normal string as parameter with the same result.
std::cout << block;
block = this->removeNullCharacters(block.c_str());
Output of the first cout is : "0/"
The output of the function below is nothing. Nothing at all is shown up in the console.
std::string FileSystem::removeNullCharacters(const char * input){
std::string out = "";
for(int i = 0; i < 512; ++i){
std::cout << i << ": " << input[i];
/*if(input[i] == '\0'){
return out;
}
else{
out += input[i];
}*/
}
return out;
}
Not clear at all what you try to achieve or, in other words, you question is not complete.
input is a pointer to char and points to the first element of an array of chars of size 512.
out is an empty string and returned at the end of the function.
block is schizophren, a pointer to char and a std::string at the same time i guess. You need to fix this one really.
Try this. I think you want to pass a pointer to a sequence of chars to the function and concatenate each char to the string until the end of the sequence (which is terminated by \0). Forget about the size completely and simply use the null termination as a loop condition.
std::string FileSystem::removeNullCharacters(const char * input){
std::string out = "";
for(int i = 0; input[i] != '\0'; ++i){
std::cout << i << ": " << input[i];
out += input[i];
}
return out;
}
But you gotta make sure you pass in an array of chars or a valid pointer to char (which is the beginning of a sequence of chars and the last element is \0).

How do I combine two strings?

The question requires combining two strings(the longer string in the front and the shorter one after the longer one) without using <string> header file.Each string inputted can't exceed 20 characters.
My logic behind this is:
first use strlen to get the length of the str1 and str2,
use str3 to store the longer string, and str4 to store the shorter.
add str3 and str4 to str5
Here is my code:
#include<iostream>
using namespace std;
int main()
{
// combine two strings , longer in the front, and shorter
// after it. do not use strcat
char str1[20],str2[20],str3[20],str4[20],str5[40];
// str1 and str2 stores original data, str3 stores longer
// and str4 stores shorter, str5 stores total
int j=0;
cin.getline(str1,20);
cin.getline(str2,20);
if(strlen(str1)<=strlen(str2))
// give longer string value to str3,shorter to str2
{
for (int i=0;i<20;i++)
{
str3[i]=str2[i];
str4[i]=str1[i];
}
}
else
{
for (int i=0;i<20;i++)
{
str3[i]=str1[i];
str4[i]=str2[i];
}
}
for(j=0;str3[j]!='\0';j++)
{
str5[j]=str3[j];
}
for(int i=j;i<40;i++)
for(int m=0;m<20;m++)
{
str5[i]=str4[m];
}
cout<<str5<<endl;
return 0;
}
Here is the ouput:
What's my problem here? What are those characters in between the two strings? Thank you!!
Especially since you explicitly mentioned being a beginner, the solution is to use std::string:
#include <iostream>
#include <string>
int main() {
std::string a;
getline(std::cin, a);
std::string b;
getline(std::cin, b);
// Ensure that the longer string goes to the front.
if (a.size() < b.size());
swap(a, b);
std::string result = a + b;
std::cout << result << '\n';
// Or, simply:
std::cout << a << b << '\n';
}
The message here is that C++, despite its quirks, is a very high level language if you rely on its library instead of implementing every low level operation from scratch.
Everything is fine (!) up to this point
for(int i=j;i<40;i++)
for(int m=0;m<20;m++) // This loop runs m=0 to 20 for each position of i
{
str5[i]=str4[m];
}
For each index i you are copying in all 20 elements from str4, leaving just the value at str4[19] which could be anything
Just increment i and m by one together
int m = 0;
for(int i=j;i<40;i++)
{
str5[i]=str4[m++];
}
You are copying the entire 20 characters, 40 characters in the loop into the variables. stop copying when you find a '\0' character.
But using the std::string will make life simpler :)
Using std::string is nice and all but here's a few tips for working with char*:
1) You shouldn't copy strings to separate shorter and longer string, just use pointers and then work with these pointers, something along these lines:
const char *longer_string = 0, *shorter_string = 0;
if(strlen(str1)<=strlen(str2))
{
shorter_string = str1;
longer_string = str2;
}
else
{
shorter_string = str2;
storter_string = str1;
}
2) Using strcpy and strcat to combine strings could make life a lot easier:
char *combined_string = new char [strlen (shorter_string) + strlen (longer_string) + 1];
strcpy (combined_string, longer_string);
strcat (combined_string, shorter_string);
Some compilers would say that these functions aren't safe and you have to stick to _s versions, but I guess it's entirely up to you.
Since this is obviously homework: I'll just point out the existence of the function strcat, and the fact that you can use char* to the arrays, and just swap them, without having to recopy anything between the initial read and the concatenation (which means that you only need two arrays: one for each of the inputs, and one for the final value).
And also, when calculating sizes, etc. do not forget that C style strings have an extra '\0' at the end, and make allowances for it.
As #David Sykes has pointed out, the problem is with your for loop. So when you read input from cin ,it is not necessary that your input string contains 20 character. But in you form loop you are looping through those string beyond their length which may contains garbage characters. Example
char str1[20]
cin.getline(str1,20);
cout << str1[19] << endl;
Suppose your input for above code is "ABCD" which contains only 4 characters but your array has capacity of 20. So the remaining space has junk characters and when you will try to print any thing beyond actual length you will get wild character as you are getting in your code.