How do I write functions that accept unlimited arguments? - c++

I have only been able to find one way for functions to take a variable amount of arguments.
It's this way:
#include <iostream>
#include <stdarg.h>
using namespace std;
void Print(int argumentAmount, ... );
int main()
{
Print(5,11,22,33,44,55);
}
void Print(int argumentAmount, ... ){
va_list arguments;
va_start(arguments, argumentAmount);
int parameter;
for(int i = 0; i < argumentAmount; ++i ){
parameter = va_arg(arguments, int);
cout << parameter << endl;
}
va_end(arguments);
return;
}
2 Problems:
1.) I have to specify how many arguments I'm sending in- not desirable
2.) I can't figure out how to modify it so it will output strings.
Would something like this be possible without having to overload the function multiple times:
void Output(/*not sure how this would look*/);
int main(){
Output("hello","world");
Output("this","is","a","test");
Output("As","many","strings","as","you","want","may","be","passed","in");
return 0;
}
void Output(/*not sure how this would look*/){
//loop through each string passed in and output it
}
What about this:
void Capitalize(/*all passed by reference*/);
int main(){
string s1 = "hello";
string s2 = "world";
string s3 = "this";
string s4 = "is";
string s5 = "a";
string s6 = "test";
string s7 = "as";
string s8 = "many";
string s9 = "strings";
string s10 = "as";
string s11 = "you";
string s12 = "want";
Capitalize(s1,s2);
Capitalize(s3,s4,s5,s6);
Capitalize(s7,s8,s9,s10,s11,s12);
return 0;
}
void Capitalize(/*all passed by reference*/){
//capitalize each string passed in
}
All I can think to do is:
-overload the function multiple times
-have the function accept some type of container instead
If this is NOT POSSIBLE, could someone explain why the compiler is not capable of accomplishing a task like this.

With variadic templates in C++11, you can do something like this (see the result at ideone)
#include <string>
#include <iostream>
void Output() {
std::cout<<std::endl;
}
template<typename First, typename ... Strings>
void Output(First arg, const Strings&... rest) {
std::cout<<arg<<" ";
Output(rest...);
}
int main() {
Output("I","am","a","sentence");
Output("Let's","try",1,"or",2,"digits");
return 0;
}

Quick and simple answer.
For C++ you need to specify either the number of arguments or a sentinel value to indicate the end of arguments.
Your first example is a good example of specing the count, you could also do:
void Print(const char *arg, ... ){
va_list arguments;
for (va_start(arguments, arg); arg != NULL; arg = va_arg(arguments, const char *)) {
cout << arg << endl;
}
va_end(arguments);
}
Where your calling convention is:
Print("foo","bar",NULL);
If you want to take it to the next level, you can mix in a bit of the C Preprocessor and do:
#define mPrint(...) Print(__VA_ARGS__, NULL)
Now you can just say:
mPrint("fooo","bar");
And the macro will NULL terminate the call.

