strpbrk doesn't work - c++

char operators[] = "+-*/^(";
char* input = new char[100];
char* output = new char[100];
char* operadores = new char[100];
char* pch = input;
char* pch2 = input;
cout << "Expresion: " <<endl; cin.getline(input,100);
cout << input <<endl;
pch2 = strpbrk (pch2, operators);
pch = strtok (pch, "+-*/^(");
while (pch != NULL){
strcat (output, pch);
pch = strtok (NULL, "+-*/^(");
strcat (operadores, pch2);
}
cout << "Salida: " << output <<endl;
cout << "Operadores: " << operadores <<endl;
cout << "Entrada: " << input <<endl;
cout << "pch2 = " << pch2 <<endl;
Hi! my problem is that the function strpbrk doesnt work, it doesn't return NULL, I've proved it. But I need a char to put in a stack, and cout doesn't show me what character is pointed by pch2.

You are confusing yourself - the program is designed to confuse.
You have both pch and pch2 pointing at the same input string - input. You call strpbrk() to find one of the operators, saving that position in pch2. You then call strtok() on pch, and it finds the character that strpbrk() just found, and writes a NUL '\0' over it. So, it appears that pch2 points to the NUL at the end of a string. In the body of the loop, you then concatenate the empty string that pch2 points to onto your target list of operators.
Personally, I avoid using strtok() precisely because it mangles the input string. If you are going to use it, you are likely to need to work on a duplicate of the string, because strtok() writes NUL bytes over it.
Your diagnostic output at the end should show that the first section of the input - up to the first operator - only.
Beware of casting aspersions at standard library functions or compilers 'not working'. It is the mark of a tyro; 99.9999% of the time, it is a user error and not a system error. On those very, very, very rare occasions when you're correct (ooh, look - I've just won my third multi-million lottery prize; that's more likely, even though I don't buy lottery tickets), the way that you describe the problem is different. You describe the issue as one of utter astonishment; you document the working test cases; then you explain how the edge case you've found should work and the result - and you still aren't sure whether it is a bug in your code or in the system.
As others diagnosed, you do not initialize operadores to an empty string, so concatenating to it leads to undefined behaviour.
There's really no need to allocate the 100-byte strings:
char input[100]; // Cleaner, simpler, more reliable

Related

Why a dynamically allocated char cannot be outputted?

With reference to this thread: What's the difference between new char[10] and new char(10)
I've this code snippet:
char *temp1 = new char(10);
printf("%s", temp1);
std::cout<< "tmp is" << temp1 << std::endl;
It dynamically allocates a single char initialized with an integer value of 10 and prints its value.
Why the code didn't output anything?
thk U all guys, I finally figure it out.
A single char is not a string, and a pointer to a single char is not a string. Then you have the extra complication that 10 is the ASCII code for \n so you wouldn't be able to see anything even if your code was correct.
This code works, although it's not a good idea to mix C I/O and C++ I/O.
char *temp1 = new char(65); // ASCII for 'A'
printf("%c", *temp1);
std::cout<< "tmp is" << *temp1 << std::endl;

Why allocating memory to a char pointer does not truncate the char sequence

