more modern way of looping through C++ arrays - c++

Recently I have found a lot of examples, most of them regards the C++ 98, anyways I have created my simple-array and a loop (codepad):
#include <iostream>
using namespace std;
int main ()
{
string texts[] = {"Apple", "Banana", "Orange"};
for( unsigned int a = 0; a < sizeof(texts); a = a + 1 )
{
cout << "value of a: " << texts[a] << endl;
}
return 0;
}
Output:
value of a: Apple
value of a: Banana
value of a: Orange
Segmentation fault
It's working fine, except the segmentation fault at the end.
My question is, does this array/loop through is done a good way? I am using C++ 11 so would like to be sure it fits the standards and couldnt be done a better way?

In C/C++ sizeof. always gives the number of bytes in the entire object, and arrays are treated as one object. Note: sizeof a pointer--to the first element of an array or to a single object--gives the size of the pointer, not the object(s) pointed to. Either way, sizeof does not give the number of elements in the array (its length). To get the length, you need to divide by the size of each element. eg.,
for( unsigned int a = 0; a < sizeof(texts)/sizeof(texts[0]); a = a + 1 )
As for doing it the C++11 way, the best way to do it is probably
for(const string &text : texts)
cout << "value of text: " << text << endl;
This lets the compiler figure out how many iterations you need.
as others have pointed out, std::array is preferred in C++11 over raw arrays; however, none of the other answers addressed why sizeof is failing the way it is, so I still think this is the better answer.

string texts[] = {"Apple", "Banana", "Orange"};
for( unsigned int a = 0; a < sizeof(texts); a = a + 1 )
{
cout << "value of a: " << texts[a] << endl;
}
Nope. Totally a wrong way of iterating through an array. sizeof(texts) is not equal to the number of elements in the array!
The modern, C++11 ways would be to:
use std::array if you want an array whose size is known at compile-time; or
use std::vector if its size depends on runtime
Then use range-for when iterating.
#include <iostream>
#include <array>
int main() {
std::array<std::string, 3> texts = {"Apple", "Banana", "Orange"};
// ^ An array of 3 elements with the type std::string
for(const auto& text : texts) { // Range-for!
std::cout << text << std::endl;
}
}
Live example
You may ask, how is std::array better than the ol' C array? The answer is that it has the additional safety and features of other standard library containers, mostly closely resembling std::vector. Further, The answer is that it doesn't have the quirks of decaying to pointers and thus losing type information, which, once you lose the original array type, you can't use range-for or std::begin/end on it.

sizeof tells you the size of a thing, not the number of elements in it. A more C++11 way to do what you are doing would be:
#include <array>
#include <string>
#include <iostream>
int main()
{
std::array<std::string, 3> texts { "Apple", "Banana", "Orange" };
for (auto& text : texts) {
std::cout << text << '\n';
}
return 0;
}
ideone demo: http://ideone.com/6xmSrn

you need to understand difference between std::array::size and sizeof() operator. if you want loop to array elements in conventional way then you could use std::array::size. this will return number of elements in array but if you keen to use C++11 then prefer below code
for(const string &text : texts)
cout << "value of text: " << text << endl;

If you have a very short list of elements you would like to handle, you could use the std::initializer_list introduced in C++11 together with auto:
#include <iostream>
int main(int, char*[])
{
for(const auto& ext : { ".slice", ".socket", ".service", ".target" })
std::cout << "Handling *" << ext << " systemd files" << std::endl;
return 0;
}

How about:
#include <iostream>
#include <array>
#include <algorithm>
int main ()
{
std::array<std::string, 3> text = {"Apple", "Banana", "Orange"};
std::for_each(text.begin(), text.end(), [](std::string &string){ std::cout << string << "\n"; });
return 0;
}
Compiles and works with C++ 11 and has no 'raw' looping :)

sizeof(texts) on my system evaluated to 96: the number of bytes required for the array and its string instances.
As mentioned elsewhere, the sizeof(texts)/sizeof(texts[0]) would give the value of 3 you were expecting.

