C++ Copying an array of chars using char* (no string libraries) - c++

I am writing a C++ function that is supposed to duplicate an array of chars by copying each element character-by-character into a new array. Ideally, if I make the statements
char* a = "test";
char* b = copyString(a);
then both a and b should contain the string "test." However, when I print the copied array b, I get "test" plus a series of nonsense characters that seem to be the pointer. I don't want those, but I can't figure out where I'm going wrong.
My current function is as follows:
char* copyString(char* s)
{
//Find the length of the array.
int n = stringLength(s);
//The stringLength function simply calculates the length of
//the char* array parameter.
//For each character that is not '\0', copy it into a new array.
char* duplicate = new char[n];
for (int j = 0; j < n; j++)
{
duplicate[j] = s[j];
//Optional print statement for debugging.
cout << duplicate[j] << endl;
}
//Return the new array.
return duplicate;
}
For the purposes of understanding certain aspects of C++, I cannot use string libraries, which is where other answers I have found have fallen short in this case. Any help with this problem is greatly appreciated.
EDIT: I though my stringLength function was fine - perhaps I was wrong.
int stringLength(char* s)
{
int n;
//Loop through each character in the array until the '\0' symbol is found. Calculate the length of the array.
for (int i = 0; s[i] != '\0'; i++)
{
n = i + 1;
}
//Optional print statement for debugging.
// cout << "The length of string " << s << " is " << n << " characters." << endl;
return n;
}

You need to copy the 0 too. That's what a C-style string is, a null-terminated character array.
Really, all you need to do is add one to the length:
int n = stringLength(s) + 1; // include the '\0'
And then everything else will account for itself - you'll allocate an array of sufficient size, and copy the '\0' in your loop too.

Related

C++: How to allocate and fill dynamic array of a struct passed by reference?

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.

Change pointer value after function block

I have this code:
#include <iostream>
#include <string.h>
using namespace std;
void copyString(char *input, int offset, int length, bool invert, char *output, int output_offset)
{
char *cp = new char[length+1];
for (int i = 0; i < length + 1; i++)
{
cp[i] = input[offset + i];
}
if (invert)
{
for (int i = 0; i < length/2; i++)
{
swap(cp[i], cp[length - i - 1]);
}
}
int count = 0;
while (output[count])
count++;
int cutlength = count - output_offset;
char *temp = new char[count + 1];
for (int i = 0; i < count + 1; i++)
temp[i] = output[i];
for (int i = 0; i < cutlength; i++)
{
temp[output_offset + i] = cp[i];
}
output = temp;
}
void main()
{
char *st = "Hello world";
cout << "st= " << st << endl;
char *st2 = "My name is C++";
cout << "st2= " << st2 << endl;
copyString(st, 6, 5, true, st2, 11);
cout << "st2 output= " << st2 << endl;
system("Pause");
}
The idea is that the function will copy a length of an input string and replace a part of an output with that copied string.
All i want to do is make the st2 value change after copyString function, but i can't seem to change it through temp var. But if i try to change the st2 value in the function, i got the Access Violation Error. Any idea how to fix this?
st and st2 are pointers to a constant string, and as such you shouldn't try to change there contents (although you can make them point at an entirely different string).
To make st2 a character array (string) that is editable, you should declare st2 as follows
char st2[] = "My name is C++";
In your copyString function, the output pointer originally points at the same place as st2, and then at the end of the function make output point at a different string. However this wont change st2 or its contents in any way.
You need either need to make st2 a character array and edit this array in the copyString function, or have the copyString function create and return a new string that st2 can point at (although likely to end up with memory leaks if your not careful).
The following should do what you are after. It can could be improved a little by removing cp altogether and directly writing from the input buffer to output buffer.
void copyString(char *input, int offset, int length, bool invert, char *output, int output_offset)
{
char *cp = new char[length+1];
for (int i = 0; i < length + 1; i++)
{
cp[i] = input[offset + i];
}
if (invert)
{
for (int i = 0; i < length/2; i++)
{
swap(cp[i], cp[length - i - 1]);
}
}
int count = strlen(output);
int cutlength = count - output_offset;
for (int i = 0; i < cutlength; i++)
{
output[output_offset + i] = cp[i];
}
delete[] cp;
}
void main()
{
char st[] = "Hello world";
cout << "st= " << st << endl;
char st2[] = "My name is C++";
cout << "st2= " << st2 << endl;
copyString(st, 6, 5, true, st2, 11);
cout << "st2 output= " << st2 << endl;
system("Pause");
}
Your copyString function is generally similar to the C strcpy function (if not to take the inversion option into the account). It needs the output to be an already allocated buffer (a char array) of a sufficient size to write the string content into (say, you may allocate a buffer using new char[strlen(str) + 1] ) . However, you are trying to write into the memory where the string constants "Hello world" and "My name is C++" are located. This is obviously a memory access violation (otherwise you would modify those constants).
No need to say that this way of dealing with strings is extremely error prone and far not C++ish.
1) You count the output length (undefined behavior!), not the input one.
2) output pointer goes to nowhere - do you want to use a reference function argument for it?
Memory access violation is due to your are trying to modify a string, which size can't accommodate extra length (i.e. st2 in your case)
Note: Temp method worked without error as your updated the base address of st2 <-- temp.
Also boundary checking against offset & input params lengths are also recommended.
In general use of strcpy_s is preferred over strcpy.

error: variable length array of non-POD element type 'string' (aka 'basic_string<char>')

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.

Reverse char string with pointers