I can't understand why char *ptr = new char[7] would not truncate an input of data bigger than 7 characters. Also why eighter char c[7] would let me input more than 6 characters (but giving error when attributing it a literal value of more than 6 characters).
Doing it with malloc function seems a little bit to hard for me for a moment, this is why i prefer not to use it. I would prefer for the moment not to use it.
char qs[7] = "1234567"; //error too many
char qs2[7];
cin >> qs2; //input 123456789
cout << qs2; //out same as input, expecting 123456
char *qs3 = new char[7];
cin >> qs3; //input 123456789
cout << qs3; //out same as input, expecting 123456
The input stream, currently, only takes a pointer as an argument. Therefore it cannot know the size of the buffer that it fills. Therefore it cannot know whether it should truncate or not. Reading a string longer than the buffer will cause the buffer to overflow, and behaviour of the program will be undefined. Don't do it.
Since C++20, the array operand is passed by reference, and the operation does know the size and will truncate the input. This won't help in the case of qs3 however, since it is only a pointer rather than an array.
Instead, you can use:
std::cin.get(qs3, 6);
qs3[6] = '\0';
To ensure that no more characters are read than fit the buffer.
Or if you prefer to not truncate input, then you can read into std::string.
Doing it with malloc function seems a little bit to hard for me for a moment, this is why i prefer not to use it.
Good. It wouldn't solve your problem, and there is no need to use it, nor is there any advantage to using it either.
The both code snippets
char qs2[7];
cin >> qs2; //input 123456789
cout << qs2; //out same as input, expecting 123456
char *qs3 = new char[7];
cin >> qs3; //input 123456789
cout << qs3; //out same as input, expecting 123456
have undefined behavior. The memory beyond the allocated arrays is overwritten. The consequence can be of any kind.
Consider the following demonstrative program.
#include <iostream>
int main()
{
char gs1[7] = "123456";
char gs2[7];
char gs3[7] = "ABCDEF";
std::cin >> gs2;
std::cout << '\n';
std::cout << gs1 << '\n';
std::cout << gs2 << '\n';
std::cout << gs3 << '\n';
return 0;
}
If to enter
1234567
then the program output might look like
123456
1234567
As you can see the string "ABCDEF" was not outputted. It is the result of that the terminating zero '\0' that was appended to the array gs2 after this statement
std::cin >> gs2;
overwrites the first character of the array gs3. Now it content looks like
{ '\0', 'B', 'C', 'D', 'F', '\0' }
So as the first element of the array is the terminating zero then the empty string was being outputted in this statement
std::cout << gs3 << '\n';
The C-strings are zero-terminated, that mean you should always allocate buffer with size string length + 1.
char qs[7] = "1234567"; //error too many
In statically allocated buffers it is obvious for the compiler that your buffer does not have space for terminating zero. This should be char qs[8].
In both other examples, the operator takes pointer to buffer as argument and there is no way for it to know how large it is. It just fill it until the end of input. You get classic buffer overrun situation, and you are lucky that there is nothing important out there (after the buffer boundaries).

Conversion from string constant, pointers in c++

After reading several answers I have corrected my code to as follows;
int main()
{
// a pointer to char is initialized with a string literal
char Buffer[100];
cout << "Enter an initial string: " << endl;
cin >> Buffer;
cout << "Original content of Buffer:--> " << Buffer << endl;
cout << "Enter a sentence: " << endl;
cin >> Buffer;
gets(Buffer);
cout << "Now the Buffer contains:--> " << Buffer << endl;
return 0;
}
I know longer have the warning code, but now the program doesnt execute as I would like. The last part does not output my new sentance.
I know people mentioned not to use gets, but I tried using getline, obviously I cant use it as a direct replacement so I was a bit lost.
Any suggestions
You cannot read into a memory which contains string constant. Often those string constants are stored in read-only memory and even if not, they can share the constants so you would override one string for all parts of your code.
You need to copy the string into some buffer and then do whatever you want. For example:
const char *myOrigBuffer = "Dummy string";
char buffer[1024];
strcpy(buff, myOrigBuffer);
....
gets(buff);
You cannot modify string literral. Your way of coding is too much "C style".
If the original buffer content doesn't matter and you must use gets(), don't initialize your buffer :
char Buffer[100];
cout << "Enter a sentence: " << endl;
gets(Buffer);
cout << "Now the Buffer contains:--> " << endl;
cout << Buffer << endl;
Don't forget that if you input more than 100 characters (as the size of the buffer), this will also crash.
As gets is deprecated, fgets must be encouraged : it protects you from overflows. You should code this way in C-Style :
char buffer[10];
printf("Your sentence : \n");
fgets(buffer, sizeof buffer, stdin);
printf("Your sentence : %s\n", buffer);
Ouputs :
Your sentence :
1 34 6789012345
Your sentence : 1 34 6789
Nonetheless, you should consider using std::cin with std::string to make correct C++ inputs :
std::string sentence;
std::cout << "Enter a sentence (with spaces): ";
std::getline(std::cin, sentence);
std::cout << "Your sentence (with spaces): " << sentence << std::endl;
Outputs :
Enter a sentence (with spaces): My super sentence
Your sentence (with spaces): My super sentence
A string literal like "Dummy content." is logically const, since any operation that attempts to change its contents results in undefined behaviour.
The definition/initialisation
char *Buffer = "Dummy content.";
however, makes Buffer a non-const pointer to (the first character of) a string literal. That involves a conversion (from array of const char to a char *). That conversion exists in C for historical reasons so is still in C++. However, subsequently using Buffer to modify the string literal - which is what gets(Buffer) does unless the user enters no data - still gives undefined behaviour.
Your "stopped working" error is one manifestation of undefined behaviour.
Giving undefined behaviour is the reason the conversion is deprecated.
Note: gets() is more than deprecated. It has been removed from the C standard, from where it originated, completely because it is so dangerous (no way to prevent it overwriting arbitrary memory). In C++, use getline() instead. It is often not a good idea to mix C I/O function and C++ stream functions on the same input or output device (or file) anyway.
char *Buffer = "Dummy content.";
You should use pointer on const char here because "Dummy content." is not a buffer but pointer on string literal that has type "array of n const char" and static storage duration, so cannot be changed through pointer. Correct variant is:
char const* Literal = "Dummy content.";
But you cannot use it as parameter for gets
gets(Buffer);
It is bad idea and should cause write access exception or memory corruption on writing. You should pass to gets a pointer to a block of memory where received string will be stored.
This block should have enough length to store whole string, so in general gets is unsafe, check https://stackoverflow.com/a/4309845/2139056 for more info.
But as temporary test solution you can use buffer on stack:
char Buffer[256];
gets(Buffer);
or dynamic allocated buffer:
char* Buffer= new char[256];
gets(Buffer);
//and do not forget to free memory after your output operations
delete [] Buffer;

