I have the next C++ code snippet:
...
static constexpr const char* my_char_array [10] { // Some literals here... } // Member of a class
std::vector<std::string> splitted_input { // Contains C++ strings }
std::vector<std::string> matched_keywords { // The coincident ones will be copied here }
for (int i = 0; i < sizeof(this->my_char_array); i++) {
std::cout << "Comparing: " << this->my_char*_array[i] << std::endl;
auto value = std::find(splitted_input.begin(), splitted_input.end(), (std::string) this->my_char_array[i]);
if ( value != end(splitted_input) ) {
matched_keywords.push_back(this->keywords[i]);
}
}
I am iterating over an const char*, looking for a literal that could be inside a vec<string>.
When I use the std::find algorithm, the for loop stops on the first iteration (std::cout just outputs the first value on my_char*_array).
Never faced an issue like that. Any idea?
Thanks in advice.
In this line:
for (int i = 0; i < sizeof(this->my_char_array); i++) {
you are using sizeof operator which is returning number of bytes which my_char_array occupies, and this is equal to size of pointer (8 bytes on x64 system) multiplied by number of pointers in your array. So this code is iterating over more elements than actually are in you array which is causing UB (undefined behaviour). The usual solution is to divide by element size:
for (int i = 0; i < sizeof(this->my_char_array)/sizeof(this->my_char_array[0]); i++) {
or even better, replace array with std::array, example:
static constexpr std::array<const char*, 2> my_char_array = {"dsds", "dddd"};
and
for (int i = 0; i < my_char_array.size(); i++) {
and don't forget to #include <array>
Related
in my c++ program I want to pass an array to a function and print the members of that array to console.
now I got into two problems:
int main()
{
unsigned char numbers[8] = { 1,2,3,4,5,6,7,8 };
for (auto i = 0; i < sizeof(numbers); i++)
{
std::cout << numbers[i] << "\n"; // First Problem: Here i get
}
logger(numbers);
}
passing the numbers to logger defined as void logger(unsigned char data[]) cause the type change to unsigned char * so there is no way to iterate over the array as the size is unknown.
my goal also is to pass any sized arrays but assuming that the size of an array is always 8, I changed the signature to
logger(&numbers)
void logger(unsigned char(*data)[8])
{
for (auto i = 0; i < sizeof(*data); i++)
{
std::cout << *(data[i]) << "\n";
}
}
iterating over data has the first problem and output is ``
so the questions are;
why do I get a weird ASCII character at cout.
how should we deal passing an array to another function and iterate over it, I searched alot but found no solution
The problem lies in the contents of your array:
unsigned char numbers[8] = { 1,2,3,4,5,6,7,8 };
Those get interpreted as character codes (because of the array element type)., not literal values. Most probably the character mapping used is ASCII, and characters 1 through 8 aren't printable.
To obtain the character value representing 1, you'd need to write a character literal '1'. If your intended to store and treat them as numbers, you could either change the type of the array to int[8], or cast them when printing:
std::cout << static_cast<int>(numbers[i]) << "\n";
As a side not, if you intended to use characters, you should change the type to char.
To solve passing the arrays of arbitrary size, either use a template and pass a reference to std::array, or simply use a vector.
You cannot pass an array to a function in C++. There are several ways around this
1) Use vectors instead of arrays
2) Pass a reference to the array (this only works with a fixed size array)
3) Pass a pointer to the first element of the array (this requires that you pass the size as a seperate parameter).
Here's how you do all three
1) use vectors
#include <vector>
std::vector<unsigned char>{1,2,3,4,5,6,7,8}:
logger(numbers);
void logger(const vector<unsigned char>& data)
{
for (auto i = 0; i < data.size(); i++)
{
std::cout << (unsigned)data[i] << "\n";
}
}
2) use a reference
unsigned char numbers[8] = { 1,2,3,4,5,6,7,8 };
logger(numbers);
void logger(unsigned char (&data)[8])
{
for (auto i = 0; i < 8; i++)
{
std::cout << (unsigned)data[i] << "\n";
}
}
3) use a pointer
unsigned char numbers[8] = { 1,2,3,4,5,6,7,8 };
logger(numbers, 8);
void logger(unsigned char *data, size_t size)
{
for (auto i = 0; i < size; i++)
{
std::cout << (unsigned)data[i] << "\n";
}
}
vectors are the best solution. C++ has proper data structures as standard, use them.
As has already been explained your printing problems are due to the special rules for printing characters, just cast to unsigned before printing.
No code has been tested (or even compiled).
For your first problem use:
int arr_size = sizeof(numbers)/sizeof(numbers[0]);
I have a very simple structure for adding size field to dynamic arrays:
template <typename T>
struct sized_array {
int size;
T* array;
};
I cannot use std::vector or std::array. The function to fill the array initializes the sized_array.array field and fills it with random integers:
void array_fill(sized_array<int> &array, int size = ARRAY_SIZE) {
array.array = new int[size];
array.size = size;
for (int i = 0; i < size; i++) {
array.array[i] = random_in_range(RANDOM_MIN, RANDOM_MAX);
}
}
The other functions, array_join and array_print print the contents of an array:
string array_join(sized_array<int> &array, string delimiter) {
string text = "";
for (int i = 0; i < array.size; i++) {
text += array.array[i];
if (i < array.size) text += delimiter;
}
return text;
}
void array_print(sized_array<int> &array) {
cout << "array(" << array.size << ") = [";
cout << array_join(array, ", ") << "]" << endl;
}
The array variable is declared like so, and the program runs this code:
sized_array<int> number_array;
int main() {
srand(time(NULL));
array_fill(number_array);
array_print(number_array);
system("pause");
return 0;
}
When debugging, the array shows this value when first initialized, then appears to take the first returned value of random_in_range and never change, staying at one element -- the first returned value.
When printed, the array appears to be filled with random ASCII characters, and the first element is never the one it was (even though the debugger displayed it had one element).
What is the cause of this and how to avoid this problem?
When printed, the array appears to be filled with random ASCII characters
This is because you have an error in your array_join function:
text += array.array[i];
This would append an int re-interpreted as char, not a decimal representation of the number.
Use std::to_string to fix the problem:
text += std::to_string(array.array[i]);
If you are restricted to a C++ version prior to C++11, use std::stringstream instead.
I'm eradicating std::string in favor of C-strings, which I'm new to. How do I get the following to compile? g++ complains: cannot convert char(*)[16] to char**
#include <iostream>
void print(char** s, int n)
{
for (int i = 0; i < n; ++i)
{
std::cout << s[i] << '\n';
}
}
int main()
{
constexpr int n = 3;
char s[n][16]{ "Hello", "Bye", "Sky"};
print(s, n);
}
You created a multidimensional array, not an array of pointers. Usually an array can be said to be equivalent to a pointer, however in this case c++ needs to know the size of the second dimension of your array. The function would be as follows
void print(char s[][16], int n)`{
for (int i = 0; i < n; ++i)
{
std::cout << s[i] << std::endl;
}
}
Understandably you may want to pass the function using pointers as to not make an entire copy of the 2-d array. I saw you mentioned you were okay with variable length strings. That functionality is supported in the string library. You are dealing with c-strings which are not strings at all but static arrays of type character. Defining these c-strings using dynamic memory happens to give you the desired behavior as you create in the simplest terms an array of pointers.
void print(char** s, int n)
{
for (int i = 0; i < n; ++i)
{
std::cout << s[i] << std::endl;
}
}
int main()
{
int n = 3, i;
char** s = new char*[n];
for (i = 0; i < 3; i++) {
s[i] = new char[16];
}
s[0] = "Hello";
s[1] = "Bye";
s[2] = "Sky";
print(s, n);
for (i = 0; i < 3; i++) {
delete [] s[i];
}
delete [] s;
s = NULL;
return 0;
}
Since you are using dynamic memory now you need to free your memory which is what the last loop serves to do. As you can see using all this dynamic memory is quite taxing and it would be easier to use the string library that has been optimized to do a much better job then you can. If you're still not convinced you should at least make your own string class to handle the dynamic memory that contains a char * as its private member. In either case you avoid this mess and just make an array of zed class objects and not deal at all with multidimensional nonsense. No one likes seg faults and memory leaks.
Given any type T, T arr[N]; declares a variable arr of type T[N], which is an array and not a pointer. When you use arr in almost all contexts, array to pointer conversions happen, giving the incorrect illusion that arr is a pointer of type T*.
char s[n][16] = { "Hello", "Bye", "Sky" };
declares s as an array of n elements of type char[16]. Now, when array to pointer conversion happens, s decays into a pointer of type char (*)[16]. Hence, your function needs to have the signature
void print(char (*s)[16], int n);
Which is equivalent to
void print(char s[][16], int n);
the [] is interpreted as a pointer by the compiler.
To make these complex types more readable, a type alias may be used.
using T = char[16];
void print(T s[], int n);
Addressing some concerns
As pointed out in the comments, std::string should almost always be preferred over a char array. If you have performance concerns, benchmark before doing this. I really doubt much performance gains can be observed in most cases.
Declaring an array with length n which is an int is not standard C++. It is an extension provided by your compiler, it is not portable and in most cases not necessary.
int n = 3;
char vla[n]; // this is a variable length array
char arr[3]; // this is just an array
char* darr = new char[3]; // this is a pointer pointing to dynamically allocated memory
std::string str; // but instead, this is just better
The compiler cannot extract from char ** the infomation about char[16]. You need to define a type char[16] and pass the pointer to this type to your print function.
#include <iostream>
typedef char str_t[16];
void print(str_t* s, int n)
{
for (int i = 0; i < n; ++i)
{
std::cout << s[i] << std::endl;
}
}
int main()
{
int n = 3;
char s[n][16]{ "Hello", "Bye", "Sky"};
print(s, 3);
}
We are not allowed to use vectors.
Another noobie with a problem. I am trying to find the index of a dynamic int array that is initialized to a size of 50. We can only use a dynamic int array.
Say for example I’m only entering 10 integers into this array: 1 9 5 3 8 0 8 2 0 6
The rest of the array is NULL. The goal is to keep the integer values that were entered into this array and put them into an output file. So the size of the array of ints I want is 9. But when I do the following:
int index = 0;
while (intArray[index])
{
index++;
}
It tells me that the index is 5 and thus only 5 values get copied into the output file. I know it’s because of the 0 in the array. If there is no 0 in the array, the results come out as expected. How do I bypass this so that the index is correctly reflected as 9 and all the values get copied properly? I appreciate any input!
As pointed out in the comments the best approach here, given that you cannot use std::vector (*sigh*), is to make your own minimal dynamic_array (mimicking a std::vector) which knows its own size at all times.
It's interface could look something like this:
template<typename _Ty>
class dynamic_array {
public:
typedef _Ty value_type;
typedef const _Ty& const_reference;
typedef _Ty& reference;
typedef std::size_t size_type;
//... other necessary typedefs
dynamic_array() : arr(), arr_size(0), allocated_size(0) {}
dynamic_array(size_type n) : arr_size(n), allocated_size(n) { allocate(n); }
~dynamic_array() { delete arr; }
size_type size() const noexcept { return arr_size; }
const_reference operator[](size_type n) const { return arr[n]; }
reference operator[](size_type n) { return arr[n]; }
void push_back(const value_type& _val) {
++arr_size;
// actual implementation of pushing back _val up to you
}
private:
value_type* arr;
size_type arr_size; // number of elements
size_type allocated_size; // actual number of allocated elements in memory
void allocate(size_type n) { arr = new value_type[n]; }
};
Then to iterate through this via indices you would simply do:
dynamic_array<int> darr;
// populate darr
for (int i = 0; i < darr.size(); ++i) {
// do stuff with each element accessing via: darr[i] as before
}
Edit - If you can use std::unique_ptr then use this instead of the raw pointer value_type* arr to avoid any potential memory management headaches.
There is no such thing as NULL for integer value. So there are 3 usual approaches, that used:
Special value, marker of the end. This aproach is used for C-style strings, where '\0' used as end. For integer this approach will not work as 0 is "normal" value. You may use some other special value, like std::numeric_limits<int>::min or std::numeric_limits<int>::max, but this would be not very good approach.
Have another variable, that holds current size of array. Note this is usually different, than for how many elements were allocated. This could be wrapped into a class. This is how std::vector is implemented.
Have another variable, pointer, that points to the pone element after the last one. This could be useful for using in standard algorithms.
Approach 2 and 3 are very close and one can be converted very easy to another. So I would recommend to rewrite your algorithm using start and behind_end iterator:
auto end = myArray + 5; // 5 elements in array
for( auto it = myArray; it != end; ++it ) {
std::cout << *it << endl;
}
this code can be easily templatized and used with standard containers.
You could do something like this only if you are sure that the input data are not negatives:
#include <iostream>
int main()
{
int MyArray[50]; // declare your array
// ..
for (int i = 0; i < 50; i++)
MyArray[i] = -1; // fill with negatives
MyArray[0] = 0; // some data
MyArray[1] = 10; // some data
for (int i = 0; i < 50; i++)
{
if (MyArray[i] >= 0 )
{
std::cout << MyArray[i];
// write to your file
}
}
//..
return 0;
}
boost::static_vector would help here. It is essentually a cross between vector and array: either a stack-allocated vector with fixed capacity or uninitialized array with some facilities to track logical size.
Example of use:
#include "boost/container/static_vector.hpp"
#include <iostream>
namespace cnt = boost::container;
void foo()
{
cnt::static_vector<int, 50> buf = {1, 9, 5, 3, 8, 0, 8, 2, 0, 6,};
std::cout << buf.size();
}
int main()
{
foo();
}
I know eventually I need to change trigram, whose one space contains 3 characters from the former string, into a dynamic array to solve this problem, but I tried to set my array's capacity large enough at first. However, when I compile my code, the error appears.
#error: variable length array of non-POD element type 'string' (aka 'basic_string<char>'#
Code:
//global variable
int CAPACITY = 1000;
int main()
{
//a string that reads in the language of the text
string language = "";
//a string that reads in the file name of the text
string filename = "text.txt";
//a string that reads in the original text characters
string original = "";
//a string that reads in the modified original array
string rid_of_spaces = "";
//an array with capacity that stores the trigrams
string trigrams[CAPACITY];
ifstream finput;
char c;
//the length of an array
int sLength = 0;
//the tracker for trigrams
int counter = 0;
cin >> language >> filename;
finput.open(filename.c_str());
while (finput.get(c)){
//to test if the character is alpha
if (isalpha(c)){
//change the alphabet to lowercase
c = tolower(c);
//store the modified letter in the array
original += c;
}
//change any other characters into a space
else original += ' ';
}
sLength = original.length();
//loop through the original array and change mutiple spaces into one
for (int i = 0; i < sLength; i++){
if (isalpha(original[i]))
rid_of_spaces += original[i];
else {
while (original[i] == ' ')
i++;
rid_of_spaces += ' ';
rid_of_spaces += original[i];
}
}
sLength = rid_of_spaces.length();
for (int i = 0; i < CAPACITY; i++)
trigrams[i] = 0;//initialize each element to 0
for (int i = 0; i < sLength - 2; i++){
trigrams[counter] += rid_of_spaces[i]
+ rid_of_spaces[i + 1]
+ rid_of_spaces[i + 2];
counter++;
}
cout << filename << endl;
cout << original << endl;
cout << rid_of_spaces << endl;
for (int i = 0; i < counter; i++)
cout << trigrams[i] << endl;
finput.close();
return 0;
}
The variable
int CAPACITY = 1000;
should be a constant
const int CAPACITY = 1000; // or with c++11 constexpr int CAPACITY = 1000;
for
string trigrams[CAPACITY];
because "ISO C++ forbids variable length array 'trigrams'" (g++ message)
And this
for (int i = 0; i < CAPACITY; i++)
trigrams[i] = 0;//initialize each element to 0
should be
for (int i = 0; i < CAPACITY; ++i)
trigrams[i] = "";//initialize each element to 0
You don't "initialize [strings] to 0" but with a zero length C-string. A zero length C-string is not an invalid 0-pointer, but a (valid) pointer to a char with value 0;
Generally, it's better not to use C-arrays if there are STL-means to avoid them; with c++11, std::array<std::string, CAPACITY> would be preferable here if you want to stay with the "capacity large enough" approach.
live at Coliru's
I took the liberty to change all i++ to ++i in the for-loops' heads while at it; see eg. What is the difference between ++i and i++ for the rationale behind that.
For a dynamic (without pre-defined bounds) array use std::vector<std::string> trigrams;,
push_back or emplace_back your strings into that vector,
and for i- iterate
for (std::size_t i = 0; i < trigrams.size(); ++i) {/* ... */}
Or use the iterator-interface of std::vector, e.g.
std::for_each(trigrams.begin(), trigrams.end(),
some_function_or_functor_that_does_the_job);
(see std::foreach here ),
or with c++11 just
for (auto& s : trigrams) {/* ... */}
unless you need to customize the iteration like you do it inside your second loop.
The variable CAPACITY is not a compile-time constant variable, and variable-length arrays are not in C++ (though some have it as an extension, but apparently not for all types).
There are two solutions to your problem:
Turn the variable into a compile-time constant, either by making it constexpr alternatively const (for older compilers), or by defining it as a preprocessor macro.
Use std::vector, like std::vector<std::string> trigram(CAPACITY);
My suggestion is that you use both solutions above, at least if you later need to resize the vector. If the size will be fixed at compile-time and never change, then use the first solution and use std::array instead of a C-style array:
constexpr std::size_t CAPACITY = 1000;
...
std::array<std::string, CAPACITY> trigram;
The size of a C++ array must be a constant expression. You declare it as int CAPACITY = 1000;, which is not a constant expression. Adding a const qualifier solves the issue: int const CAPACITY = 1000;.
However, you should avoid plain arrays. Try using std::array if you know the size at compile time, or std::vector if not.