I need reverse my char string only with pointers. How can I do this? My code:
// this cannot be modified !!!
char s[10] = "abcde";
char *pS;
// my code
pS = new char;
int count = 5;
for (int i = 0; i < 10; i++)
{
if (s[i] != '\0') // not null
{
pS[count - 1] = s[i];
count--;
}
}
cout << "Reversed = " << pS;
Sometimes if works fine, I see only 5 chars, they are reversed. But sometimes I see some extra chars (looks like temp symbols). Where I miss something? Thank you!
your char array "s" contains 10 chars, but you only initialize the first 6 chars of that array with "abcde" and the \0 terminator.
When you loop over the complete array, you access not initialized chars.
I also see, that you try to write to memory, which you didn't allocate.
You only allocate memory for 1 char for you "pS" pointer, but you try to access it's memory like it is an array of chars in your for-loop.
Instead of using hardcoded:
int count = 5;
you also could use the string function strlen() to determine the length of the c-string.
Edited (untested code):
char s[10] = "abcde";
char pS[10];
for (int i = 0; i < strlen(s); i++)
{
if (s[i] == '\0') // not null
{
// stop loop, as soon as you reach the end of the original string
break;
}
pS[strlen(s) - 1 - i];
}
// now add the termination char \0 to your pS array
pS[strlen(s)] = '\0';
cout << "Reversed = " << pS;
Just giving you the hint how to reverse the string using pointers:
Take two pointers front and rear where front is pointing to first char of string and rear is pointing to last char of string.
Check if front is less than rear
If yes, swap the value of first and last character. If no , just print the string.
Increment front pointer and decrement rear pointer
Repeat from step 2.
After reading another book I fully understand pointers and how to correctly allocate memory. Here is my final code which correctly reverse array of char string (I don't need universal code, just working example + without std methods for reversing):
// not edited part - based on exercise (I mean I cannot change pS to char[5] etc.
char s[10] = "abcde";
char *pS;
pS = new char[strlen(s) + 1]; // allocate correct memory size based on string size
cout << "Size is " << sizeof(pS) << endl; // just for testing
int count = strlen(s); // for iteration
pS[count] = '\0'; // last symbol must be '\o' (thanks to Mr.Yellow)
for (int i = 0; i < 10; i++) // 10 because array of char still has 10 elements
{
if (s[i] != '\0') // looks like "not garbage memory"
{
count--;
pS[count] = s[i]; // set correct value
}
}
cout << "Reversed = " << pS << endl;
Thank you to all who helps me!

Why is my Dynamic Array of Char's still returning uninitialized values?

For some weird reason, it keeps on creating uninitilized values when I pass in the length as 12, it creates an array of about 16 and stores the rest with crap that I don't want. Anyone know why this isn't working? It's for an assignment that's due tomorrow and this is my last problem... Any help would be appreciated thanks.
char * convertToUppercase (char* toUpSize, int length) {
std::cout << "ToUpsize: " << toUpSize << "\nLength: " << length << "\n";
char * upsized = new char[length];
for (int i = 0; toUpSize[i]; i++) {
upsized[i] = toupper(toUpSize[i]);
}
return upsized;
}
I think you either write i< length in the for loop, instead of toUpSize[i] as:
for (int i = 0; i < length; i++) {
upsized[i] = toupper(toUpSize[i]);
}
Or pass toUpSize as null-terminated string if you want to write toUpSize[i] in the for loop condition. If you do so, then you've to put \0 at the end of upsized after you exit from the loop, at index i for which toUpSize[i] is \0. And to accomplish this, you've te move the definition of i outside the for loop, so that you can use it after you exit from the loop.
Null-terminated string is what which has \0 character at the end of the string.
char x[] = {'N', 'a', 'w', 'a', 'z' };
char y[] = {'N', 'a', 'w', 'a', 'z', '\0' };
Here, x is not a null-terminated string, but y is a null-teminated string.
If the strings are defined as:
char z[] = "Nawaz";
const char *s = "Nawaz";
Here z and s are null-terminated string, because both of them are created out of "Nawaz" which is a null-terminated string. Note that sizeof("Nawaz") would return 6, not 5, precisely because there is an \0 at the end of the string.
You need to null-terminate the returned array if you want to print it like a string. Make sure that it ends with a null-terminator. Depending on how you calculate the length argument you may need to add extra space for it to the array. You may also want to make sure that the array that you pass in is null-terminated.
You need to add the termination char:
char * convertToUppercase (char* toUpSize, int length) {
std::cout << "ToUpsize: " << toUpSize << "\nLength: " << length << "\n";
char * upsized = new char[length];
int i;
for (i = 0; toUpSize[i]; i++) { // stops when you get toUpSize[i]==0
upsized[i] = toupper(toUpSize[i]);
}
upsized[i] = '\0'; //add termination
return upsized;
}
Your code assumes length to be the length of the allocated array, not the length of the string. strlen(toUpSize) counts the chars that are not '\0' from position 0 in toUpSize.
E.g.: strlen("abc\0def") -> 3
sizeof("abc\0def") -> 8!
Why are you even bothering with char pointers? This is C++, not C.
#include <string>
#include <algorithm>
#include <cstring>
#include <iostream>
std::string to_upper_case(std::string str)
{
std::transform(str.begin(), str.end(), str.begin(), toupper);
return str;
}
int main()
{
std::cout << to_upper_case("hello world\n");
}
If you decide to stick to the C solution, reserve one more char for the NUL terminator and put it there:
char * upsized = new char[length + 1]; // note the +1
upsized[length] = 0;