Character const or string const? - c++

Here is a part of the code that seeks to display the names of people that have blood group as a+ or ab+.
//class definition
class bloodbank
{
int idno;
char doname[30];
char group[3];
//the function i am talking about
void blood()
{
bloodbank b;
int count =0;
ifstream fin("donor.dat",ios::binary);
if( (strcmp(b.retgroup(),'A+')==0) ||(strcmp(b.retgroup(),'AB+')==0 ) )
{
b.display();
count++;
fin.read((char*)&b,sizeof(b));
}
fin.close();
cout<<"No of records"<<" "<<count;
}
}
i am confused whether i have to put "A+" or just 'A+'.
if i put "A+", i get count as 0.

'A+' is a multicharacter literal in C++. It has an int type, but the value is implementation-defined, although 'A' * 256 + '+' is common.
As such they are rarely used.
You want "A+". That is a const char[3] type that decays to a const char* pointer which strcmp can use.
Finally, you're using C++. Why not use std::string rather than those char[] types? Then you could write b.retgroup() == "A+" and so on rather than those strcmp calls. Or if you need to retain the char[] for some reason, you could use, from C++14 onwards, the flashy Yoda-expression
"A+"s == b.retgroup()

Related

Why casting a quoted string to "string &" causes a crash?

Please note that's just a curious question, I don't need a problem solution!
I have a method that takes in a string reference (string &some_string) and only reads from referenced string. Writing some code I forgot that it needs a reference and passed a quoted string (not a variable, just like "something") and IDE suggested casting it to string reference, as it won't compile. I didn't give it a thought and applied the suggestion (now passing (string &) "something"). And my program crashed as it reached this piece of code. So, why exactly would it cause a crash, compiling without even a warning (g++, c++11)? I don't really understand it since I'm only reading from this string.
Example:
#include <string>
#include <iostream>
using namespace std;
class CharSet
{
private:
const string basis = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const int N = 26;
string array; // string containing all element of the set
int length;
bool add_element(char chr) // Add an element (as char) if not already. Returns true if added, false if not.
{
bool b = false;
if (!in_set(chr) && basis.find(chr) != string::npos) {
array += chr;
length++;
b = true;
}
return b;
}
bool in_set(char chr) const // Checks whether an element (as char) is in set. Returns corresponding bool value.
{
bool b = false;
if (array.find(chr) != string::npos) b = true;
return b;
}
public:
explicit CharSet(string& str) // str - string of possible elements
{
array = "";
length = 0;
int len = (int) str.length();
for (int i = 0; i < len; i++)
add_element(str[i]);
if (str.length() != length)
cout << "\nSome mistakes corrected. Elements read: " << array << endl;
}
};
int main()
{
CharSet A((string &)"AKEPTYUMRX");
getchar();
return 0;
}
A cast like this
(string)"a string literal"
constructs a new std::string by passing "a string literal" as an argument to its constructor. It is equivalent to static_cast<std::string>("a string literal"). It is completely valid, even though C-style casts in C++ are a bad idea in general.
On the other hand, a cast like this
(string&)"a string literal"
constructs a reference to an std::string object that resides at the same location as the string literal. Since there is no such object at that location, using this reference results in undefined behaviour (often expressed as a crash). This cast is equivalent to reinterpret_cast<std::string&>("a string literal"). You might know that reinterpret_cast is dangerous and should be avoided as much as possible. With reinterpret_cast, the compiler trusts the programmer nearly totally, which is not really a wise thing to do, but such is the C++ way.
For your function to be able to accept a string literal, it should have a const std::string& parameter, or perhaps better, if you are using C++17, an std::string_view.

warning: ISO C++ forbids converting a string constant to 'char*'

