I'm trying to output the number of element-objects in my array, but the syntax for that is not the same as it is for Java:
// print list of all messages to the console
void viewSent()
{
cout << "You have " << sent.size() << " new messages.\n";//Error: left of '.size' must have class/struct,union
std::cout << "Index Subject" << '\n';
for (size_t i = 0; i < sent.size(); ++i)
{
std::cout << i << " : " << sent[i].getSubject() << '\n';
}
}
if the .size doesn't work in C++ syntax, what does?
The C++ equivalent of a Java array is std::vector. sent.size() is the correct way to get the size.
You didn't post your definition of sent but it should be std::vector<YourObject> sent;, perhaps with initial size and/or values also specified.
I'm guessing you tried to use a C-style array -- don't do that, C-style arrays have strange syntax and behaviour for historical reasons and there is really no need to use them ever in C++.
If your array is a C-Array, you can loop through it like this:
for (size_t i = 0; i < (sizeof(sent) / sizeof(TYPE)); ++i)
... where TYPE is the underlying type of the array.
For example, if sent is defined as:
int sent[];
... then TYPE would be int, like this:
for (size_t i = 0; i < (sizeof(sent) / sizeof(int)); ++i)
A C-Array is not an object. So it has no members or methods and you cannot use the member operator with it. The sizeof operator is used to find the size of a fundamental type, in bytes. sizeof returns an integer value of type size_t.
Related
I've seen that a dynamic 2D array in C++ can be created as follows:
auto arr{ new int[nRows][nCols] };
nRows and nCols are compile-time known and the size of the array will not change during runtime.
I've tested what is the type of arr is PAx_i (where x is nCols). But I cannot figure out what to put instead of auto (if I don't want to use it) to create a dynamic 2D array with a single statement.
So, the question: Is it possible in C++ to specify the type of a dynamic 2D array directly (C-style like)? If yes, how?
C++ does not support dynamically-sized raw arrays (aka Variable Length Arrays, or VLAs). Whenever you come across the need for such a dynamic array (how ever many dimensions it may have), you should be immediately thinking of using the std::vector container.
Once properly created, you can use the [] operator (concatenated, for 2-D vectors) in much the same way as you would with raw arrays.
Here's a short code demo that creates a dynamic, 2-dimensional 'array' of integers, using the std::vector class, and initializes all elements with an especially significant, non-zero value:
#include <iostream>
#include <vector>
int main()
{
size_t nCols, nRows;
std::cout << "Enter nRows and nCols: ";
std::cin >> nRows >> nCols;
if (nCols < 2 || nRows < 2) {
std::cout << "Matrix is too small!\n";
return 1;
}
// The following SINGLE LINE declares and initializes the matrix...
std::vector<std::vector<int>> arr(nRows, std::vector<int>(nCols, 42));
std::cout << "nRows = " << arr.size() << "\n";
std::cout << "nCols = " << arr[0].size() << "\n";
for (auto& row : arr) {
for (auto i : row) {
std::cout << i << " ";
}
std::cout << std::endl;
}
// Demo for how to use the "[][]" operator ...
arr[0][0] = arr[nRows - 1][nCols - 1] = 33; // Change 1st and last
std::cout << "------------\n";
for (auto& row : arr) {
for (auto i : row) {
std::cout << i << " ";
}
std::cout << std::endl;
}
return 0;
}
One of the great benefits of using std::vector over new[] is that you don't have to worry about subsequently calling delete[] – the container class takes care of all memory allocation and deallocation internally.
In C++ try to avoid new/delete unless you have no other choice.
Next up is std::make_unique (or std::make_shared).
For dynamic arrays C++ has 'std::vector'
Like this :
#include <vector>
#include <iostream>
int main()
{
// 2x2 vector
std::vector<std::vector<int>> values{ {0,1},{2,3},{4,5} };
std::cout << values[1][1];
return 0;
}
Thusly, vector of vector, in this case the values are uninitialized.
std::vector<std::vector<int>> arr(nRows,std::vector<int>(nCols));
You can also do this with gcc compiler, but its not per standard, and won't work if nRows or nCols is variable in Visual Studio:
int arr[nRows][nCols];
It's better for you to get comfortable with C++ standard library, as it will open up new worlds for you.
Another way:
int *arr = new int[nRow*nCol];
You can then index into the flat buffer like:
arr[0][1]
I'm simply trying to get the size of an array passed as a parameter in a function, but I dont understand why the sizeof(k)/sizeof(k[0]) just returns one, when it works fine in the scope where it was declared, what am i missing here?
Heres the code:
#include <iostream>
using namespace std;
int fn(int k[]){
cout << "size in function :" << sizeof(*k) / sizeof(k[0]) << endl; //returns 1 for some reason
//cout << "size in function :" << end(k)-begin(k) << endl; // can't find begin-end fitting function?
int m = *max(&k[0], &k[sizeof(k)/sizeof(int)]);
return m;
}
int main()
{
int k[] = { 1,2,3,4,5,6,7 };
int s = size(k);
cout << "size :" << sizeof(k) / sizeof(k[0]) << endl;
cout << "max: " << fn(k);
return 0;
}
The size of an array only "exists" at compile-time. By the time the code is compiled, arrays are nothing more than pointers.
So if you want a function to accept a sized array, then the size needs to be passed at compile-time, which means it has to be a template parameter.
template<std::size_t N>
int fn(int (&k)[N]){
cout << "size in function :" << N << endl;
cout << "size in function :" << end(k)-begin(k) << endl;
int m = *max(begin(k), end(k));
return m;
}
what am i missing here?
The type of the parameter is int* (after having been adjusted from an array of unknown bound).
sizeof(k)/sizeof(int)
You're dividing the size of a pointer with the size of an integer. That division has nothing to do with the size of the array whose element is being pointed at.
when it works fine in the scope where it was declared
That's because the type of that k isn't int*. The type of the that k is int[7].
find size of parameterized array
The type of a parameter is never an array in C++. If you declare a function parameter to be an array, then the type will be adjusted to be a pointer to element of such array.
If the type of the parameter is pointer, and if that pointer points to element of an array, then there is no general way to find out the size of the array in question.
Some arrays contain a sequence that is terminated by an element with a sentinel value. You can determine the length of such sequence by performing a linear search for the sentinel value. Null terminated strings are a common example of such sequences.
Old fashioned, C style is to pass the size of the array as separate parameter:
int fn(int k[], std::size_t size);
// usage
int k[] = { 1,2,3,4,5,6,7 };
fn(k, std::size(k));
A more modern approach is to combine the pointer and the size in a class. There is a class template for such purpose in the standard library:
int fn(std::span<int> k);
// usage
int k[] = { 1,2,3,4,5,6,7 };
fn(k);
I managed to convert an over 5000 line code of Fortran 77 program to C++ manually but the conversion didn't go according to plan. So I am trying to debug the C++ program using my Fortran 77 program. In fortran I developed a subroutine that takes an array and prints out the array index and its value into a comma delimited file. I am trying to do the similar thing in C++. but run foul of the "double temp1[tempi]" declaration. The array does not have to be the same size in all the calls to the function. So I cant code it a say "double temp1[21]" because the next time it is 25. Fortran passes arrays by reference. What do you propose I do.
I managed to to this for the Fortran program. The idea is to take the variable memory dump from the c++ program and compare the values in Excel using vba to see which one changes the most and then to focus on that variable in the C++ program a starting debugging point.
c++ code logic:
void singlearrayd(double temp1[tempi], int tempi, string str1){
for (int md_i = 1; md_i <= tempi; md_i++){
cout << temp1[md_i] << "," << str1 << "(" << md_i << ")";
}
}
int main(){
double askin[22];
double fm[26];
singlearrayd(askin,22,"askin");
singlearrayd(fm,26,"fm");
return 0;
}
Fortran 77 code logic:
PROGRAM PRINT_MEMORY
real*8 :: ASKIN(21)
real*8 :: FM(25)
CALL SINGLEARRAYD(ASKIN,21,"ASKIN")
CALL SINGLEARRAYD(FM,25,"FM")
END PRINT_MEMORY
SUBROUTINE SINGLEARRAYD(TEMP1,TEMPI,STR1)
IMPLICIT NONE
CHARACTER(LEN=*) :: STR1
INTEGER*4 MD_I,TEMPI
REAL*8, DIMENSION(1:TEMPI) :: TEMP1
DO MD_I = 1, TEMPI
WRITE(51,'(ES25.16E3,A1,A25,A1,I5,A1)') TEMP1(MD_I),',',STR1,'(',
1 MD_I,')'
ENDDO
ENDSUBROUTINE SINGLEARRAYD
There are multiple problems in your code.
In C++, a native array (like your askin in main()) is converted into a pointer when passed to a function. So there is no need to declare a dimension on the array in the argument list BUT it is still necessary to pass a second argument, as you are specifying the size.
This means the C++ function should have the form
void singlearrayd(double temp1[], int tempi, std::string str1)
or (equivalently)
void singlearrayd(double *temp1, int tempi, std::string str1)
Note in the above that I have specified the type of the third argument by its full name as std::string. In a lot of cases, it is better avoid using namespace std.
The second problem is that you are assuming Fortran array indexing and C++ array indexing are the same. In reality, Fortran array indexing is 1-based (the first element of an array has index one, by default) and C++ array indexing is 0-based (the first element on an array has index zero). Using Fortran array indexing in C++ causes undefined behaviour, because it will access elements outside the valid range.
The third (potential) problem is that your function defines two variables named md_i (one in the function, and one within the loop). It is better to avoid doing that.
Addressing all of the above will turn your function to (in full)
void singlearrayd(double temp1[], int tempi, std::string str1)
{
for (int md_i = 0; md_i < tempi; ++md_i) // note the differences here carefully
{
cout << temp1[md_i] << "," << str1 << "(" << md_i << ")";
}
}
The fourth problem is that main() in C++ returns int, not void.
The fifth problem is that main() does not initialize the arrays before singlearrayd() prints them. In Fortran, arrays that are local to a function are (often) zero-initialised. In C++, they are uninitialised by default, so accessing their values (e.g. to print them) gives undefined behaviour.
int main()
{
double askin[21] = {0.0}; // initialise the first element. Other elements are initialised to zero
double fm[21] = {0.0};
singlearrayd(askin,21,"askin");
singlearrayd(fm,25,"fm");
}
That will get your code working. Practically, however, there are improvements possible. The first improvement is to use a standard container rather than an array. Standard containers know their size, so that allows simplifying your function. Second, pass non-trivial arguments (like containers or strings) by reference - and preferably const reference if no change is being made to the argument. Unlike Fortran, where function arguments are often passed by reference BY DEFAULT, it is necessary to DELIBERATELY introduce references in C++.
#include <vector>
void singlearrayd(const std::vector<double> &temp1, const std::string &str1)
{
for (std::size_t md_i = 0; md_i < temp1.size(); ++md_i)
{
cout << temp1[md_i] << "," << str1 << "(" << md_i << ")";
}
}
int main()
{
std::vector<double> askin(21); // askin has 21 elements, initialised to zero
std::vector<double> fm(21);
singlearrayd(askin, "askin");
singlearrayd(fm, "fm");
}
C++ containers also support iterators - which are safer in practice AND often more efficient - than using array indexing. I'll leave it as an exercise for you to learn how to use those.
A key message however: don't assume that a simple mechanical translation from Fortran to C++ will work. You have already demonstrated pitfalls of such an assumption. Take the time to learn C++ BEFORE trying to translate too much code from Fortran to C++. That is necessary both to get the C++ code working correctly and also to get it running efficiently.
A more modern implementation would be
#include <string>
#include <array>
#include <iostream>
template <std::size_t size, class U>
void singlearrayd(const std::array<U, size>& temp1, const std::string& str1){
int i = 0;
for (const auto& x : temp1)
std::cout << x << "," << str1 << "(" << (i++) << ")";
}
int main(){
std::array<double, 21> askin;
std::array<double, 21> fm;
singlearrayd(askin, "askin");
singlearrayd(fm, "fm");
return 0;
}
Please note that in the code above the two arrays askin and fm are not initialized. Presumably, in the real code you would have already initialized them before calling singlarrayd.
Also, remember that main must return an int.
Thank you for your valuable insight and comments. I think the best approach was use
void singlearrayd(double *temp1, int tempi, std::string str1)
Extending this idea and doing some more research using google I was able to extend this idea to handle 2D and 3D arrays.
void doublearrayd(double *temp1, int tempi, int tempj, std::string str1){
for (int md_j = 1; md_j<tempj; md_j++){
for (int md_i = 1; md_i<tempi; md_i++){
std::cout << *(temp1 + md_i*tempj + md_j) << "," << str1 << "(" << md_i << ";" << md_j << ")" << std::endl;
}
}
}
void triplearrayd(double *temp1, int tempi, int tempj, int tempk, std::string str1){
for (int md_k = 1; md_k < tempk; md_k++){
for (int md_j = 1; md_j<tempj; md_j++){
for (int md_i = 1; md_i<tempi; md_i++){
std::cout << *(temp1 + md_i*tempj*tempk + md_j*tempk + md_k) << "," << str1 << "(" << md_i << ";" << md_j << ";" << md_k << ")" << std::endl;
}
}
}
}
https://en.wikipedia.org/wiki/Row-_and_column-major_order
How can I pass a dynamic multidimensional array to a function?
I am trying to convert a char array to integers:
const int LENGTH = 3 * sizeof(int);
char data[LENGTH];
/* data is filled */
for (int i = 0; i < LENGTH; i += sizeof(int)) {
std::cout << "Integer: " << (int)data[i] << std::endl;
}
for (int i = 0; i < LENGTH; i += sizeof(short)) {
std::cout << (short)data[i] << " ";
}
the output is:
Integer: 0
Integer: 0
Integer: 0
0 3 0 3 0 3
I'd expect that if the shorts are not zero so must the integers. Probably the conversion as seen here works for just that one character/byte and not as expected for the folloing 4 bytes. How can I fix that?
To be clear: I want bytes 0 to 3 casted into one integer, then the next (4 to 7) into the next integer and so on...
You are casting data[i] to an int. However, data[i] is a char, so you can cast all you want, the cast is not going to magically read extra bytes. Instead, you have to cast the data pointer to int * and only then dereference it.
Basically, you'll end up with something like this:
auto voidPtr = static_cast<void const *>(data);
auto intPtr = static_cast<int const *>(voidPtr);
for (size_t i = 0; i < LENGTH / sizeof(int); ++i) {
std::cout << "Int: " << intPtr[i] << "\n";
}
Note how i is only incremented by 1 each time, but the number of increments is divided by sizeof(int). This is because the compiler will automatically do the right thing when you're indexing an int *.
Also be aware that what you're getting back might not be what you expect. Depending on whether the machine you're running this on is big- or little-endian.
P.S.: It's generally discouraged to use a C-style cast, static_cast<int> is much more explicit in showing what you want to achieve.
As #underscore_d pointed out, *((int*)&data[i]) from this answer will result in undefined behaviour and memcpy should be used.
int intData[3];
std::memcpy(intData, data, sizeof data);
for (int i = 0; i < 3; i++) {
std::cout << "int: " << intData[i] << " ";
}
is working fine and complies with the reference of memcpy.
I get a segmentation fault when my function reads floats from a string and places them in a void array. The segfault occurs after about 200 iterations of the for loop in the following code:
// Allocate memory
void** data;
data = (void**)malloc(num_vals * sizeof(float));
// Convert text to floats
(*(float**)data)[0] = atof(strtok(text, " "));
for(int index=1; index<num_vals; index++) {
(*(float**)data)[index] = atof(strtok(NULL, " "));
std::cout << (*(float**)data)[index] << std::endl;
}
The void array is necessary because the size and type of data in the string are determined at run-time. I've tried increasing the malloc size, but it doesn't change anything. Any thoughts?
Seriously??
std::vector<float> data;
std::istringstream str(text);
float fv;
while (str >> fv)
{
data.push_back(fv);
}
Now that's c++
As much as it pains me to do so, here is a version of your code that probably does what you want.
// Allocate memory
void* data;
data = malloc(num_vals * sizeof(float));
// Convert text to floats
((float*)data)[0] = atof(strtok(text, " "));
for(int index=1; index<num_vals; index++) {
((float*)data)[index] = atof(strtok(NULL, " "));
std::cout << ((float*)data)[index] << '\n';
}
Note, however, that if you worked for me and tried to check in that code, we would have a serious discussion about your choice of career.
I'd rather see something like this:
std::vector<float> v;
std::copy(std::istream_iterator<float>(std::istringstream(text)),
std::istream_iterator<float>(),
std::back_inserter(v));
P.s. Rob's rule #47: Never say std::endl when you mean '\n'.
Why do you convert to void ** ??? You code contains couple errors on indexing, so let me show some reasonable changes
float* data;
data = (float*)malloc(num_vals * sizeof(float));
// Convert text to floats
data[0] = atof(strtok(text, " "));
for(int index=1; index<num_vals; index++) {
data[index] = atof(strtok(NULL, " "));
std::cout << data[index] << std::endl;
}
You got your types mixed up in your inexplicable attempt to create this monster under the pretence of writing "C++". Anyway. what you're mallocing is nothing but a float*, so you need to cast data back to float*:
((float*)data)[0] = myfloat;
I think, since you are defining a pointer of void pointer and allocating/casting it to pointer of void pointer, it allocates 4 byte memory for each element because in C/C++, regardles of the type of the pointer, pointers are always 4 bytes which are not big enough floats.
There're several issues. One is - data should be void *, you have redundant *. Other might be alignment, i'm not sure you are able to place a float in any location in the memory.