I've recently started learning c++ after 5 years with python and am struggling quite a bit when it comes to loops. the whole for(;;) business.
in python i was used to:
for x in y:
print(x)
> x
> y
> z
however in c++ i seem to need two semi-colons like
for(;;)
what i would like to do is print the paths of my directories given in the below code. Thank you for your time!
#include <windows.h>
#include <string>
#include <iostream>
#pragma comment(lib, "user32.lib")
using namespace std;
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
// -- main
int main(void) {
// -- console
SetConsoleTextAttribute(hConsole, 12);
// -- paths
string ScanDir[2] = {"C:/Users/Stephen/Downloads/", "C:/Users/Stephen/Documents/"};
// -- loops
for (int i = 0; i < ScanDir->length(); ++i) {
string ss = ScanDir[i];
cout << ss.c_str() << "\n";
}
return 0;
}
here is the error:
error screenshot
In C++ arrays are not objects in the OOP sense and do not have methods. They are just a dumb block of memory.
ScanDir->length()
is not getting the length of the array. Instread ScanDir is decaying to a pointer to the first string in the array and length is being called on this string. As a result
for (int i = 0; i < ScanDir->length(); ++i)
iterates length of the first string times, not length of the array, and shoots off the end of the array and into the unknown. This invokes Undefined Behaviour which in this case lead to a crash.
The smart thing to do is use a Range-based for loop
for (const auto & dir: ScanDir) {
cout << dir.c_str() << "\n";
}
which figures out the dimensions for you. Some additional reading on the const auto & bit: What is the correct way of using C++11's range-based for?
You can also use
for (int i = 0; i < std::size(ScanDir); ++i)
if compiling to C++ 17 or better or replace the magic number 2 in
string ScanDir[2] = {"C:/Users/Stephen/Downloads/", "C:/Users/Stephen/Documents/"};
with a constant that can be used wherever the size of the array is required.
Another alternative is replace the array with a smarter container like std::array
std::array<std::string, 2> ScanDir = {"C:/Users/Stephen/Downloads/", "C:/Users/Stephen/Documents/"};
which has a size method.
ScanDir->length() is the length of your strings which is notably greater than 2. You can either use 2 as upper loop boundary or sizeof(ScanDir) / sizeof(ScanDir[0]), or type the loop itself as for(auto const &ss: ScanDir) count << ss.c_str() << '\n';.
Related
I am a beginner to C++ syntax. Now, I need to create an mxn 2D array in C++ to use it in another project. I have looked at other answers which involve using tools like vector, etc. Many tools are not working on my Visual Studio 15 i.e. for vector I can not define with std::vector without a message like vector is not in std. So, I have wrote the following code:
#include "stdafx.h"
#include <iostream>
using namespace std;
int main()
{
int i; int j; int row[5][10] = {};
for (int j = 0; j < 10;)
for (int i = 0; i < 5;)
{
row[i][j] = 500;
int printf(row[i][j]);
i++;
j++;
cout << "Array:" << row[i][j] << endl;
}
return 0;
}
Surely, this is not the correct syntax. So the output is beyond my expectation. I want to create an m*n array with all the elements being the same integer; 500 in this case. That is, if m=3, n=2, I should get
500 500 500
500 500 500
There's a couple things wrong with your current code.
The first for loop is missing curly brackets
You're redefining int i and int j in your for loop. Not a complilation issue but still an issue.
You're using printf incorrectly. printf is used to output strings to the console. The correct line would be printf("%d", row[i][j]);
If you want to use a vector, you have to include it using #include <vector>. You can use a vector very similar to an array, but you don't have to worry about size.
You seem to be learning. So, I did minimal correctios to make it work. I suggest you to make modifications as per your needs.
#include <iostream>
using namespace std;
int main()
{
int row[5][10] = {};
for (int j = 0; j < 10; j++) {
for (int i = 0; i < 5; i++) {
row[i][j] = 500;
cout << row[i][j] << " ";
}
cout << endl;
}
return 0;
}
Care and feeding of std::vector using OP's program as an example.
#include <iostream>
#include <vector> // needed to get the code that makes the vector work
int main()
{
int m, n; // declare m and n to hold the dimensions of the vector
if (std::cin >> m >> n) // get m and n from user
{ // m and n are good we can continue. Well sort of good. The user could
// type insane numbers that will explode the vector, but at least they
// are numbers.
// Always test ALL user input before you use it. Users are malicious
// and incompetent <expletive delteted>s, so never trust them.
// That said, input validation is a long topic and out of scope for this
// answer, so I'm going to let trapping bad numbers pass in the interests
// of keeping it simple
// declare row as a vector of vectors
std::vector<std::vector<int>> row(m, std::vector<int> (n, 500));
// breaking this down:
// std::vector<std::vector<int>> row
// row is a vector of vectors of ints
// row(m, std::vector<int> (n, 500));
// row's outer vector is m std::vector<int>s constructed with
// n ints all set to 500
for (int j = 0; j < n; j++) // note: j++ has been moved here. This is
// exactly what the third part of a for
// statement is for. Less surprises for
// everyone this way
// note to purists. I'm ignoring the possible advantages of ++j because
// explaining them would muddy the answer.
// Another note: This will output the transverse of row because of the
// ordering of i and j;
{
for (int i = 0; i < m; i++) // ditto I++ here
{
// there is no need to assign anything here. The vector did
// it for us
std::cout << " " << row[i][j]; // moved the line ending so that
// the line isn't ended with
// every column
}
std::cout << '\n'; // end the line on the end of a row
// Note: I also changed the type of line ending. endl ends the line
// AND writes the contents of the output stream to whatever media
// the stream represents (in this case the console) rather than
// buffering the stream and writing at a more opportune time. Too
// much endl can be a performance killer, so use it sparingly and
// almost certainly not in a loop
}
std::cout << std::endl; // ending the line again to demonstrate a better
// placement of endl. The stream is only forced
// to flush once, right at the end of the
// program
// even this may be redundant as the stream will
// flush when the program exits, assuming the
// program does not crash on exit.
}
else
{ // let the use know the input was not accepted. Prompt feedback is good
// otherwise the user may assume everything worked, or in the case of a
// long program, assume that it crashed or is misbehaving and terminate
// the program.
std::cout << "Bad input. Program exiting" << std::endl;
}
return 0;
}
One performance note a vector of vectors does not provide one long block of memory. It provides M+1 blocks of memory that may be anywhere in storage. Normally when a modern CPU reads a value from memory, it also reads values around it off the assumption that if you want the item at location X, you'll probably want the value at location X+1 shortly after. This allows the CPU to load up, "cache", many values at once. This doesn't work if you have to jump around through memory. This means the CPU may find itself spending more time retrieving parts of a vector of vectors than it does processing a vector of vectors. The typical solution is to fake a 2D data structure with a 1D structure and perform the 2D to 1D mapping yourself.
So:
std::vector<int> row(m*n, 500);
Much nicer looking, yes? Access looks a bit uglier, though
std::cout << " " << row[i * n + j];
Fun thing is, the work done behind the scenes converting row[j][i] to a memory address is almost identical to row[j*n+i] so even though you show more work, it doesn't take any longer. Add to this the benefits you get from the CPU successfully predicting and reading ahead and your program is often vastly faster.
I am attempting to take char input from the user, and typecasting it to integer, such the A = 1; B = 2...Z = 26; and store it in an array.
This is the code:
#include <iostream>
using namespace std;
int main(){
char input;
int f[8];
int counter;
for(counter = 0; counter <= 8; counter++){
cin >> input;
f[counter] = (int)input - 64;
if(input == '\n') break;
}
cout << f[0] << endl; }
the if(input = '\n') break; line gives me an error. I am trying to break the loop on entering a new line. What's wrong in that? I am attempting to do this as I am taking 2 lines as input(which isn't important information here).
Why does this code in C++ give me a runtime error?
Because you don't use safe high-level facilities like std::array or std::vector instead of error-prone raw arrays.
The error you made causes undefined behaviour. int f[8]; is an array with 8 elements, indexed from 0 to 7, yet later on, in your loop your condition says counter <= 8, which includes 8. It should be counter < 8. Accessing f[8] is undefined behaviour.
Undefined behaviour means the compiler can build any behaviour into your program, including a crash with an error message.
Here's a safer, modern version of your program:
#include <iostream>
#include <array>
int main() {
std::array<int, 8> f;
for(auto&& element : f) {
char input;
std::cin >> input;
element = static_cast<int>(input) - 'A' + 1;
}
std::cout << f[0] << "\n";
}
Some changes:
Used std::array.
Replaced C-style cast with static_cast.
Used a C++11 range-based for loop. No need to hard-code the array's size in the loop condition.
Removed using namespace std;, which is almost always bad practice and which beginners should not use at all.
Removed your \n check, which will never work because std::cin >> input; disregards all whitespace. You will have to use std::getline to get the behaviour you want. In fact, you will want to do this anyway to get some actual error handling into your program.
Minimised scope of local variables.
The character A is not guaranteed to have the integer value 65. It's safer (and more readable) to write 'A'.
The program is giving you a runtime error because your Array loop is out of bounds. for(counter = 0; counter <= 8; counter++).
You should use for(counter = 0; counter < 8; counter++).
operator >> here skips whitespace by default, which includes newline characters - so you will never see one.
To avoid that you need to use cin.get(). There are other alternatives - you could use getline to read a line at a time and then parse the string you get.
Fixing your subscript out of bounds would be a good idea too, of course.
I am new to C++, I have a problem of array manipulation. I have an array of X with length 100, I need to fill the value of X with integer value of 1 to 10 (1,2,3,4,5,6,7,8,9,10) randomly.
I know that there will be duplicate, maybe like 1 printed ten times, etc, but that's really what I want.
Here is what I have:
an array of X:
int X[100];
Here is my code snippet:
int* X = NULL;
int* val = NULL;
int length1= 100;
int length2= 10;
X = new int[length1];
val = new int[length2];
int i;
int j;
for (i = 0; i < isi; i++) {
val[i] = i;
for (j = 0; j < length1; j++) {
if (j > i) {
X[j] = val[i];
} else {
X[j] = val[0];
}
cout << "X[" << j << "] = " << X[j] << "\n";
Sleep(1);
}
}
Code above makes the array X from index 0 to 99 has value of 0, then index 0 to 99 has value of 1 and so the other index until the index 0 to 99 has value of 9.
This is not what I want, what I want is to make it (if it is not random) index 0 to 9 has value of 0, then 10 to 19 has value of 1 ... until index 90 to 99 has value of 9. Hope my explanation clear.
I have come to a question in stackoverflow: How would you make an array of 10000 with only values of 1-1000 inclusive?
But still can't resolve my problem my self.
Can someone please give me solution to this.
Thank you in advance
#include <stdlib.h>
int main(int argc, char **argv) {
int r[100];
for (int i = 0; i < 100; ++i) {
r[i] = rand() % 10 + 1;
}
}
For some output, you can #include <iostream> and then std::cout << "r[" << i << "] = " << r[i] << "\n" inside the loop after each assignment.
If you want to seed the random number generator for a different sequence each time, then #include <time.h> and then srand(time(NULL)) before your first call to rand.
You can also use generate function:
#include <iostream>
#include <algorithm>
#include <random>
using namespace std;
int main()
{
int arr[100];
random_device rd;
default_random_engine dre(rd());
uniform_int_distribution<int> uid(0,9);
generate(arr, arr + sizeof(arr) / sizeof(int), [&] () { return uid(dre); });
for (int a : arr)
cout << a << " ";
}
Here are two ways to solve this problem - since this is a learning experience, only pseudo code (and relevant links) are provided. Each "task" can be looked up and solved separately. Note that neither method uses a secondary array.
If the amount of each number in the final result does not need to be the same (eg. 2 might appear 17 times) then consider the following loop-and-assign-random approach. A standard C for-each loop is sufficient.
# for every index pick a random value in [0, 10) and assign it
for i in 0 to last array index:
array[i] = random in range 0, 10
If the amount of numbers need to be the same, then consider filling the array and then shuffling it. The modulus operator is very handy here. (This assumes the array length is a multiple of the group size.)
# fill up array as 0,1,2,3,4,5,6,7,8,9,0,1,2.. (will be 10 groups)
for i in 0 to last array index:
array[i] = i % 10
# and randomly rearrange order
shuffle array
For the shuffle see Fisher-Yates, which even shows a C implementation - there are "more C++" ways, but this is a good technique to learn and practice with loops. (One cool property about Fisher-Yates is that as soon an item is swapped into the current index it is at the final swap location - thus the shuffle loop can be modified to shuffle and immediately perform an action such as displaying the value.)
In both cases a random function should be used; else the numbers will not be .. random.
To loop over the items of a collection the most natural C++ loop is the range based for loop.
In order to assign something to each item, the formal item name should be a reference, thus:
for( auto& item : X )
{
// E.g. assign to item here.
}
This serves up each item of the array, in order, to the code marked by a comment.
There are two different random generators in C++, the old C library one, which is just a pair of functions, and the more general and modern but also not-so-easy-to-grok C++11 thingy. I suggest you google it and try out things. Ask new more specific question if/when stuck.
I think others have pointed it out but you have to first write the pre-compiler directive #include <ctime> and then use the srand function. Most would say don't use that but since you and I are at the basics our teachers, respectively, start us off with that. And it might work with your compiler as well.
Here is a link to learn more about it. I would have commented but I can't.
http://www.cplusplus.com/reference/cstdlib/srand/
I'm trying to practice some c++ stuff and have gotten stuck on something which has been difficult to google or find the answer too.
basically say we have:
char* myarray[] = {"string","hello", "cat"};
how would I got about say getting myarray[1] which is "string" and then traversing through the letter s t r i n g.
I was looking at vectors and wonder if that is the route to take or maybe taking myarray[1] and storing it into another array before iterating through it. What is the best way of doing this
That's very easy in C++11 (using std::string rather than a pointer to an array of characters):
#include <iostream>
#include <string>
int main()
{
std::string myarray[] = {"string","hello", "cat"};
for (auto c : myarray[0]) { std::cout << c << " "; }
}
Output (live example):
s t r i n g
The following code:
for(int i = 0; i < strlen(myarray[0]); i++) {
printf("%c\n", myarray[0][i]);
}
Will print out
s
t
r
i
n
g
If you're looking to practice C++ and not C, I suggest learning std::string and std::vector. They are not always the solution but usually using them first is the right answer. The memory layout of std::vector<std::string> will be different and it is important to understand this.
I am trying to use the new C++11 range based for loops. Here's my program:
#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
using namespace std;
ofstream logger("log.txt");
void log(string message)
{
logger << message << std::endl;
logger.flush();
}
int main( int argc, char* args[] )
{
log("hello world");
cout << "hello world\n";
log("declare sort me");
int sortMe[10];
log("loop sortMe");
for(int i : sortMe) {
log("in loop " + i);
sortMe[i] = i + 1;
}
}
I'm using clang++ to compile. It compiles with the warning:
clang++ -o mycpp mycpp.cpp
mycpp.cpp:24:12: warning: range-based for loop is a C++11 extension
[-Wc++11-extensions]
for(int i : sortMe) {
^
1 warning generated.
When it runs, I get this output:
hello world
Segmentation fault (core dumped)
According to the log.txt file, the program gets to the for loop, but it never enters the for loop. What am I missing?
This loop:
for(int i : sortMe) {
log("in loop " + i);
sortMe[i] = i + 1;
}
Loops and returns the values stored in the sortMe array, not the indices of the sortMe array. As a result, the lookup sortMe[i] will jump to a totally random index of the array (probably way, way out of bounds), causing the segfault.
If you want to set each element equal to its position, just use a normal for loop:
for (int i = 0; i < 10; i++) {
sortMe[i] = i + 1;
}
Also, as #hmjd noted, the call to log will not work correctly, because you are doing pointer arithmetic on a string, not doing a string concatenation.
Hope this helps!
You're using a range-based for loop where you should be using a standard for loop. The int i in your loop is not the index of the current element, but the value of that element. That is, if your array contained {1, 3, 3, 7}, the value of i at each iteration would be 1, then 3, then 3, then 7. Since your array is uninitialized, you have no idea what the values of i will be and you're getting undefined behaviour.
If you want the index in your for loop, use a standard for loop:
for(int i = 0; i < 10; i++) {
log("in loop " + std::to_string(i));
sortMe[i] = i + 1;
}
Note that to do string concatenation with +, one of your operands will need to be a std::string. Otherwise you are adding i to the pointer that points at the first character in "in loop ".
The call to log() within the for loop is incorrect. The argument is the result of pointer arithmetic, with an unknown int value being used as an offset from the base address of a string literal.
Use std::to_string() to convert an int to a std::string.
Just to mention std:iota, that can be used to set elements of a range to increasing values based of an initial value:
std::iota(std::begin(sortMe), std::end(sortMe), 1);
I think that you expect the range-based loop to do something different from what it does...
If you write for (int var: array) { log(var); } then the code will be executed as many times as there are elements in the array. Each time, var will be equal to one of the elements of array. Note that I use var directly as an array element, and not array[var]!
For instance, if you have int[3] array = { 42, 69, 1337 };, the precedent for loop will log 42, 69 and 1336.
So if I simply do int[3] array;, the precedent for loop will loop the three random integers that were already in memory where the array is being stored... if instead of using var directly I was to use array[var] it would most likely crash because var would not be a valid index for array.
Solution:
Don't get confused with the difference between array elements and indexes...
If you want to manipulate directly the elements of the array:
for(int element : sortMe) {
/* Do something with the element */
}
If you want to use indexes, don't go for a range-based loop:
for(int index = 0; index < 10; ++index) {
/* Do something with the index */
}
You can do what you want the new way, but it is a rather pointless exercise and better done with a regular for loop. The range loop returns values, not references. At least in the way you are using it.
int count = 0;
// Use a reference so we can update sortMe
for (int& i : sortMe) {
i = ++count;
}
On looking at it, it is a little more compact than a normal for loop and strangely I prefer it. ;)