Instead of passing in the count, you can have a special "trailing" argument (either nullptr or a pointer to some hard-coded "magic" string) and your variable-argument functions should stop extracting more arguments once they see the trailing one. That can ease your coding a bit.
You could also pass pointers (references) to containers, containing (or pointing at/referencing) your strings. Anything that can somehow link all your individual arguments will do (e.g. a vector).
Example (might be not very idiomatic, but should serve as an illustration):
#include <iostream>
#include <string>
#include <cstdarg>
#include <cctype>
#include <vector>
using namespace std;
void AntiCapitalize(vector<string*>& v);
void Capitalize(string* s, ...);
void Print(string* s, ...);
int main()
{
string s1 = "hello";
string s2 = "world";
string s3 = "this";
string s4 = "is";
string s5 = "a";
string s6 = "test";
string s7 = "as";
string s8 = "many";
string s9 = "strings";
string s10 = "as";
string s11 = "you";
string s12 = "want";
Capitalize(&s1, &s2, 0);
Capitalize(&s3, &s4, &s5, &s6, 0);
Capitalize(&s7, &s8, &s9, &s10, &s11, &s12, 0);
Print(&s1, &s2, 0);
Print(&s3, &s4, &s5, &s6, 0);
Print(&s7, &s8, &s9, &s10, &s11, &s12, 0);
vector<string*> v;
v.push_back(&s1);
v.push_back(&s2);
v.push_back(&s3);
v.push_back(&s4);
v.push_back(&s5);
v.push_back(&s6);
v.push_back(&s7);
v.push_back(&s8);
v.push_back(&s9);
v.push_back(&s10);
v.push_back(&s11);
v.push_back(&s12);
AntiCapitalize(v);
Print(&s1, &s2, 0);
Print(&s3, &s4, &s5, &s6, 0);
Print(&s7, &s8, &s9, &s10, &s11, &s12, 0);
return 0;
}
void Capitalize(string* s, ...)
{
va_list ap;
va_start(ap, s);
while (s)
{
string::size_type i = 0;
while ((*s)[i] != '\0')
{
(*s)[i] = toupper((*s)[i]);
i++;
}
s = va_arg(ap, string*);
}
va_end(ap);
}
void Print(string* s, ...)
{
va_list ap;
va_start(ap, s);
while (s)
{
cout << *s << endl;
s = va_arg(ap, string*);
}
va_end(ap);
}
void AntiCapitalize(vector<string*>& v)
{
vector<string*>::iterator it;
for (it = v.begin(); it != v.end(); it++)
{
string::size_type i = 0;
while ((**it)[i] != '\0')
{
(**it)[i] = tolower((**it)[i]);
i++;
}
}
}
Output:
HELLO
WORLD
THIS
IS
A
TEST
AS
MANY
STRINGS
AS
YOU
WANT
hello
world
this
is
a
test
as
many
strings
as
you
want

I think there is another possible solution:
You could overload an operator '<<' like this:
class OutputObject {
public:
// Some class functions/members
};
template<class T>
static operator << (OutputObject& out, T temp) {
cout << temp;
}
static OutputObject Obj = OutputObject();
And then you can do the following in the main:
#include "OutputObject.hpp"
#include <string>
using namespace std;
int main(void) {
string str = "Hello World";
Obj << 12 << str << 3.14f << "C++";
Obj << 12;
Obj << str;
return(0);
};
If I did something wrong or there is a reason not to that please
tell me, that was just my Idea of infinite parameters.
I was not able to test it yet, but I think it should work.

Related

How to use pointer to string in cpp?