Cannot compile a C++ program in Xcode, but it compiles fine in Visual Studio Express 2013

I am having an issue compelling a program in Xcode but it compiles fine in Visual Studio Express 2013.
This is actually an example that my professor in school typed out and is using to show us how tokens work in C++. I did not write this. The errors that I get are on the following lines:
while (tokenptr != '\0') // while tokenptr is not a null (then must be a token)
error is: "Comparison between pointer and integer('char' and 'int)"
tokenptr = strtok('\0', " ,.!?;:"); // get next token from null where left off
error is: "No matching function for call to 'strtok'"
I have the feeling that the issue is with '\0' since the program is highlighting that.
Can anyone please help me to understand the problem?
Thanks
/*
Exercise 3-12
This program will change an English phrase to Pig Latin
*/
#include<iostream>
#include<cstring>
using namespace std;
void plw(char []);
int main()
{
char sent[50], *tokenptr;
cout << "Enter a sentence: ";
cin.getline(sent, sizeof(sent), '\n'); // input up to size of cstring or until enter
tokenptr = strtok(sent, " ,.!?;:"); // get first token
while (tokenptr != '\0') // while tokenptr is not a null (then must be a token)
{
plw(tokenptr); // convert this cstring token to Pig Latin
cout << " "; // put space back in (old space was delimiter and removed)
tokenptr = strtok('\0', " ,.!?;:"); // get next token from null where left off
}
cout << endl;
}
// function to take each word token (or cstring or char array) and print as Pig Latin
void plw(char word[])
{
int x;
for (x = 1; x < strlen(word); x++)
cout << word[x];
cout << word[0] << "ay";
}
Xcode does not let you use '\0' in place of a null pointer. Technically, it should let you do it, and quietly cast to NULL. However, for readability you should really be passing NULL for null pointer, like this:
while (tokenptr != NULL)
or even skip the check altogether, like this:
while (tokenptr)
The second way to check for NULL is idiomatic to C/C++, but not everyone likes it, preferring to add != NULL anyway.
Similarly, you should pass NULL to strtok:
strtok(NULL, " ,.!?;:")
Finally, since strtok is not re-entrant, consider using strtok_r instead.

Read into std::string using scanf

