Need to use cstrings for an assignment, so bear with me as I'm new to the concept.
The purpose of the assignment is to enter a password into a function, validate it against some criteria, then reverse the password and validate it again. There are three functions in total: one to get the password from the user, one for validation, and one to reverse what was entered.
Note: during the validation, each criteria not met has to be addressed in the invalid message returned to main.
Main:
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstring>
#include <cctype>
#include <string>
using namespace std;
char* Get_Pass(char p_word[]);
char* Valid_Pass(char p_word[]);
//char* Reverse_Pass(char p_word[]);
int main()
{
const int SIZE = 31;
char password[SIZE];
char* rtn_stg = new char[SIZE];
char* rtn_ptr;
rtn_stg = Get_Pass(password);
rtn_ptr = rtn_stg;
cout << Valid_Pass(rtn_stg);
//cout << Reverse_Pass(rtn_string, SIZE);
delete[] rtn_ptr;
cout << "\n\n";
system("pause");
return 0;
}
The issue I'm experiencing is twofold; the first function works fine. If I disabled everything else and sent the result back to main, it would print what I entered. It's when passing the password into the second function for validation where it outputs garbage code, along with the message for the unmet criteria, for example:
"Enter password for verification: hello
════════════════════════════════════════════════════════════════════════════════
═══════════════════════════════════════════════²²²²▌▌▌▌▌·ò=uYPassword must be si
x characters long. Password must contain one digit."
Here's the function in question:
char* Valid_Pass(char p_word[])
{
int char_count = 0, digi_count = 0, up_count = 0, low_count = 0, index = 0;
char mess_1[] = "Password must be six characters long. ";
char mess_2[] = "Password must contain one digit. ";
char mess_3[] = "Password must contain one upper and lower case letter. ";
char mess_4[] = "Password valid.";
const int SIZE = strlen(mess_1) + strlen(mess_2) + strlen(mess_3) + 1;
char* out_message = new char[SIZE];
while (p_word[index] != '\0')
{
if (isalpha(p_word[index]))
char_count++;
if (isdigit(p_word[index]))
digi_count++;
if (isupper(p_word[index]))
up_count++;
if (islower(p_word[index]))
low_count++;
index++;
}
if (index < 6)
strcat(out_message, mess_1);
if (digi_count < 1)
strcat(out_message, mess_2);
if (up_count < 1 && low_count < 1)
strcat(out_message, mess_3);
//if(index )
//out_message = mess_4;
return out_message;
}
On top of that, upon execution, it triggers a break point at the "delete[] rtn_ptr".
Any insight into this would be appreciated. Like I said above, I'm new to cstrings and couldn't find an solution on here that addressed the problems I'm experiencing.
Thanks!
Your problem is caused by use of strcat with message_out as argument.
The arguments to strcat need to be null-terminated strings. In your case, you have allocated memory for message_out but you have not initialized it anything before the first call to strcat. As a consequence, your program has undefined behavior.
Make sure you initialize it to an empty string before using strcat.
out_message[0] = '\0';
if (index < 6)
strcat(out_message, mess_1);
if (digi_count < 1)
strcat(out_message, mess_2);
if (up_count < 1 && low_count < 1)
strcat(out_message, mess_3);
Related
My stringNAdd function will duplicate strncat (original). I cannot accept arrays as parameters, but pointers. I wonder if my code right?
Here is the fixed code:
#include <string>
#include <iostream>
using namespace std;
char *stringNAdd(char str1[], char str2[],size_t num);
int main()
{
char dest[50] = "Using strncat function,";
char src[50] = " this part is added and this is ignored";
cout<< strncat(dest, src, 20) << endl;
cout << stringNAdd(dest, src, 20) << endl;
cin.get();
return 0;
}
char *stringNAdd(char str1[], char str2[],size_t num){
size_t str1_len = strlen(str1);
size_t i;
for (i=0; i < num && str2[i] != '\0'; i++)
i==num;
str1[str1_len+i] = str2[i];
str1[str1_len+i] = '\0';
return str1;
}
Output:
Using strncat function, this part is added
Using strncat function, this part is added
The problem is that you don't do the test of both functions in the same conditions: once you've executed strncat(), the dest already contains the longer concatenated version.
The second problem is that dest was already enlarged by 15 chars. It has therefore an initial length of 38 chars + the null terminator before calling stringNAdd(). Adding 15 more chars result in a string of 53 chars plus a null terminator, which is 4 chars longer than your array. So you'll get a buffer overflow, hence memory corruption and undefined behavior.
But all this is related to the testing conditions: your clone works fine.
Suggestions:
Run your functions in distinct blocks, and define your testing variables local to that block:
{
char dest[50] = "Using strncat function,";
char src[50] = " this part is added and this is ignored";
cout<< strncat(dest, src, 15) << endl;
cout << strlen(dest)<<endl;
}
{
char dest[50] = "Using strncat function,";
char src[50] = " this part is added and this is ignored";
cout << stringNAdd(dest, src, 15) << endl;
}
Think of a more secure version of your function, in which you would have an additional argument with the total length of the destination array to prevent these errors. This would increase the security of your code. By the way, this is what Microsoft does with strncat_s().
Finally, you could ask your teacher why he/she still lets you work with cstrings, when there are the so much more convenient and secure std::string, and that he certainly could find more modern exercises with the same pedagogical benefits.
Here is equivalent based on https://opensource.apple.com/source/Libc/Libc-167/gen.subproj/i386.subproj/strncat.c
#include <iostream>
char *strnadd(char *dst, const char *src, size_t n)
{
// abort if source is empty
if (n != 0)
{
// copy pointers
char *d = dst;
const char *s = src;
// find end of destination str
while (*d != 0)
d++;
// start copying chars from source str to the end of destination str
// until either source string ends or number of chars copied
// destination string has to be long enough to accommodate source
do
{
if ((*d = *s++) == 0)
break;
d++;
}
while (--n != 0);
// add null termination
*d = 0;
}
// return the resulting string
return dst;
}
int main()
{
char strCat[50];
char strAdd[50];
strcpy(strCat, "string1");
strcpy(strAdd, "string1");
char const *str2 = "string2";
std::cout << strncat(strCat, str2, 6) << std::endl;
std::cout << strnadd(strAdd, str2, 6) << std::endl;
return 0;
}
Prints:
string1string
string1string
I am having trouble with a two-dimensional array comparison. I need to create a pseudo login system that asks the user for a username and password and then compares the input to a predefined list of usernames.
In the function, the predefined usernames are represented by the *s and the user input is *s1. When I try to compile it, this pesky trouble-maker appears:
68 D:\Personal\Dev-Cpp\projects\loginSysTest\main.cpp
invalid conversion from char' toconst
regarding the strncmp function in the if statement.
This is the code:
#define nameLenght 30
#define User 10
char usernames[User][User] = {{"student"}, {"admin"}, {"Deus Ex Machina"}};
//=====================================================================
int main(int argc, char *argv[])
{
char usernameInput[nameLenght + 1] = {0};
gets(usernameInput);
int login = compS(*usernames, usernameInput);
if(login == 0)
printf("Access Granted! \n");
else
printf("Access Denied!");
system("PAUSE");
return 0;
}
//=====================================================================
int compS(char *s, char *s1)
{
for(int k = 0 ;k < nameLenght; k++)
{
if(strncmp(s[k], s1, strlen(s1)) == 0)
return 1;
}
}
Thank you in advance.
Just use std::vector and std::string along the lines of:
std::vector<std::string> usernames({"student", "admin", "Deus Ex Machina"});
std::string input;
std::cin >> input;
if (std::find(begin(usernames), end(usernames), input) != end(usernames))
std::cout << "Access Granted!\n";
else std::cout << "Access Denied!\n";
Live demo
If you want (or need) to keep your C-style not-pretty-nor-very-safe code, you could write
strncmp(s + (k+nameLenght), s1, strlen(s1))
That would compile and perhaps work with some more efforts, but you'll have to correct the usernames sizes and actually call compS.
See that other answer for a safer C++-styled code.
There are a few ways you can accomplish this. This is the first way that comes to mind.
char* usernames[] = { "student" , "admin", "Deus Ex Machina", NULL };
int compS(char **s, char *s1)
{
for (int k = 0; s[k] != NULL; k++)
{
if (strncmp(s[k], s1, strlen(s1)) == 0)
return 0;
}
return 1;
}
You have to pass a pointer to a string to strncmp() so I changed your array to an array of pointers to strings. The array has a null pointer at the end to allow the loop to know when it has reached the end.
I'm making a class to delete repeated character from a random word. For example if the input is "aabbccddeeff", it should output "abcdef". However my output contains strange characters after "abcdef". The main.cpp file already exists as the requirements for creating the class. Please see the following codes:
main.ccp
#include <iostream>
#include "repeatdeletion.h"
using namespace std;
int main()
{
char* noRepeats;
int length;
string s;
cout<<"Enter a random word with repeating characters: ";
cin>>s;
RepeatDeletion d;
length=s.length();
noRepeats=d.deleteRepeats(s, length);
cout<<"Your word without any repeating characters: ";
for (int k=0; k<length; k++){
cout<<noRepeats[k];
}
cout<<endl;
delete [] noRepeats;
noRepeats=NULL;
return 0;
}
repeatdeletion.h
#ifndef REPEATDELETION_H
#define REPEATDELETION_H
#include <iostream>
using namespace std;
class RepeatDeletion
{
char* c;
char arr[128]={};
bool repeated;
bool isRepeated(char);
public:
RepeatDeletion();
~RepeatDeletion();
char* deleteRepeats(string, int);
};
#endif // REPEATDELETION_H
repeatdeletion.cpp
#include "repeatdeletion.h"
RepeatDeletion::RepeatDeletion()
{
repeated=false;
}
RepeatDeletion::~RepeatDeletion()
{
delete [] c;
c=NULL;
}
bool RepeatDeletion::isRepeated(char c){
bool repeated=false;
if (arr[c]>=1){
repeated=true;
arr[c]++;
}else{
arr[c]++;
}
return repeated;
}
char* RepeatDeletion::deleteRepeats(string str, int len){
c=new char[len];
int j=0;
for (int i=0; i<len; i++){
if (isRepeated(str[i])==false){
c[j]=str[i];
j++;
}
}
return c;
}
Your return character array is not null terminated.
The length function of string does not include \0.
You have two choices
Add null at the end of returned character array, and std::cout the char array directly (instead of char by char)
Output the final length of your char array, and use that as range to print it char by char
Your printing loop loops using the old and unmodified string length. That means you will go outside the characters you added to memory returned by deleteRepeats.
The easiest solution to handle this is to terminate the data as a proper string, and check for the terminator in the loop.
If you want to use a C-string array, they have a null terminator at the end. That means you'll want to (in deleteRepeats) define your character array one character larger than the length:
c=new char[len+1];
And, after the for loop, ensure you put that null terminator in:
c[j] = '\0';
Then, in your calling function, you can just do:
cout << noRepeats;
Even if you don't want to use C strings, you'll need to communicate the new length back to the caller somehow (currently, you're using the original length). The easiest way to do that is (IMNSHO) still using a C-style string and using strlen to get the new length (a).
Otherwise, you're going to need something like a reference parameter for the new length, populated by the function and used by the caller.
(a) But I'd suggest rethinking the way you do things. If you want to be a C++ coder, be a C++ coder. In other words, use std::string for strings since it avoids the vast majority of problems people seem to have with C strings.
That's because in your code you write the following:
cout<<"Your word without any repeating characters: ";
for (int k=0; k<length; k++){
cout<<noRepeats[k];
}
cout<<endl;
Here, length refers to the length of the original string (which you, by the way shouldn't pass to your deleteRepeats method). I would suggest you make deleteRepeats return a string and write something like this:
std::string noRepeats = d.deleteRepeats(s);
std::cout << "Your word without any repeating characters: ";
std::cout << noRepeats << std::endl;
C-style string (char *, if you insist) follow the convention that the last character is '\0', indicating that the string ends. You could also change deleteRepeats by appending '\0', i.e.
char* RepeatDeletion::deleteRepeats(string str){
c = new char[str.size() + 1];
int j = 0;
for (int i = 0; i < str.size(); i++){
if(isRepeated(str[i]) == false){
c[j] = str[i];
j++;
}
}
c[j] = '\0';
return c;
}
and in your main
std::cout << noRepeats << std::endl;
instead of the for loop. But really, you should use std::string, and if possible not mix it with char *. Hope that helps.
for(k=0;k<length;k++)
Here length should be the exact length of noRepeats, but not of s
so :
char* RepeatDeletion::deleteRepeats(string str, int len)
should return the length-after too
use std::unique it does what you want:
std::string s{};
std::cin>>s;
auto it = std::unique(std::begin(s), std::end(s));
s.resize(std::distance(std::begin(s),it));
std::cout << s;
the way it works is to go through the range begin to end and move all the remaining elements forward if the current element is equal to the next. It returns the position of the end of the new string (it in this example) but does not actually shorten the string so on the next line we shorten the string to the length equal to the distance of begin() to it.
see live at http://ideone.com/0CeaHW
I'm attempting to take an input from the command line and then convert it to lower case. To do this, I've written:
istream& operator>>(istream& is, Card& c)
{
static map<string,Card::Rank> mr = createmr();
static map<string,Card::Suit> ms = createms();
string srank, c1, ssuit;
if (is >> srank >> c1 >> ssuit)
{
if (c1 == "of")
{
string nsrank;
string nssuit;
for(unsigned int i = 0; i < srank.length(); i++) {
char temp = srank[i];
nsrank[i] = tolower(srank[i]);
}
It fails on the second iteration of that for loop (more precisely, it fails on nsrank[i] = tolower(srank[i]);). The error that is displayed is "string substring out of range" but I don't understand how this could be the case as there are definitely still characters left in the string.
To give an example:
If I enter "Ace of Spades" then it will iterate through the first time (when i=0) and transfer the 'a' fine. However, it then goes back through with i equaling 1 (which should refer to the 'c') but instead it tells me the substring is out of range (even though the assignment to the char temp works fine). During debugging, "nsrank" claims a size of 15 so I don't see how that could be out of range either....
The problem is that nsrank is an empty string, so accessing with operator[]....
If pos is not greater than the string length, the function never
throws exceptions (no-throw guarantee). Otherwise, it causes undefined
behavior.
This one worked for me: http://ideone.com/3LcYqv
#include <iostream>
using namespace std;
int main() {
string srank="Ace of Spades";
string nsrank;
nsrank.resize(srank.length());
for(unsigned int i = 0; i < srank.length(); i++) {
char temp = srank[i];
nsrank[i] = tolower(srank[i]);
}
cout << nsrank << endl;
return 0;
}
The key is the resize to make nsrank the same size as srank.
Edit: Added compact solution
From many places, among them from this answer
#include <algorithm>
#include <string>
string srank="Ace of Spades";
string nsrank=srank;
std::transform(nsrank.begin(), nsrank.end(),nsrank.begin(), ::toupper);
resize nsrank to match size of srank before entering the loop
I wrote this code to reverse strings. It works well, but when I enter short strings like "american beauty," it actually prints "ytuaeb nacirema2." This is my code. I would like to know what is wrong with my code that prints a random 2 at the end of the string. Thanks
// This program prompts the user to enter a string and displays it backwards.
#include <iostream>
#include <cstdlib>
using namespace std;
void printBackwards(char *strPtr); // Function prototype
int main() {
const int SIZE = 50;
char userString[SIZE];
char *strPtr;
cout << "Please enter a string (up to 49 characters)";
cin.getline(userString, SIZE);
printBackwards(userString);
}
//**************************************************************
// Definition of printBackwards. This function receives a *
// pointer to character and inverts the order of the characters*
// within it. *
//**************************************************************
void printBackwards(char *strPtr) {
const int SIZE = 50;
int length = 0;
char stringInverted[SIZE];
int count = 0;
char *strPtr1 = 0;
int stringSize;
int i = 0;
int sum = 0;
while (*strPtr != '\0') {
strPtr++; // Set the pointer at the end of the string.
sum++; // Add to sum.
}
strPtr--;
// Save the contents of strPtr on stringInverted on inverted order
while (count < sum) {
stringInverted[count] = *strPtr;
strPtr--;
count++;
}
// Add '\0' at the end of stringSize
stringInverted[count] == '\0';
cout << stringInverted << endl;
}
Thanks.
Your null termination is wrong. You're using == instead of =. You need to change:
stringInverted[count] == '\0';
into
stringInverted[count] = '\0';
// Add '\0' at the end of stringSize
stringInverted[count] == '\0';
Should use = here.
What is wrong with your code is that you do not even use strlen for counting the length of the string and you use fixed size strings (no malloc, or, gasp new[]), or the std::string (this is C++)! Even in plain C, not using strlen is always wrong because it is hand-optimized for the processor. What is worst, you have allocated the string to be returned (stringInverted) from the stack frame, which means when the function exits, the pointer is invalid and any time the code "works" is purely accidental.
To reverse a string on c++ you do this:
#include <iostream>
#include <string>
int main() {
std::string s = "asdfasdf";
std::string reversed (s.rbegin(), s.rend());
std::cout << reversed << std::endl;
}
To reverse a string in C99 you do this:
char *reverse(const char *string) {
int length = strlen(string);
char *rv = (char*)malloc(length + 1);
char *end = rv + length;
*end-- = 0;
for ( ; end >= rv; end --, string ++) {
*end = *string;
}
return rv;
}
and remember to free the returned pointer after use. All other answers so far are blatantly wrong :)