I am studying pointers in C++. I have studied call by value and call by reference concept. I am trying to create a function to reverse a string which accepts a pointer to string and the size of string. The code is as follow
void reverse(string* str, int size)
{
int start = 0;
int end = size - 1;
while(start < end)
{
swap(*str[start++], *str[end--]);
}
}
int main()
{
string str = "Something";
reverse(&str, str.length());
cout << "Reversed string: " << str << endl;
return 0;
}
I am getting this error:
error: no match for ‘operator*’ (operand type is ‘std::string’ {aka
‘std::__cxx11::basic_string’})
12 | swap(*str[start++], *str[end--]);
I don't want to use the character array, is there way to do it?
Someone please explain, what's wrong in my code. Thank you.
Here is the simple fix. You don't need to change anything except a few lines.
#include <iostream>
#include <algorithm>
#include <cstring>
void reverse( std::string* str ) // no need to pass size to this function
{
int start = 0;
int end = str->length() - 1; // get the length of str like this
char* ptrToCharArray = const_cast<char*>( str->c_str() ); // gets the pointer to str's internal buffer
while ( start < end )
{
std::swap( ptrToCharArray[start++], ptrToCharArray[end--] ); // no need to use * operator anymore
}
}
int main()
{
std::string str = "Something";
reverse( &str );
std::cout << "Reversed string: " << str << std::endl;
return 0;
}
Output is:
Reversed string: gnihtemoS
Hopefully, this helps you.
Just need a little bit of change in your code
Change this *str[start++] to (*str).at(start++)
void reverse(string* str, int size)
{
int start = 0;
int end = size - 1;
while(start < end)
{
swap((*str).at(start++),(*str).at(end--));
}
}
int main()
{
string str = "Something";
reverse(&str, str.length());
cout << "Reversed string: " << str << endl;
return 0;
}
Note that there is no need to pass the size of the string as an argument to the function. You can use the member function std::string::size for that purpose as shown below:
Version 1: Passing pointer to string as argument
#include <iostream>
#include <algorithm>
void reverse(std::string *str)
{
int n=(*str).size()-1;//dereference the pointer and use size member function on the resulting string object
for(int i=0;i<((*str).size()/2);i++){
//Using the swap method to switch values at each index
std::swap((*str).at(i),(*str).at(n)); //note this can also be written as std::swap((*str)[i],(*str)[n]);
n = n-1;
}
}
int main()
{
std::string myString = "myString";
reverse(&myString);
std::cout<<"Reversed string is: "<<myString<<std::endl;
return 0;
}
In version 1, *(str) gives us a std::string type object. Next we call size member function on this std::string object. Similarly we can call the std::string::at member function on this std::string object.
Version 2: Passing reference to string as argument
#include <iostream>
#include <algorithm>
void reverse( std::string &str)
{
int n=str.size()-1;
for(int i=0;i<(str.size()/2);i++){
//Using the swap method to switch values at each index
std::swap(str.at(i),str.at(n));
n = n-1;
}
}
int main()
{
std::string myString = "myString";
reverse(myString);
std::cout<<"Reversed string is: "<<myString<<std::endl;
return 0;
}

How to find the index of element (and a few other things)