I just started learning C++, and during compilation my code I get an error:
main.cpp:59:50: warning: ISO C++ forbids converting a string constant to 'char*' [-Wwrite-strings]
Encryption delta("dragons.txt", "output1.txt");
I don't know what this error means, or how to make it work, so if somebody could explain to me why this is happening and how to fix that, I would be very grateful :)
#include <iostream>
#include <fstream>
using namespace std;
class Encryption
{
fstream file1; //source file
fstream file2; //destination file
public:
Encryption::Encryption(char *filename1, char *filename2)
{
file1.open(filename1, ios::in | ios::out | ios::binary);
file2.open(filename2, ios::out | ios::binary);
}
//encrypts the file
void Encrypt(void)
{
char currentByte;
bool currentBit;
int index = 0;
//sets the pointers to the beginning of the file
file1.seekg(0, ios::beg);
file2.seekp(0, ios::beg);
//reads the first value
file1.read(&currentByte, 1);
while (file1.good())
{
//loop for four bits
for (int c = 0; c < 4; c++)
{
//finds out if the first bit is a one
currentBit = (int)((unsigned char)currentByte / 128);
//shifts the byte over
currentByte <<= 1;
//if the first bit was a one then we add it to the end
if (currentBit)
{
currentByte += 1;
}
}
//writes the character
file2.write(&currentByte, 1);
//increments the pointer
file1.seekg(++index);
file2.seekp(index);
//reads the next value
file1.read(&currentByte, 1);
}
}
//closes both of the files
void close(void)
{
file1.close();
file2.close();
}
};
int main(void)
{
cout << "Welcome to the S.A.S encryption program.";
Encryption delta("dragons.txt", "output1.txt");
delta.Encrypt();
delta.close();
Encryption gamma("output1.txt", "output2.txt");
gamma.Encrypt();
gamma.close();
return 0;
}
To quote what is a literal type
Literal types are the types of constexpr variables and they can be constructed, manipulated, and returned from constexpr functions.
Essentially these are entities that can be used at compile time. Since they are constexpr, so for one thing, these are const, therefore, read-only. To fix that, you need to change to
Encryption(char const* filename1, char const *filename2)
Also, you don't need to scope the constructor of Encryption class with Encryption:: since it's defined within the class itself, so just remove that. Else your program won't compile.
Encryption::Encryption(char *filename1, char *filename2)
{
// ...
}
Since you don't intend to modify the characters which filename1, filename2 point to, you should declare them as const char *. Otherwise, informally speaking, the compiler is worried that you might do so, which would not be allowed for a string literal.
Also, since you are defining this function inside the definition of the class Encryption, you don't need to restate that with Encryption::. So change this line to
Encryption(const char *filename1, const char *filename2)
{
// ...
}
String literals are const char[] types in C++. Since C++11 onwards, string literals cannot be assigned to non-const char* pointers. Doing so would allow code to mutate a string literal's data, which is undefined behavior. This assignment was allowed prior to C++11, for backwards compatibility with C, but it was always discouraged.
If you want to pass string literals to Encryption(), you need to change the types of its parameters to const char* (or char const *) instead:
Encryption::Encryption(const char *filename1, const char *filename2)
{
file1.open(filename1, ios::in | ios::out | ios::binary);
file2.open(filename2, ios::out | ios::binary);
}
Especially since that is what fstream::open() takes in anyway, and you are not altering that parameter in any way:
void open( const char *filename,
ios_base::openmode mode = ios_base::in|ios_base::out );
As the warning tells you, converting a string constant to char* - which is not a constant - is not allowed.
The reason for this is that you can theoretically change the contents of a char*. But string constants (literals) are read-only.
Therefore you should use const char* as parameter type in your method.

comparing s.at(i) to a character?

I'm getting an error that C++ forbids comparison of pointer with character
some sort of -fpermissive error wherever i compare s.at(i) with anything.
string s;
cout<<"Enter the expression";
getline(cin,s);
int i;
for(i=0;i<s.length();i++)
{
if(s.at(i)=="("||s.at(i)=="["||s.at(i)=="{")
push(s.at(i));
}
for(i=0;i<s.length();i++)
{
if(s.at(i)=="]"||s.at(i)==")"||s.at(i)=="}")
{
x=pop();
if (s.at(i)==x)
continue;
else
{enter code here
cout<<"\nInvalid expression";
return 0;
}
}
}
Consider s.at(i)=="(".
The literal "(" is actually a const char[2] type (one element for the (, the other for a NUL-terimator), which decays into a const char* pointer under certain instances, such as this case when using ==.
s.at(i) returns a single char type, which is being compared to a const char* pointer. Your helpful compiler is warning you of this error.
The solution is simple: use '(' instead, which is a single char literal. You can compare a char to a char.
You are encasing characters in double quotes ")" which makes them strings, or char *'s. They should be in single quotes, like ')' so they become char's.

Automatically Concatenate Strings and Int C++