Add a stopping value to the array:
#include <iostream>
using namespace std;
int main ()
{
string texts[] = {"Apple", "Banana", "Orange", ""};
for( unsigned int a = 0; texts[a].length(); a = a + 1 )
{
cout << "value of a: " << texts[a] << endl;
}
return 0;
}

In my point of view:
It is because the sizeof() operator returns the size
of a type in bytes.
So, Simply we can use size() instead of sizeof(). If
we need or must use sizeof() we have to divide it
with sizeof(dataType):
First way:
#include <iostream>
using namespace std;
int main ()
{
string texts[] = {"Apple", "Banana", "Orange"};
for(int a = 0; a < size(texts); a++){
cout << "value of a: " << texts[a] << endl;
}
return 0;
}
Second way:
#include <iostream>
using namespace std;
int main ()
{
string texts[] = {"Apple", "Banana", "Orange"};
for(int a=0; a<sizeof(texts)/sizeof(string); a++)
{
cout << "value of a: " << texts[a] << endl;
}
return 0;
}

Feels like illegal but this works:
So basically it is dynamic multidimensional array iteration termination case and it differs a bit from one dimensional solution, last element is -1 and it is stop value for cycle (I am new to C++ but this method me likes)
int arr[][3] = {{164, 0, 0}, {124, 0, 0}, {92, 4, 0}, {68, 4, 0}, -1};
for(int i = 0; arr[i][0]!=-1; i++)
{
cout << i << "\n";
}

You can do it as follow:
#include < iostream >
using namespace std;
int main () {
string texts[] = {"Apple", "Banana", "Orange"};
for( unsigned int a = 0; a < sizeof(texts) / 32; a++ ) { // 32 is the size of string data type
cout << "value of a: " << texts[a] << endl;
}
return 0;
}

Related

How do I sort a vector of strings in descending order?

