Could someone explain why those calls are not returning the same expected result?
unsigned int GetDigit(const string& s, unsigned int pos)
{
// Works as intended
char c = s[pos];
return atoi(&c);
// doesn't give expected results
return atoi(&s[pos]);
return atoi(&static_cast<char>(s[pos]));
return atoi(&char(s[pos]));
}
Remark: I'm not looking for the best way to convert a char to an int.
None of your attempts are correct, including the "works as intended" one (it just happened to work by accident). For starters, atoi() requires a NUL-terminated string, which you are not providing.
How about the following:
unsigned int GetDigit(const string& s, unsigned int pos)
{
return s[pos] - '0';
}
This assumes that you know that s[pos] is a valid decimal digit. If you don't, some error checking is in order.
What you are doing is use a std::string, get one character from its internal representation and feed a pointer to it into atoi, which expects a const char* that points to a NULL-terminated string. A std::string is not guaranteed to store characters so that there is a terminating zero, it's just luck that your C++ implementation seems to do this.
The correct way would be to ask std::string for a zero terminated version of it's contents using s.c_str(), then call atoi using a pointer to it.
Your code contains another problem, you are casting the result of atoi to an unsigned int, while atoi returns a signed int. What if your string is "-123"?
Since int atoi(const char* s) accepts a pointer to a field of characters, your last three uses return a number corresponding to the consecutive digits beginning with &s[pos], e.g. it can give 123 for a string like "123", starting at position 0. Since the data inside a std::string are not required to be null-terminated, the answer can be anything else on some implementation, i.e. undefined behaviour.
Your "working" approach also uses undefined behaviour.
It's different from the other attempts since it copies the value of s[pos]to another location.
It seems to work only as long as the adjacent byte in memory next to character c accidentally happens to be a zero or a non-digit character, which is not guaranteed. So follow the advice given by #aix.
To make it work really you could do the following:
char c[2] = { s[pos], '\0' };
return atoi(c);
if you want to access the data as a C string - use s.c_str(), and then pass it to atoi.
atoi expects a C-style string, std::string is a C++ class with different behavior and characteristics. For starters - it doesn't have to be NULL terminated.
atoi takes pointer to char for it's argument. In the first try when you are using the char c it takes pointer to only one character hence you get the answer you want. However in the other attempts what you get is pointer to a char which has happened to be beginning of a string of chars, therefore I assume what you are getting after atoi in the later attempts is a number converted from the chars in positions pos, pos+1, pos+2 and up to the end of the s string.
If you really want to convert just a single char in the string at the position (as opposed to a substring starting at that position and ending at the end of the string), you can do it these ways:
int GetDigit(const string& s, const size_t& pos) {
return atoi(string(1, s[pos]).c_str());
}
int GetDigit2(const string& s, const size_t& pos) {
const char n[2] = {s[pos], '\0'};
return atoi(n);
}
for example.
Related
So I have a function which edits the values, but I cout the values in main to see that it outputs 1309668848 and changes every time I run the program. (this isn't happening in the preprocessor). I have been struggling with this for a while and decided to come here for advice.
Here's the function.
void GetDahInt() {
std::string NewValueS;
getline(std::cin, NewValueS);
NewValue = (int)NewValueS.c_str();
}
You can use
std::stoi( str )
Discards any whitespace characters (as identified by calling isspace()) until the first non-whitespace character is found, then takes as many characters as possible to form a valid base-n (where n=base) integer number representation and converts them to an integer value.
Source :Documentation of stoi
NewValueS.c_str() returns a pointer to array of characters.
You are casting the pointer to int (get the memory address of c_str()).
See: http://www.cplusplus.com/reference/string/string/c_str/
string::c_str
Get C string equivalent
Returns a pointer to an array that contains a null-terminated sequence of characters (i.e., a C-string) representing the current value of the string object.
This array includes the same sequence of characters that make up the value of the string object plus an additional terminating null-character ('\0') at the end.
NewValue = (int)NewValueS.c_str(); cast the address to string, and the address changes every time you execute your code.
You're looking for something like std::stoi (string to int), called like this:
std::string str = ...;
int value = std::stoi(str);
The pros and cons of this option compared to others such as std::stringstream are discussed here:
How to parse a string to an int in C++?
The reason your cast produces a "random" number is that c_str returns a pointer to the start of an array of char.
Casting from a const char* to an int is undefined behavior (these types may not even have the same size), but will likely produce a memory address that depends on where your std::string is allocated.
I'm just starting c++ and am having difficulty understanding const char*. I'm trying to convert the input in the method to string, and then change the strings to add hyphens where I want and ultimately take that string and convert it back to char* to return. So far when I try this it gives me a bus error 10.
char* getHyphen(const char* input){
string vowels [12] = {"A","E","I","O","U","Y","a","e","i","o","u","y"};
//convert char* to string
string a;
int i = 0;
while(input != '\0'){
a += input[i];
input++;
i++;
}
//convert a string to char*
return NULL;
}
A: The std::string class has a constructor that takes a char const*, so you simply create an instance to do your conversion.
B: Instances of std::string have a c_str() member function that returns a char const* that you can use to convert back to char const*.
auto my_cstr = "Hello"; // A
std::string s(my_cstr); // A
// ... modify 's' ...
auto back_to_cstr = s.c_str(); // B
First of all, you don't need all of that code to construct a std::string from the input. You can just use:
string a(input);
As far as returning a new char*, you can use:
return strdup(a.c_str()); // strdup is a non-standard function but it
// can be easily implemented if necessary.
Make sure to deallocate the returned value.
It will be better to just return a std::string so the users of your function don't have to worry about memory allocation/deallocation.
std::string getHyphen(const char* input){
Don't use char*. Use std::string, like all other here are telling you. This will eliminate all such problems.
However, for the sake of completeness and because you want to understand the background, let's analyse what is going on.
while(input != '\0'){
You probably mean:
while(*input != '\0') {
Your code compares the input pointer itself to \0, i.e. it checks for a null-pointer, which is due to the unfortunate automatic conversion from a \0 char. If you tried to compare with, say, 'x' or 'a', then you would get a compilation error instead of runtime crashes.
You want to dereference the pointer via *input to get to the char pointed to.
a += input[i];
input++;
i++;
This will also not work. You increment the input pointer, yet with [i] you advance even further. For example, if input has been incremented three times, then input[3] will be the 7th character of the original array passed into the function, not the 4th one. This eventually results in undefined behaviour when you leave the bounds of the array. Undefined behaviour can also be the "bus error 10" you mention.
Replace with:
a += *input;
input++;
i++;
(Actually, now that i is not used any longer, you can remove it altogether.)
And let me repeat it once again: Do not use char*. Use std::string.
Change your function declaration from
char* getHyphen(const char* input)
to
auto hyphenated( string const& input )
-> string
and avoid all the problems of conversion to char const* and back.
That said, you can construct a std::string from a char_const* as follows:
string( "Blah" )
and you get back a temporary char const* by using the c_str method.
Do note that the result of c_str is only valid as long as the original string instance exists and is not modified. For example, applying c_str to a local string and returning that result, yields Undefined Behavior and is not a good idea. If you absolutely must return a char* or char const*, allocate an array with new and copy the string data over with strcpy, like this: return strcpy( new char[s.length()+1], s.c_str() ), where the +1 is to accomodate a terminating zero-byte.
I wrote some code to verify a serial number is alpha numeric in C using isalnum. I wrote the code assuming isalnum input is char. Everything worked. However, after reviewing the isalnum later, I see that it wants input as int. Is my code okay the way it is should I change it?
If I do need to change, what would be the proper way? Should I just declare an int and set it to the char and pass that to isalnum? Is this considered bad programming practice?
Thanks in advance.
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
bool VerifySerialNumber( char *serialNumber ) {
int num;
char* charPtr = serialNumber;
if( strlen( serialNumber ) < 10 ) {
printf("The entered serial number seems incorrect.");
printf("It's less than 10 characters.\n");
return false;
}
while( *charPtr != '\0' ) {
if( !isalnum(*charPtr) ) {
return false;
}
*charPtr++;
}
return true;
}
int main() {
char* str1 = "abcdABCD1234";
char* str2 = "abcdef##";
char* str3 = "abcdABCD1234$#";
bool result;
result = VerifySerialNumber( str1 );
printf("str= %s, result=%d\n\n", str1, result);
result = VerifySerialNumber( str2 );
printf("str= %s, result=%d\n\n", str2, result);
result = VerifySerialNumber( str3 );
printf("str= %s, result=%d\n\n", str3, result);
return 0;
}
Output:
str= abcdABCD1234, result=1
The entered serial number seems incorrect.It's less than 10 characters.
str= abcdef##, result=0
str= abcdABCD1234$#, result=0
You don't need to change it. The compiler will implicitly convert your char to an int before passing it to isalnum. Functions like isalnum take int arguments because functions like fgetc return int values, which allows for special values like EOF to exist.
Update: As others have mentioned, be careful with negative values of your char. Your version of the C library might be implemented carefully so that negative values are handled without causing any run-time errors. For example, glibc (the GNU implementation of the standard C library) appears to handle negative numbers by adding 128 to the int argument.* However, you won't always be able to count on having isalnum (or any of the other <ctype.h> functions) quietly handle negative numbers, so getting in the habit of not checking would be a very bad idea.
* Technically, it's not adding 128 to the argument itself, but rather it appears to be using the argument as an index into an array, starting at index 128, such that passing in, say, -57 would result in an access to index 71 of the array. The result is the same, though, since array[-57+128] and (array+128)[-57] point to the same location.
Usually it is fine to pass a char value to a function that takes an int. It will be converted to the int with the same value. This isn't a bad practice.
However, there is a specific problem with isalnum and the other C functions for character classification and conversion. Here it is, from the ISO/IEC 9899:TC2 7.4/1 (emphasis mine):
In all cases the argument is an int, the value of which shall be
representable as an unsigned char or shall equal the value of the
macro EOF. If the argument has any other value, the behavior is
undefined.
So, if char is a signed type (this is implementation-dependent), and if you encounter a char with negative value, then it will be converted to an int with negative value before passing it to the function. Negative numbers are not representable as unsigned char. The numbers representable as unsigned char are 0 to UCHAR_MAX. So you have undefined behavior if you pass in any negative value other than whatever EOF happens to be.
For this reason, you should write your code like this in C:
if( !isalnum((unsigned char)*charPtr) )
or in C++ you might prefer:
if( !isalnum(static_cast<unsigned char>(*charPtr)) )
The point is worth learning because at first encounter it seems absurd: do not pass a char to the character functions.
Alternatively, in C++ there is a two-argument version of isalnum in the header <locale>. This function (and its friends) do take a char as input, so you don't have to worry about negative values. You will be astonished to learn that the second argument is a locale ;-)
I got an unsigned char with the value of 1, I need to put this in a string like "1".
But if I try to put this directly into a stringstream it will get the value of char(1) and I need it to be "1".
I know that if I can get this into the function atoi it will return the "1" value.
But I tried to cast it to char and put it in the atoi function, but it throws an exception.
Already tried to put it in a string and them cast c_str() into atoi function, but without success yet.
If someone can help me I'll apreciate.
Simply cast a char to an int before inserting it into the std::stringstream:
ss << static_cast<int>(c);
This will treat the value of the char not as a character but as a numerical value.
I believe you're confusing two functions here.
You wish to convert an integer to a string.
atoi (ascii to integer) however takes a string and parses it into an integer, making "123" into 123.
You are looking for the itoa function here, which has this prototype:
char * itoa ( int value, char * str, int base );
In your case, this would look like this:
char Buf[10];
itoa(123, Buf, 10);
printf("%s", Buf); //Outputs 123
Please remember though, that itoa is not part of the standard, even though it is supported by some compilers. For a more standard-compliant version use:
sprintf(Buf, "%d", 123);
Of course all of this is plain C, but any C++ compiler will work with this all the same.
To convert a numeric value in the range [0,9] to the corresponding character, just add '0' to it. That's guaranteed to work on all systems.
so I am trying to insert the character, which i got from a string, to another string.
Here I my actions:
1. I want to use simple:
someString.insert(somePosition, myChar);
2. I got an error, because insert requires(in my case) char* or string
3. I am converting char to char* via stringstream:
stringstream conversion;
char* myCharInsert;
conversion << myChar //That is actually someAnotherString.at(someOtherPosition) if that matters;
conversion >> myCharInsert;
someString.insert(somePosition, myCharInsert);
4. Everything seems to be compiling successfully, but program crashes the gets to
conversion >> myCharInsert;
line.
5.I am trying to replace char* with string:
stringstream conversion;
char* myCharInsert;
conversion << myChar //That is actually someAnotherString.at(someOtherPosition) if that matters;
conversion >> myCharInsert;
someString.insert(somePosition, myCharInsert);
Everything seems to be OK, but when someAnotherString.at(someOtherPosition) becomes space, program crashes.
So how do I correctly do this?
There are a number of overloads of std::string::insert. The overload for inserting a single character actually has three parameters:
string& insert(size_type pos, size_type n, char c);
The second parameter, n, is the number of times to insert c into the string at position pos (i.e., the number of times to repeat the character. If you only want to insert one instance of the character, simply pass it one, e.g.,
someString.insert(somePosition, 1, myChar);
Simplest is to provide yourself with a function that turns a character into a string. There are lots of ways of doing this, such as
string ToStr( char c ) {
return string( 1, c );
}
Then you can simply say:
someString.insert(somePosition, ToStr(myChar) );
and use the function in other cases where you want a string but have a char.
Everything seems to be compiling successfully, but program crashes the gets to
conversion >> myCharInsert;
The problem is that you are trying to dereference(access) myCharInsert(declared as a char* ) which is pointing to a random location in memory(which might not be inside the user's address space) and doing so is Undefined Behavior (crash on most implementations).
EDIT
To insert a char into a string use string& insert ( size_t pos1, size_t n, char c ); overload.
Extra
To convert char into a std::string read this answer
You can try:
std::string someString{"abc"};
someString.push_back('d');