I was writing a code that would substitute some random 17 character strings into a single alphabet, and I can't find a way. Basically, what I'm trying to do is this:
char strings[] = {
"L-nIbhm5<z:92~+,x",
"9bC5f0q#qA(RKZ>|r",
"9bC5f0q#qA(RKZ>|r",
"k=5,ln(08IAl(gGAK",
"|N,8]dGu)'^MaYpu[",
"!&,Y*nz8C*,J}{+d]",
"Us9%^%?n5!~e##*+#",
"zF8,1KV#¥]$k?|9R#",
"0B4>=nioEjp>4rhgi",
}
char alphabet[]{
"a","b","c","d","e","f","g","h","i",
}
replace(std::string str){
/**get str and then see the index of the corresponding string in strings[], and replace the string with alphabet[index number], while deleting the original string part that was replaced**/
int main(){
cin >> std::string replace;
replace(replace);
example input: L-nIbhm5<z:92~+,x9bC5f0q#qA(RKZ>|r9bC5f0q#qA(RKZ>|r
expected output: abc
EDIT:
New Code
Changes from the original code
It also has a bigger array than the simplified version(previous code). It displays the structure of the full program.(where the strings are routed to and why)
Basically What it's doing
getting input from user, put it in the input variable, input goes through algorithm() function untouched, and then goes to the replace function and is replaced. It then the replaced string gets returned back through the original route to the main function, where it is displayed.
I've kept the arrays a string type because the const char* gave me a segmentation error.
std::string Subs[53]=
{
"LQlMv]G5^^1kcm?fk",
"7W^S;/vB(6%I|w[fl",
"<w7>4f//Z55ZxK'z.",
"_W5g(lu<pTu3^_A7n",
"OfLm%8:EF}0V1?BSS",
"|+E6t,AZ~XewXP17T",
"L-nIbhm5<z:92~+,x",
"L-nIbhm5<z:92~+,x",
"9bC5f0q#qA(RKZ>|r",
"9bC5f0q#qA(RKZ>|r",
"k=5,ln(08IAl(gGAK",
"|N,8]dGu)'^MaYpu[",
"!&,Y*nz8C*,J}{+d]",
"Us9%^%?n5!~e##*+#",
"zF8,1KV#¥]$k?|9R#",
"0B4>=nioEjp>4rhgi",
"EG#0[W9.N4i~E<f3x",
"(0Pwkk&IPchJHs.7A",
"7XgmQ6fW<|J+NY[m0",
".g4CwX/DU!!~!zbtZ",
"+_U'qn_/9Fo|gT/!n",
"=0s(mYh&F%y=MBS5(",
"cg71(}bo+Q5P8F[T6",
"lc|a\%5.9pOpooU+QR",
"E_(3A:o+.]qL3MYA6",
"H#O'X_RiVS#8l0bKD",
"Y1gbGD`~8d>HSWN35",
"LQlMv]G5^^1kcm?fk",
"T4}gI;`BFVfhw=-sf",
"6BHMA0IRix]/=(jht",
"yS$=#Jdpp?P2k6SMQ",
"t1~|kkh+>4d>}OQ`a",
"2Y-\\CU\"944yBluWD5",
"'M\\ZbIX5{`Xd;qi!o",
"?N+RtVqj_r(C5##0\"",
"2;*Livh?V$X/8z#Md",
")IN|7FOs2l-mAM[d#",
"(~f268J},xXrK'Rp'",
"&r/qf9fFHnzV!RzH/",
"}naDRH4p$NI2a).t,",
"{8DM+7!.Mge|~fnO|",
")r[#nI0YDH>6cE38p",
"(0Pwkk&IPchJHs.7A",
")r[#nI0YDH>6cE38p",
"8M-=cQFQ,pPo7eu=p",
"0PHw=/|(tZ1}FHm/'",
"[su`'0Oybc.\"-/W5)",
"1uHl[IC7Sr#NUJV;I",
"8z8%,jK0CDOkJz8I?",
"3Ao2yXDN%YzpE&Suy",
"zNs`7E'e/$i8VqaUL",
"bzHmA^K2>7`UZ?!AO",
};
std::string Alphabet[53] =
{
" ","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","r","w","x","y","z",
"A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z",
};
std::string replace(std::string rep) {
int len = sizeof(Subs)/sizeof(Subs[0]);
std::stringstream ss1;
for(int i = 0; i < len; i++) {
if (rep.find(Subs[i]) != std::string::npos) {
ss1 << Subs[i];
}
}
std::string input = ss1.str();
return input;
}
std::string algorithm(std::string input)
{
//some other algorithms come here(not relative to this question)
input = replace(input);
return input;
}
int main(void){
int ed;
std::cin >> ed;
if(ed == 1){
//different function(not relative to the question)
}
else if(ed == 0){
std::string input;
std::cin >> input;
input = algorithm(input);
std::cout << input << std::endl;
}
else{
std::cout << "1 or 0" << std::endl;
main();
}
return 0;
}
example input: L-nIbhm5<z:92~+,x9bC5f0q#qA(RKZ>|r9bC5f0q#qA(RKZ>|r
expected output: abc
actual output: L-nIbhm5<z:92~+,xL-nIbhm5<z:92~+,x9bC5f0q#qA(RKZ>|r9bC5f0q#qA(RKZ>|r
Sorry it's become long.
There are few mistakes in above code :
char array initialization is not correct.
method body for main and replace method is not closed.
Currently by default return type of replace method is int.
There is string#find method which can be helpful here.
I have tried to make those fixes and here is updated code in C++17 :
#include <iostream>
#include <sstream>
using namespace std;
const char *strings[9] = {
"L-nIbhm5<z:92~+,x",
"9bC5f0q#qA(RKZ>|r",
"9bC5f0q#qA(RKZ>|r",
"k=5,ln(08IAl(gGAK",
"|N,8]dGu)'^MaYpu[",
"!&,Y*nz8C*,J}{+d]",
"Us9%^%?n5!~e##*+#",
"zF8,1KV#¥]$k?|9R#",
"0B4>=nioEjp>4rhgi"
};
const char *alphabet[9] = {
"a","b","c","d","e","f","g","h","i"
};
void replace(std::string rep) {
int len = sizeof(strings)/sizeof(strings[0]);
std::stringstream ss1;
for(int i = 0; i < len; i++) {
if (rep.find(strings[i]) != std::string::npos) {
ss1 << alphabet[i];
}
}
std::cout << ss1.str();
}
int main(){
std::string rep;
cin >> rep;
replace(rep);
}
For reference : https://onlinegdb.com/Bd9DXSPAa
Note - Above code is just for reference, please make sure to add all test cases handling.
I made a c++17 version for your code.
Replacing 'c' style arrays and pointers with C++ style containers, iterators.
And using std::string::replace function. Use the standardlibrary if you can,
its tested and well documented.
#include <algorithm>
#include <iostream>
#include <regex>
#include <string>
#include <vector>
// std::vector/std::array instead of 'c' style arrays.
// allows us to us range based for loops later.
std::vector<std::string> strings =
{
"L-nIbhm5<z:92~+,x",
"9bC5f0q#qA(RKZ>|r",
"k=5,ln(08IAl(gGAK",
"|N,8]dGu)'^MaYpu[",
"!&,Y*nz8C*,J}{+d]",
"Us9%^%?n5!~e##*+#",
//"zF8,1KV#¥]$k?|9R#", // <<== I commented out this line, ¥ is not a valid charcter in my environment
"0B4>=nioEjp>4rhgi"
};
// a string is already an array of characters.
std::string alphabet{ "abcdefghijkl" };
std::string replace_with_alphabet(const std::string& input)
{
std::string retval{ input };
std::size_t index{ 0 };
// range based for, it will keep the order of the vector.
for (const auto& str : strings)
{
// look if you can find any of the predefined strings
// in the input strings.
const size_t pos = retval.find(str, 0);
// if found
if (pos != std::string::npos)
{
// get the next character from the alphabet
std::string replacement{ alphabet[index++] };
// use std::string::replace for replacing the substring
const size_t len = str.length();
retval.replace(pos, len, replacement, 0);
}
}
return retval;
};
/**get str and then see the index of the corresponding string in strings[], and replace the string with alphabet[index number], while deleting the original string part that was replaced**/
int main()
{
auto output = replace_with_alphabet("L-nIbhm5<z:92~+,x9bC5f0q#qA(RKZ>|rk=5,ln(08IAl(gGAK");
std::cout << output << std::endl;
}

How to split a string by another string in Arduino?

I have a character array like below:
char array[] = "AAAA... A1... 3. B1.";
How can I split this array by the string "..." in Arduino? I have tried:
ptr = strtok(array, "...");
and the output is the following:
AAAA,
A1,
3,
B1
But I actually want output to be
AAAA,
A1,
3.B1.
How to get this output?
edit:
My full code is this:
char array[] = "AAAA... A1... 3. B1.";
char *strings[10];
char *ptr = NULL;`enter code here`
void setup()
{
Serial.begin(9600);
byte index = 0;
ptr = strtok(array, "..."); // takes a list of delimiters
while(ptr != NULL)
{
strings[index] = ptr;
index++;
ptr = strtok(NULL, "..."); // takes a list of delimiters
}
for(int n = 0; n < index; n++)
{
Serial.println(strings[n]);
}
}
The main problem is that strtok does not find a string inside another string. strtok looks for a character in a string. When you give multiple characters to strtok it looks for any of these. Consequently, writing strtok(array, "..."); is exactly the same as writing strtok(array, ".");. That is why you get a split after "3."
There are multiple ways of doing what you want. Below I'll show you an example using strstr. Unlike strtokthe strstr function do find a substring inside a string - just what you are looking for. But.. strstr is not a tokenizer so some extra code is required to print the substrings.
Something like this should do:
int main()
{
char array[] = "AAAA... A1... 3. B1...";
char* ps = array;
char* pf = strstr(ps, "..."); // Find first substring
while(pf)
{
int len = pf - ps; // Number of chars to print
printf("%.*s\n", len, ps);
ps = pf + 3;
pf = strstr(ps, "..."); // Find next substring
}
return 0;
}
You can implement your own split as strtok except the role of the second argument :
#include <stdio.h>
#include <string.h>
char * split(char *str, const char * delim)
{
static char * s;
char * p, * r;
if (str != NULL)
s = str;
p = strstr(s, delim);
if (p == NULL) {
if (*s == 0)
return NULL;
r = s;
s += strlen(s);
return r;
}
r = s;
*p = 0;
s = p + strlen(delim);
return r;
}
int main()
{
char s[] = "AAAA... A1... 3. B1.";
char * p = s;
char * t;
while ((t = split(p, "...")) != NULL) {
printf("'%s'\n", t);
p = NULL;
}
return 0;
}
Compilation and execution:
/tmp % gcc -g -pedantic -Wextra s.c
/tmp % ./a.out
'AAAA'
' A1'
' 3. B1.'
/tmp %
I print between '' to show the return spaces, because I am not sure you want them, so delim is not only ... in that case
Because you tagged this as c++, here is a c++ 'version' of your code:
#include <iostream>
using std::cout;
using std::endl;
#include <vector>
using std::vector;
#include <string>
using std::string;
class T965_t
{
string array;
vector<string> strings;
public:
T965_t() : array("AAAA... A1... 3. B1.")
{
strings.reserve(10);
}
~T965_t() = default;
int operator()() { return setup(); } // functor entry
private: // methods
int setup()
{
cout << endl;
const string pat1 ("... ");
string s1 = array; // working copy
size_t indx = s1.find(pat1, 0); // find first ... pattern
// start search at ---------^
do
{
if (string::npos == indx) // pattern not found
{
strings.push_back (s1); // capture 'remainder' of s1
break; // not found, kick out
}
// else
// extract --------vvvvvvvvvvvvvvvvv
strings.push_back (s1.substr(0, indx)); // capture
// capture to vector
indx += pat1.size(); // i.e. 4
s1.erase(0, indx); // erase previous capture
indx = s1.find(pat1, 0); // find next
} while(true);
for(uint n = 0; n < strings.size(); n++)
cout << strings[n] << "\n";
cout << endl;
return 0;
}
}; // class T965_t
int main(int , char**) { return T965_t()(); } // call functor
With output:
AAAA
A1
3. B1.
Note: I leave changing "3. B1." to "3.B1.", and adding commas at end of each line (except the last) as an exercise for the OP if required.
I looked for a split function and I didn't find one that meets my requirement, so I made one and it works for me so far, of course in the future I will make some improvements, but it got me out of trouble.
But there is also the strtok function and better use that.
https://www.delftstack.com/es/howto/arduino/arduino-strtok/
I have the split function
Arduino code:
void split(String * vecSplit, int dimArray,String content,char separator){
if(content.length()==0)
return;
content = content + separator;
int countVec = 0;
int posSep = 0;
int posInit = 0;
while(countVec<dimArray){
posSep = content.indexOf(separator,posSep);
if(posSep<0){
return;
}
countVec++;
String splitStr = content.substring(posInit,posSep);
posSep = posSep+1;
posInit = posSep;
vecSplit[countVec] = splitStr;
countVec++;
}
}
Llamada a funcion:
smsContent = "APN:4g.entel;DOMAIN:domolin.com;DELAY_GPS:60";
String vecSplit[10];
split(vecSplit,10,smsContent,';');
for(int i = 0;i<10;i++){
Serial.println(vecSplit[i]);
}
String input:
APN:4gentel;DOMAIN:domolin.com;DELAY_GPS:60
Output:
APN:4g.entel
DOMAIN:domolin.com
DELAY_GPS:60
RESET:true
enter image description here

Gettin Lvalue Error in C++ array

I need to sort the first name and then last name of of student and then display the fully sorted names on screen using structure in C++. I tried but compiler showing Lvalue Required error - in these line
tfname = s[i].fname;
s[i].fname = s[j].fname;
s[j].fname = tfname;
tlname = s[i].lname;
s[i].lname = s[j].lname;
s[j].lname = tlname;
Here the complete code
#include <iostream.h>
#include <conio.h>
#include <stdio.h>
#include <string.h>
struct student
{
char fname[20];
char lname[20];
int id_no;
};
typedef student S;
void main()
{
S s[25];
char tfname[20], tlname[20];
int t;
for(int i = 0; i<25; i++)
{
cout<<"\n Enter Student's first name:";
cin>>s[i].fname;
cout<<"\n Enter Student's last name:";
cin>>s[i].lname;
cout<<"\n Enter ID NO";
cin>>s[i].id_no;
}
for(i = 0; i<24; i++)
{
for(int j = i+1; j<25; j++)
{
if(strcmp(s[i].fname, s[j].fname)>0)
{
tfname = s[i].fname;
s[i].fname = s[j].fname;
s[j].fname = tfname;
tlname = s[i].lname;
s[i].lname = s[j].lname;
s[j].lname = tlname;
t = s[i].id_no;
s[i].id_no = s[j].id_no;
s[j].id_no = t;
}
else
{
if(strcmp(s[i].fname, s[j].fname)==0)
{
if(strcmp(s[i].lname, s[j].lname)>0)
{
tfname = s[i].fname;
s[i].fname = s[j].fname;
s[j].fname = tfname;
tlname = s[i].lname;
s[i].lname = s[j].lname;
s[j].lname = tlname;
t = s[i].id_no;
s[i].id_no = s[j].id_no;
s[j].id_no = t;
}
}
}
}
cout<<"\n\n FIRST NAME \t LASTNAME \t ID NO ";
for(int i = 0; i<25; i++)
{
cout<<"\n"<< c[i].fname<<"\t" <lt; c[i].lname <<="" <
}
getch();
}
}
Kindly help me how can I solve this error
Use std::vector and std::string instead of arrays. Arrays have all kinds of problems in use cases such as yours. One of their big problems if that you cannot pass around or return them like "normal" objects, e.g. of type int or double. Their "second-class citizen" status in the C++ language also causes the strange behaviour you've observed in your program.
int GetInt() // works fine
{
return 123;
}
void DoSomethingWithDouble(double d) // works fine
{
// ...
}
char[] GetArray() // does not even compile
{
// ...
}
void DoSomethingWithArray(int array[]) // does not work as expected, either
{
// ...
}
This is why in C++, we use std::string, std::vector or other sophisticated classes which allow us to treat strings or collections exactly (or almost exactly) like simple (so-called "primitive") types such as int or double:
std::string GetString()
{
// ...
}
void DoSomethingWithString(std::string const &s) // you should use const & here because
// it does not cost you anything and may
// increase speed of your program
{
// ...
}
std::vector<int> GetInts()
{
// ...
}
void DoSomethingWithInts(std::vector<int> const &v) // you should use const & here because
// it does not cost you anything and
// may increase speed of your program
{
// ...
}
You can even assign them completely safely and with no special syntax:
std::vector<int> v1;
std::vector<int> v2;
v1 = v2;
std::string s1;
std::string s2;
s1 = s2;
And you can create string vectors, and they will behave exactly as you expect:
std::vector<std::string> string_vector;
string_vector.push_back("abc");
string_vector[0][0] = 'A';
std::cout << string_vector[0]; // prints "Abc"
Arrays do have their place in C++, but not for beginners and not in high-level programming.
You cannot swap strings this way. Strings should be copied using strcpy().
strcpy(tfname, s[i].fname);
strcpy(s[i].fname, s[j].fname);
strcpy(s[j].fname, tfname);
Another way is #include <string> and declare student::fname, student::lname, tfname and tlname as std::string. Then you could use assignment operator to copy them correctly.
Arrays has no the assignment operator. Instead of it you have to copy elements from one array to another. As in your code arrays has type char and designed to store strings you should use standard C function std::strcpy declared in header <cstring>
So this code snippet
tfname = s[i].fname;
s[i].fname = s[j].fname;
s[j].fname = tfname;
tlname = s[i].lname;
s[i].lname = s[j].lname;
s[j].lname = tlname;
will look the following way
std::strcpy( tfname, s[i].fname );
std::strcpy( s[i].fname,s[j].fname );
std::strcpy( s[j].fname, tfname );
std::strcpy( tlname, s[i].lname );
std::strcpy( s[i].lname, s[j].lname );
std::strcpy( s[j].lname, tlname );
The other approach is to use standard class std::array. In this case you may use the assignment operator. For example
#include <array>
//...
struct student
{
std::array<char, 20> fname;
std::array<char, 20> lname;
int id_no;
};
And at last you could use class std::string instead of raw character arrays.
If your task description contains C++, struct, and sort, what about this version?
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
struct Student
{
std::string fname;
std::string lname;
int id_no;
};
/// checks order after comparing (fname, then lname)
/// (usable with std::sort)
bool operator<(const Student& lhs, const Student& rhs)
{
int c=lhs.fname.compare(rhs.fname);
if (c < 0) {
return true;
} else if (c > 0) {
return false;
}
return lhs.lname < rhs.lname;
}
/// write student to standard output stream
/// (usable with std::for_each)
void out_student(const Student& s)
{
std::cout << s.fname << " " << s.lname << " " << s.id_no << std::endl;
}
int main()
{
std::list<Student> students;
// entering students left out
std::sort(students.begin(), students.end());
std::for_each(students.begin(), students.end(), out_student);
return 0;
}

Modify the self-made concat function so it will accept more than two arguments

I have programmed a self-made concat function:
char * concat (char * str1, char * str2) {
for (int i=0; i<BUFSIZ; i++) {
if (str1[i]=='\0') {
for (int j=i; j<BUFSIZ; j++) {
if (str2[j-i]=='\0') return str1;
else str1[j]=str2[j-i];
}
}
}
}
Now if I want to concat more than 2 strings, i.e. buf temp1 temp2,
I have to use something like that:
strcpy(buf, concat(concat(buf,temp1),temp2));
Please tell me, is there a simple way to modify my function so it would accept many arguments?
In C++ use string instead of char* and functions: std::string result = std::string(buf) + temp1 + temp2;
The feature you're looking for is varargs. This allows you to write a C function which accepts a variable number of arguments. It's how functions like printf are implemented
char* concat(size_t argCount, ...) {
va_list ap;
char* pFinal = ... // Allocate the buffer
while (argCount) {
char* pValue = va_arg(ap, char*);
argCount--;
// Concat pValue to pFinal
}
va_end(ap);
return pFinal;
}
Now you can call concat with a variable number of arguments
concat(2, "hello", " world");
concat(4, "hel", "lo", " wo", "rld");
Very simple:
#include <string>
#include <iostream> // for the demo only
std::string concat(std::string const& a) {
return a;
}
template <typename... Items>
std::string concat(std::string const& a, std::string const& b, Items&&... args) {
return concat(a + b, args...);
}
int main() {
std::cout << concat("0", "1", "2", "3") << "\n";
}
See it in action at ideone:
0123
Of course, you can add some overloads for efficiency.