I have this following code where there is a vector of strings. Each string is an integer. I want to sort this in a descending order.
The regular sort function did not solve my problem.
Can someone point out how to do this? I want the output as 345366,38239,029323. I want the leading zero in 029323 as well.
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
using namespace std;
int main() {
vector<string> v = {"345366", "029323", "38239"};
vector<int> temp(v.size());
for (int idx = 0; idx < v.size(); idx++)
temp[idx] = stoi(v[idx]);
sort(temp.begin(), temp.end()));
cout<<temp[0]<<" "<<temp[1]<<" "<<temp[2];
return 0;
}
You can use a comparator function like this:
vector<string> v = {"345366", "029323", "38239"};
std::sort(v.begin(), v.end(), [](const std::string &s1, const std::string &s2) -> bool {
return std::stoi(s1) > std::stoi(s2);
});
for(auto i : v)
cout << i << endl;
Check this std::stoi() reference.
Edit: From the comments, it seems std::stoi() is much better than std::atoi(). For converting C++ strings, use std::stoi(). For C strings, std::atoi() will silently fail without generating any error if the string is not convertible to int, whereas std::stoi() will generate an exception, thus is a safer choice as well.
cout << std::atoi("abc") << endl; // runs smoothly
cout << std::stoi("abc") << endl; // creates an 'uncaught exception of type std::invalid_argument: stoi'
However, the results will be the same in this case (will extract the prefix integer part and exit, in case of std::stoi(), if the string doesn't begin with integers, it will create an exception):
cout << std::atoi("999abc12") << endl; // prints 999
cout << std::stoi("999abc12") << endl; // prints 999
cout << std::stoi("abcdef12") << endl; // generates exception
Also see this answer.

How to give Default Argument of Std::Vector of std::pairs?

I don't know how many inputs will come to my function as pair of ints. So in order to achieve that I would like to get a default parameter of std::vector of std::pair, because I want at least one pair in case there is no input. How Can I achieve this?
#include <iostream>
#include <string>
void default_function(int inp1 = 11, int inp2 = 13){ //, std::vector<std::pair<int,int>> defaultVector = XXXX
}
int main()
{
default_function();
return 0;
}
For example user can input no pairs in that case I will set them 0,0. They can input (0 , 2) as one pair, or (0 , 5), (2 , 2),(0 , 2) as three or more pairs. How to handle this?
(C++ 14 Version)
Use an initializer list as shown below
void default_function(
std::vector<std::pair<int,int>> v =
{{1,2}, {3,4}, {5,6}})
{
for(auto &p: v)
std::cout << p.first << ", " << p.second << " : ";
}
int main() {
default_function();
return 0;
}

How to access a row of a C++ char matrix?

I am relearning C++ after many years of matlab. Here is some code that I wrote
char couts[3][20]={"Area of Rectangle: ","Area of Triangle: ","Area of Ellipse: "};
char C[20];
for (int i = 0; i < 3; i++) {
C=couts[i];
cout << C;
//code that calculates and couts the area
}
clearly this is the wrong way of getting that row of couts to print, but after trying many variations and googling I can't work out what I'm doing wrong. :(
You probbaly should use C++ features and not old C idioms:
#include <iostream>
#include <array>
#include <string>
const std::array<std::string, 3> couts{ "Area of Rectangle: ","Area of Triangle: ","Area of Ellipse: " };
int main()
{
std::string C;
for (int i = 0; i < couts.size(); i++) {
C = couts[i];
std::cout << C << "\n";
//code that calculates and couts the area
}
}
Use strings or even string_views in this case, not char arrays. You are not copying the string in C, so the cout doesn't work. In modern C++ (C++17), this would be instead:
constexpr std::string_view couts[] = {"Area of Rectangle: ","Area of Triangle: ","Area of Ellipse: "};
std::string_view C;
for (auto s: couts) {
std::cout << s << std::endl;
}
This probably the only place I would write a C-style array and not use std::array, as the number of elements may change in the future.
Here's a version using the C++17 deduction guides for std::array combined with std::string_view letting you use range based for-loops etc. on both the std::array and the std::string_views.
#include <iostream>
#include <array>
constexpr std::array couts = {
std::string_view{"Area of Rectangle: "},
std::string_view{"Area of Triangle: "},
std::string_view{"Area of Ellipse: "}
};
int main() {
for(auto& C : couts) {
for(auto ch : C) {
std::cout << ch; // output one char at a time
}
std::cout << "\n";
}
}

How to truncate a string array? C++

Let's say I have a string array with 5 words and I want to only output the first 3 letters of each word. How do I go upon doing this? I know how to do it with one string but with an array of strings I get lost.
This is how to do it with one string
std::string test = "hello";
std::cout << test << std::endl;
test = test.substr(0,3);
std::cout << test << std::endl;
What I want to do is this
std::string test[5] = {"hello", "pumpkin", "friday", "snowboard", "snacks"};
I want to cout the first 3 letters of each word. I tried test[5] = test[5].substr(0,3); and that did not work.
test[5] doesn't work because you only have 5 items in your array, only indexes 0 to 4 are valid.
Generally with arrays you need to write a loop to go through each array item in turn, for instance
for (int i = 0; i < 5; ++i)
test[i] = test[i].substr(0,3);
for (int i = 0; i < 5; ++i)
cout << test[i] << endl;
With test[5] you are reading out of bounds thus invoking undefined behavior. Arrays in C++ are zero indexed so the last element would be test[4]. Create a function that utilizes for example the std::next function or string's substr member function. Call inside a range based loop:
#include <iostream>
#include <string>
void foo(const std::string& s) {
if (s.size() >= 3) {
std::cout << std::string(s.begin(), std::next(s.begin(), 3)) << '\n';
// or simply:
std::cout << s.substr(0, 3) << '\n';
}
}
int main() {
std::string test[5] = { "hello", "pumpkin", "friday", "snowboard", "snacks" };
for (const auto& el : test) {
foo(el);
}
}
test[5] = test[5].substr(0,3); won't work and more over you don't have `test[5]`, index starts from `0`.
you may want to do like this
for(int i=0 ; i<5; i++) {
test[i] = test[i].substr(0,3);
cout << test[i] << endl;
}
substr is what you are looking for. Here is my implementation.
#include <array>
#include <string>
#include <iostream>
int main () {
std::array<std::string,5> list {"hello", "pumpkin", "friday", "snowboard", "snacks"};
for (const auto &word : list){
std::cout << word << std::endl;
}
for (auto &word : list){
word = word.substr(0,3);
}
for (const auto &word : list){
std::cout << word << std::endl;
}
}
Use the standard library.
std::for_each(std::begin(test), std::end(test), [] (auto& s) { s.erase(3); });
Or even a simple range-based for loop:
for (auto&& s : test) {
s.erase(3); // Erase from index 3 to end of string.
}
Or maybe even create another container with views of the original strings:
auto test2 = std::accumulate(std::begin(test), std::end(test),
std::vector<std::string_view>{},
[] (auto& prev, std::string_view sv) -> decltype(prev)& {
prev.push_back(sv.substr(0, 3));
return prev;
});

How to print an array of const chars?

I have written the following code to save in an char * array and print the following content:
band1.txt
band2.txt
...
band3.txt
The code seems right but what is printed on the console is very weird.
Code:
const char ** current_band = new const char * [103];
stringstream sstm;
string str;
for (i=0;i<103;i++){
current_band[i] = new char[11];
}
for (i=0;i<103;i++){
sstm.str("");
sstm << "band" << i+1 << ".txt";
str = sstm.str();
current_band[i] = str.c_str();
cout << current_band[i] << endl;
cout << i << endl;
}
for (i=0;i<103;i++){
cout << current_band[i] << endl;
cout << i << endl;
}
Console:
band1.txt
0
band2.txt
1
...
band103.txt
102
And then for the last loop:
band103.txt
0
band102.txt
1
band103.txt
2
band102.txt
3
...
band102.txt
101
band103.txt
102
How is this even possible?
EDIT: Actually i want the "bands" to be char* in order to call the ifstream current_band_file(current_band) constructor that wants such an argument
You have undefined behavior by using pointers to already destroyed objects.
Simply don't use raw pointers and raw arrays and such stuff yet.
std::string is your friend for strings, std::vector is your friend for arrays.
Example:
#include <iostream>
#include <string>
#include <vector>
using namespace std;
auto main()
-> int
{
vector<string> band_names;
for( int i = 1; i <= 103; ++i )
{
band_names.push_back( "band" + to_string( i ) );
}
for( string const& name : band_names )
{
cout << name << endl;
}
}
As a minimal change to you existing code you can change:
current_band[i] = str.c_str();
to:
strcpy(current_band[i], str.c_str());
However, moving away from this mixed C and C++ to more idiomatic C++ (like Cheers and hth. - Alf's answer) will serve you better for the future.
Sticking with things like char[11] over std::string means you're stuck with:
The arbitrary choice of max length 11 even though probably there is no good technical reason for that limit.
Dealing with handling all the details of memory allocation which a proper C++ implementation hides.
The much less natural to read lower level code style.
As a band-aid you could replace:
current_band[i] = str.c_str();
with
if ( str.size() >= 11 )
throw std::runtime_error("string too long");
std::strcpy(current_band[i], str.c_str());
However it would be a much better idea to replace this whole thing with:
std::vector<std::string> current_band(103);
int i = 0;
for (auto &s : current_band)
{
// your sstm stuff, storing to s
}
Here's an alternative way that's a little more robust, readable and more likely to be correct.
#include <vector>
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
int main()
{
vector<string> bands;
bands.reserve(103);
for(size_t i = 1 ; i <= 103 ; ++i) {
ostringstream ss;
ss << "band" << i;
bands.emplace_back( ss.str() );
}
for (size_t index = 0 ; index < bands.size() ; ++index) {
cout << index << " : " << bands[index] << endl;
}
return 0;
}
output:
Compiling the source code....
$g++ -std=c++11 main.cpp -o demo -lm -pthread -lgmpxx -lgmp -lreadline 2>&1
Executing the program....
$demo
0 : band1
1 : band2
2 : band3
...
100 : band101
101 : band102
102 : band103