In Lua (apologise, I like working with it the best), the conversion between int and string is done automatically, so
"hi"..2
would result as
"hi2"
In C++ (cause I can't seem to get the default C++11 stoi() and to_string() methods to work) I defined these for myself:
int stoi(string str) {
char* ptr;
strtol(str.c_str(), &ptr, 10);
}
string to_string(int i) {
char* buf;
sprintf(buf, "%d", i);
return buf;
}
which are basically how the default ones are defined anyways.
Then I did this:
string operator+ (string& stuff, int why) {
stuff.append(to_string(why));
}
I tried it on the following code:
void print(string str) {
cout << str << endl;
}
int main() {
cout << stoi("1") + 2 << endl;
print("die" + 1);
return 0;
}
And it outputs
3
ie
Why is this so, and how can I fix it?
EDIT:
Here's what the code looks like now:
using namespace std;
string to_string(int i) {
char* buf;
sprintf(buf, "%d", i);
return buf;
}
string operator+ (string stuff, int why) {
stuff.append(to_string(why));
return stuff;
}
int main() {
cout << string("die") + 2 << endl;
return 0;
}
And it just keeps giving me stackdumps.
Replace print("die" + 1); with cout << std::string("die") + 1;
print() doesn't know what to do with strings. Use std::cout. "die" is a char*, +1 will increment the pointer.
std::string to_string(int i) {
char buf[(sizeof(int)*CHAR_BIT+2)/3+3];
sprintf(buf, "%d", i);
return buf;
}
You need to make an actual buffer to print to. The math is a quick over-estimate of big the largest decimal int is in characters; 3 bits can fit in 1 decimal character, plus null, plus negation, plus rounding, plus 1 for good measure. Hopefully I did not err: do some testing.
Also use snprintf instead of sprintf while you are at it: buffer overflows are not to be toyed with.
The next problem is that "hello" is not a std::string, It is a char const[6] -- an array of 6 char. It can be converted tomstd::string, but +1 will instead convert it to a pointer to the first character, then +1 it to the 2nd character.
Cast it to std::string before.
Finally, it is ambiguous in the standard (really) of pverloading an operator on std::string + int is legal. It is definitely poor practice, as you cannot do it in std legally, and you should overload operators in the type's namespace (so ADL works): these two conflict. On top of that, if std in the future adds such a + your code starts behaving strangely. On top of that, operators are part of a class's interface, and modifying the interface of a class you do not 'own' is rude and a bad habit.
Write your own string class that owns a std::string rather. Or a string view.
Finally, consider telling your compiler to use c++11, you probably just need to pass a flag to it like -std=c++11.
std::string s1("h1");
std::string s2("2");
s1 += s2;
If you are using C++11 compatible compiler you can convert int to string like this:
int i = 2;
std::string s = std::to_string(i);
If you are using Boost library:
#include <boost/lexical_cast.hpp>
int i = 2;
std::string s = boost::lexical_cast<std::string>(i);
Please do not use raw char pointers in C++ for strings.
overloading the operator+ on other than your own types it at best dangerous.
Just use std::to_string in conjunction with operator+ or +=, e.g.
std::string x = "hi";
x += std::to_string(2);
C++14 introduces a user-defined literal that takes a string literal (conversions are applied to make this a pointer) and returns a std::string. In C++11, you can just write your own (this is taken from libstdc++):
inline std::string
operator""_s(const char* str, size_t len)
{
return std::string{str, len};
}
(Note: UDLs without a preceding underscore are reserved names)
And you can use it like this:
// Assumes operator+ is overloaded
print("die"_s + 1);
Demo
"die" is not a std::string. It's a string literal.
Thus when you add 1 to the string literal, it decays to a const char* and the + 1 simply increments that pointer — to next char, 'i'.
Then you call print with the incremented pointer, which causes a std::string to be constructed using that pointer. Since it pointed to the 'i' character, to constructed string is initialized to "ie".
You must first make a std::string out of your string literal to make it call your operator+:
std::cout << std::string("die") + 1;
And then make a few fixes to your operator+:
string operator+ (string stuff, int why) {
return stuff.append(to_string(why));
}
Now it works.

a value of type "const char *" cannot be assigned to an entity of type "char" C OOP

I am creating a class to calculate a grade for a user in C++ and I am coming across a simple yet annoying problem. I know what the errors means but I don't understand how to fix it and changing to a string actually fixes the issue but this is not what I want to do.
here is the error: const char *" cannot be assigned to an entity of type "char
Code
#include <string>
using namespace std;
class Gradecalc
{
public:
Gradecalc()
{
mark = 0;
}
int getmark()
{
return mark;
}
void setmark(int inmark)
{
mark = inmark;
}
void calcgrade()
{
if (mark >=70)
{
grade = "A"; //**ERROR IS HERE**
}
}
char getgrade()
{
return grade;
}
private:
int mark;
char grade; //VARIABLE IS DECLARED HERE
};
C++ has two types of constants consisting of characters - string literals and character literals.
String literals are enclosed in double quotes, and have type of const char *
Character literals are enclosed in single quotes, and have type char.
String literals allow multiple characters; character literals allow only one character. The two types of literals are not compatible: you need to supply a variable or a constant of a compatible type for the left side of the assignment. Since you declared grade as a char, you need to change the code to use a character literal, like this:
grade ='A';
C and C++ use double quotes to indicate "string literal", which is very different from a "character literal".
char (which is signed) is a type capable of storing a character representation in the compiler's default character set. On a modern, Western PC, that means ASCII, which is a character set that requires 7-bits, plus one for sign. So, char is generally an 8-bit value or byte.
A character literal is formed using single quotes, so 'A' evaluates to ASCII code 65. ('A' == 65).
On the other hand, "A" causes the compiler to write char(65) + char(0) into a part of the output program and then evaluates the expression "A" to the address of that sequence; thus it evaluates to a pointer to a char sequence, but they're in the program data itself so they are not modifiable, hence const char*.
You want
grade = 'A';
Replace
grade = "A";
by
grade = 'A';
You can only assign char to char, you cannot assign string to single char, and that is what you are trying to do.
Grade is a char variable, "A" is a const char* type.
You cannot assign const char* into char varible.
double quote means const char*, and single qoute means char.
to fix that, replace:
grade="A"
with
grade='A'.