As the title said, I'm curious if there is a way to read a C++ string with scanf.
I know that I can read each char and insert it in the deserved string, but I'd want something like:
string a;
scanf("%SOMETHING", &a);
gets() also doesn't work.
Thanks in advance!
this can work
char tmp[101];
scanf("%100s", tmp);
string a = tmp;
There is no situation under which gets() is to be used! It is always wrong to use gets() and it is removed from C11 and being removed from C++14.
scanf() doens't support any C++ classes. However, you can store the result from scanf() into a std::string:
Editor's note: The following code is wrong, as explained in the comments. See the answers by Patato, tom, and Daniel Trugman for correct approaches.
std::string str(100, ' ');
if (1 == scanf("%*s", &str[0], str.size())) {
// ...
}
I'm not entirely sure about the way to specify that buffer length in scanf() and in which order the parameters go (there is a chance that the parameters &str[0] and str.size() need to be reversed and I may be missing a . in the format string). Note that the resulting std::string will contain a terminating null character and it won't have changed its size.
Of course, I would just use if (std::cin >> str) { ... } but that's a different question.
Problem explained:
You CAN populate the underlying buffer of an std::string using scanf, but(!) the managed std::string object will NOT be aware of the change.
const char *line="Daniel 1337"; // The line we're gonna parse
std::string token;
token.reserve(64); // You should always make sure the buffer is big enough
sscanf(line, "%s %*u", token.data());
std::cout << "Managed string: '" << token
<< " (size = " << token.size() << ")" << std::endl;
std::cout << "Underlying buffer: " << token.data()
<< " (size = " << strlen(token.data()) << ")" << std::endl;
Outputs:
Managed string: (size = 0)
Underlying buffer: Daniel (size = 6)
So, what happened here?
The object std::string is not aware of changes not performed through the exported, official, API.
When we write to the object through the underlying buffer, the data changes, but the string object is not aware of that.
If we were to replace the original call: token.reseve(64) with token.resize(64), a call that changes the size of the managed string, the results would've been different:
const char *line="Daniel 1337"; // The line we're gonna parse
std::string token;
token.resize(64); // You should always make sure the buffer is big enough
sscanf(line, "%s %*u", token.data());
std::cout << "Managed string: " << token
<< " (size = " << token.size() << ")" << std::endl;
std::cout << "Underlying buffer: " << token.data()
<< " (size = " << strlen(token.data()) << ")" << std::endl;
Outputs:
Managed string: Daniel (size = 64)
Underlying buffer: Daniel (size = 6)
Once again, the result is sub-optimal. The output is correct, but the size isn't.
Solution:
If you really want to make do this, follow these steps:
Call resize to make sure your buffer is big enough. Use a #define for the maximal length (see step 2 to understand why):
std::string buffer;
buffer.resize(MAX_TOKEN_LENGTH);
Use scanf while limiting the size of the scanned string using "width modifiers" and check the return value (return value is the number of tokens scanned):
#define XSTR(__x) STR(__x)
#define STR(__x) #x
...
int rv = scanf("%" XSTR(MAX_TOKEN_LENGTH) "s", &buffer[0]);
Reset the managed string size to the actual size in a safe manner:
buffer.resize(strnlen(buffer.data(), MAX_TOKEN_LENGTH));
The below snippet works
string s(100, '\0');
scanf("%s", s.c_str());
Here a version without limit of length (in case of the length of the input is unknown).
std::string read_string() {
std::string s; unsigned int uc; int c;
// ASCII code of space is 32, and all code less or equal than 32 are invisible.
// For EOF, a negative, will be large than 32 after unsigned conversion
while ((uc = (unsigned int)getchar()) <= 32u);
if (uc < 256u) s.push_back((char)uc);
while ((c = getchar()) > 32) s.push_back((char)c);
return s;
}
For performance consideration, getchar is definitely faster than scanf, and std::string::reserve could pre-allocate buffers to prevent frequent reallocation.
You can construct an std::string of an appropriate size and read into its underlying character storage:
std::string str(100, ' ');
scanf("%100s", &str[0]);
str.resize(strlen(str.c_str()));
The call to str.resize() is critical, otherwise the length of the std::string object will not be updated. Thanks to Daniel Trugman for pointing this out.
(There is no off-by-one error with the size reserved for the string versus the width passed to scanf, because since C++11 it is guaranteed that the character data of std::string is followed by a null terminator so there is room for size+1 characters.)
int n=15; // you are going to scan no more than n symbols
std::string str(n+1); //you can't scan more than string contains minus 1
scanf("%s",str.begin()); // scanf only changes content of string like it's array
str=str.c_str() //make string normal, you'll have lots of problems without this string