I've been running into this weird issue where the split code returns correctly when I printf output inside the function, but will incorrectly return output upon calling it as an instance.
Question: How do I get the correct ouput when calling it as an instance?(see useage bellow)
Here is the code:
typedef struct SplitText
{
int splitLen;
char* splitTxt[100];
char* subTxt(char* text, int index, int len)
{
char subTxt_[1000];
int count = 0;
for (int i = 0; i < 1000; i++)
subTxt_[i] = '\0';
for (int i = index; i < index + len; i++)
subTxt_[count++] = text[i];
return subTxt_;
}
void split(char* text, char sep)
{
char separator[3] = { '<', sep, '>' };
int textLen = strlen(text);
int splitIndex = 0;
int splitCount = 0;
for (int t = 0; t < textLen; t++)
{
if (text[t] == separator[0] && text[t + 1] == separator[1] && text[t + 2] == separator[2])
{
if (splitIndex != 0)
splitIndex += 3;
splitTxt[splitCount] = subTxt(text, splitIndex, t - splitIndex);
splitIndex = t;
//correct output
printf(splitTxt[splitCount]);
printf("\n");
splitCount++;
}
}
splitLen = splitCount;
}
}SplitText;
Useage:
SplitText st;
st.split("testing<=>split<=>function<=>", '=');
for (int i = 0; i < st.splitLen; i++)
{
//incorrect output
printf(st.splitTxt[i]);
printf("\n");
}
printf("--------\n");
This:
char* subTxt(char* text, int index, int len)
{
char subTxt_[1000];
...
return subTxt_;
}
Is undefined behavior. Returning a pointer to a local stack variable (or local array var) is going to result in weird stuff like this happening.
The typical thing that corrupts the contens of that returned pointer is when another function is invoked, the memory occupied by subTxt_ is going to get overwritten with the stack variables of the next function invoked.
Better:
char* subTxt(char* text, int index, int len)
{
char *subTxt = new char[1000];
...
return subTxt_;
}
And then make sure whoever invokes subTxt remembers to delete [] on the returned pointer.
Or just use std::string and be done with it (unless this is an academic exercise).
Also, this is undefined behavior:
for (int t = 0; t < textLen; t++)
{
if (text[t] == separator[0] && text[t + 1] == separator[1] && text[t + 2] == separator[2])
when t == textLen-1, then referencing text[t+2] and text[t+1] is an out of bounds access. Change it to be:
for (int t = 2; t < textLen; t++)
{
if (text[t-2] == separator[0] && text[t -1] == separator[1] && text[t] == separator[2])
And do similar fixups with t within the block as well.
Well you can create a splitstring function instead of a struct/class.
Anyway your code still looks quite "C" like with its fixed size char arrays. This will limit the usability and stability (out-of-bound array bugs).
Strings in C++ are usually of type std::string.
and then C++ has string_view to make views on that string (so no data gets copied, but it also means your string_view is only valid for as long as the string it is viewing lives).
If you don't know the number of substrings in a string up-front, you should not use a fixed size array, but a std::vector (which can resize internally if needed)
This is what a split_string function would look like in current C++, note that the code also shows better what it is doing compared to "C" style programming that show more what you are doing.
std::vector<std::string_view> split_string(std::string_view string, std::string_view delimiters)
{
std::vector<std::string_view> substrings;
if(delimiters.size() == 0ul)
{
substrings.emplace_back(string);
return substrings;
}
auto start_pos = string.find_first_not_of(delimiters);
auto end_pos = start_pos;
auto max_length = string.length();
while(start_pos < max_length)
{
end_pos = std::min(max_length, string.find_first_of(delimiters, start_pos));
if(end_pos != start_pos)
{
substrings.emplace_back(&string[start_pos], end_pos - start_pos);
start_pos = string.find_first_not_of(delimiters, end_pos);
}
}
return substrings;
}
Take a look at std::string_view.
You can avoid allocating memory and it has a built-in substring function.
Just be careful when using printf for printing to console as "%s" will
print the whole string.
See printf documentation.
for(auto view : container_with_string_views)
printf("%.*s, (int)view.size(), view.data());
Related
Written code to find and remove the largest word in a string without the using of library functions. Everything works fine. But when I want to free memory, the result is negative (displays an empty line). If you remove the call to the memory release function, everything will work correctly, but there will be a leak of memory.
How do I fix it? Please help me.
#include <iostream>
using namespace std;
int length(char *text) // string length
{
char *begin = text;
while(*text++);
return text - begin - 1;
}
int size(char **text) // size of two-dimensional array
{
int i = 0;
while(text[i]) i++;
return i;
}
void free_memory(char **text)
{
for(int i=0; i<size(text); i++)
delete text[i];
delete [] text;
}
char **split(char *text, char delim)
{
int words = 1;
int len = length(text);
for(int i=0; i<len; i++)
if(text[i] == delim) words++;
char **result = new char*[words + 1];
int j = 0, t = 0;
for(int i=0; i<words; i++)
{
result[i] = new char[len];
while(text[j] != delim && text[j] != '\0') result[i][t++] = text[j++];
j++;
t = 0;
}
result[words + 1] = nullptr;
return result;
}
char *strcat(char *source, char *destination)
{
char *begin = destination;
while(*destination) destination++;
*destination++ = ' ';
while(*source) *destination++ = *source++;
return begin;
}
char *removeWord(char *in_string)
{
char **words = split(in_string, ' ');
int max = length(words[0]);
int j = 0;
for(int i=0; i<size(words); i++)
if(max < length(words[i]))
{
max = length(words[i]);
j = i;
}
int index;
char *result;
if(!j) index = 1;
else index = 0;
result = words[index];
for(int i=0; i<size(words); i++)
if(i != j && i != index)
result = strcat(words[i], result);
free_memory(words); // I want free memory here
return result;
}
int main()
{
char text[] = "audi and volkswagen are the best car";
cout << removeWord(text) << endl;
return 0;
}
In fact, this is C style programming - not C++. I see that your aim is to implement everything from scratch, possibly for practicing. But even then, your code is not designed/structured properly.
Besides that, you also have several bugs in your code:
result[words + 1] = nullptr; must be result[words] = nullptr;
You need result[i][t] = '\0'; after the while loop in split
delete text[i] must be delete [] text[i]
You cannot assign to your result pointer memory from words, then free it and then return it for use by the caller.
There is at least one further bug in the second half of removeWord. It would be tedious to try to understand what you are trying to do there.
You might want to start with a simpler task. You also should proceed step-by-step and check each function for correctness independently first and not implement everything and then test. Also take a look at the tool valgrind for memory checking - if you use Linux.
The way you free memory correctly is to use RAII:
Only use new and new[] in constructors
Pair those with delete and delete[] in the corresponding destructor
Use automatic storage duration objects as much as possible
If you are specifically not using std::string and std::vector etc, for reasons of learning pointers, you will end up writing some small number of classes that resemble string and vector and unique_ptr, and then you go about programming as if you were using the std versions.
You have two issues. First is that result is assigned to a memory location in words. Second, is that you're storing the result of strcat in words[i] which will likely not have enough room (see strcat documentation).
result = new char[len(in_string)+1]; // +1 for space for null char
// the old loop reversed the word order -- if you want to keep doing
// that, make this a descending loop
for(int i=0; i<size(words); i++)
if(i != j && i != index)
strcat(result, words[i]);
free_memory(words);
return result;
So that when you free words, what result points to is also free'd. You would then need to free your result in main().
int main()
{
char text[] = "audi and volkswagen are the best car";
char * result = removeWord(text);
cout << result << endl;
delete[] result;
return 0;
}
This problem comes from a textbook: Programming -- Principles and Practice Using C++ (Second Edition) By Bjarne Stroustrup.
Note: I have solved the problem, the question comes from my lack of understanding as to how my earlier attempts at a solution failed. I know it is a long question but I feel that it is important to understand this concept.
Here is the problem:
Write a function, char* findx(const char* s, const char* x), that finds the first occurrence of a C-style string x in s.
I could easily have looked up the solution (or alter the way the function is declared) but if I feel like this issue is the whole point of the exercise, so I held off on that. Feel free to correct any wrong statements I make in this question. Also, forgive me if this is an over complication but I really don't understand this.
For starters, here is my function written in a way that compiles, but causes a segfault at runtime:
char* findx(const char* s, const char* x) {
if (s==nullptr || x == nullptr) return nullptr; //if no string, return nullptr
int s_size = strlen(s);
int x_size = strlen(x);
char* ptr_to_first_occ = nullptr; //initialize pointer to 1st occ
for (int i = 0; i < (s_size-x_size); ++i) { //end with the last char that
//can hold string x to the end
for (int j = i; j < x_size; ++j) { //check if the first char match
if (s[j] != x[j]) ptr_to_first_occ = nullptr; //if it doesn't, set to 0
//and move on
else ptr_to_first_occ = (char*)(s) + i; //set equal to first occ if ==
}
}
if (ptr_to_first_occ) return ptr_to_first_occ;
else return nullptr;
}
Here is main:
const char* y = "Hey how are you?";
const char* t = "you";
char* p = nullptr;
p = findx(y, t);
cout << *p << endl;
The only reason this compiles is due to the fact that I cast the const char s to char*, which is not the right way to do things, but I am pretty sure the segfault is not caused by this as my solution includes the cast.
My goal here is to return a pointer to the memory location that holds the first character of string x in string s. The problem is that the first character itself is contained within a string that is passed as const in the first place, so the pointer to it is of type const char , not char as is the pointer that I declare. If I want to declare ptr_to_first_occ as a const char*, I can no longer assign the proper location to it since it is not writeable. This leaves me to either completely change the function (I am expected to return a char*, I could probably do it another way rather quickly) or to use a cast. Or I can change that part as such:
for (int i = 0; i < (s_size-x_size); ++i) {
for (int j = i; j < x_size; ++j) {
if (s[j] != x[j]) ptr_to_first_occ = nullptr;
else {
const char* ptr_to_first_occ = &s[i];
}
}
}
Problem is that the declaration makes it so that this variable only exists within the scope of that else, and I can't use a dummy variable to store that const char* before the the pointer goes out of scope, because I can't assign to const variables that were declared before.
At this point I realize that this:
for (int j = i; j < x_size; ++j) {...}
makes no sense, and should actually be:
for (int j = i; j < i + x_size; ++j) {...}
Alright, fair enough. My function is now this:
char* findx(const char* s, const char* x) {
if (s==nullptr || x == nullptr) return nullptr;
int s_size = strlen(s);
int x_size = strlen(x);
char* ptr_to_first_occ = nullptr;
for (int i = 0; i < (s_size-x_size); ++i) {
for (int j = i; j < i+x_size; ++j) {
if (s[j] != x[j]) ptr_to_first_occ = nullptr;
else {
ptr_to_first_occ = (char*)&s[i];
}
}
}
if (ptr_to_first_occ) return ptr_to_first_occ;
else return nullptr;
}
Compiles, and logically seems to work, but I still get a segfault due to the same reason I suspect (Segmentation fault: 11). I now change main to this:
const char* y = "Hey how are you?";
const char* t = "you";
char* p = nullptr;
p = findx(y, t);
if (p) cout << *p << endl;
Turns out that this prints nothing, so p must have remained a nullptr. Anyway, after many debugging flags, and a bunch of trial and error (read: rewriting the function in a way that made it look like a dumpster) I cleaned up the whole thing, and managed to solve the problem:
char* findx(const char* s, const char* x) {
if (s==nullptr || x == nullptr) return nullptr;
int s_size = strlen(s);
int x_size = strlen(x);
char* ptr_to_first_occ = nullptr;
for (int i = 0; i < (s_size-x_size); ++i) {
if (s[i] != x[0]) ptr_to_first_occ = nullptr;
else {
for (int k = 0; k < x_size; ++k) {
if (s[i+k] == x[k]) ptr_to_first_occ = (char*)&s[i];
else {
ptr_to_first_occ = nullptr;
break;
}
}
}
}
if (ptr_to_first_occ) return ptr_to_first_occ;
else {
return nullptr;
}
}
Which means that the cast had nothing to do with the segmentation fault, and this is essentially where my question comes from. I simply could not track down the reason for the fault in my earlier code. I would like some input as to how to understand the cause of a segmentation fault as it relates to memory locations, since my understanding of how the read only memory locations work was wrong. What was I trying to do that caused my error?
I want to remove double-quotes from a string, for example 13.3" Rentina becomes 13.3 Rentina
const char* s = sheet->readStr(row, col);
int ii = strlen(s);
char* b;
b=(char*)s;
char ch;
for (int i = 0; i < ii ;++i) {
strncpy(&ch, b+ii, 1);
if(ch == '\"'){
ch = '\"';
memcpy(b+i, &ch, 1);
}
}
myfile << b;
If you deal with strings in C++, you should use character arrays and functions like strncpy only when you have a strong reason to use them. By default you should use standard string, which makes e.g. memory management much easier. The solution to your problem with std::string is
std::string s = sheet->readStr(row, col);
size_t pos = 0;
while ((pos = s.find('"', pos)) != std::string::npos)
s = s.erase(pos, 1);
myfile << s;
You cannot do b=(char*)s!!!
The compiler lets you in on this one, but you will get a runtime exception as soon as you attempt to write into the memory address space pointed by b.
The variable s is possibly pointing to an address in the code-section, which is a read-only memory address space within your program ("possibly", because perhaps that const declaration of s is just something you added on your own initiative).
You should allocate a new char array, and copy the output string into that array instead.
So first of all, change the above statement to b = (char*)malloc(strlen(s)).
In addition, do not pass to strncpy (or any other str function for that matter) an address of a char variable. These functions operate on char arrays, and either assume that the array ends with a 0 character, or set the character at the end of the array to 0.
You can try the following piece of code (assuming that your purpose is to remove the '"'):
const char* s = sheet->readStr(row, col);
int ii = strlen(s);
char* b = (char*)malloc(ii+1);
if (b != NULL)
{
int i,j;
for(i=0,j=0; i<ii; i++)
{
if (s[i] != '"')
b[j++] = s[i];
}
b[j] = 0;
// Add your code here (do whatever you wanna do with 'b')
free(b);
}
else
{
printf("Out of memory\n");
}
I'm solving a problem that "replace all spaces in a string with ‘%20’." and I want to operate on the original string instead of create a new string. Here is my code:
void replaceSpaces(char* s, int len) {
int spaceCnt = 0;
for(int i = 0; i < len; ++i) {
if(s[i] == ' '){
++spaceCnt;
}
}
int newlen = len + 2 * spaceCnt;
s[newlen] = '\0';
for(int i = len - 1; i >= 0; --i) {
if(s[i] == ' ') {
s[newlen - 1] = '0';
s[newlen - 2] = '2';
s[newlen - 3] = '%';
newlen -= 3;
} else {
s[newlen - 1] = s[i];
--newlen;
}
}
}
And I have an "thread: exc_bad_access" error in the line s[newlen] = '\0';. I know it's dangerous to operate c-stye string in this way but I don't know how to modify it...
Any explanations or suggestions will be appreciated!
Well, if the original buffer is not long enough to contain the string with replacements, you are accessing memory out of bounds. When you determine the new size, you could do a realloc call to reallocate a suffiecient buffer with the size newlen like *s = realloc(*s, newsize). The only issue is that you need to change the paramter from char* s to char** s so that if realloc moves the memory to another block you update the pointer. And of course, this will work with strings allocated on heap, not local stack strings, since you can't reallocate that.
I'm trying to copy data from an array of character that send from main to another one in my local function and I always see garbage characters even though I've add '\0' at the end of the string.
Here is my partial of the code.
for (int i = 0; i < strlen(main) ; i++){
if (main[i] != ';'){
local[i] = main[i]; // Copy the characters until `;` isn't found
} else {
local[i] = '\0' ; // If `;` found, null terminate the copied destination.
break;
}
}
so basically the data that being send from main for example like this
look;can;you;see;me
My Local-----> 'look??y??>c?Lw?T?w??>c?2+a?'
Actual data in main---> 'look'
As you can see from the above example I'm trying to get only the first word and I always get garbage I don't know why?
EDIT:
This is the almost the whole function which 100% sure that is causing me the problem.
void myFunction(char main[ ]){
for (int i = 0; i < strlen(main) ; i++){
if (main[i] != ';'){
local[i] = main[i]; // Copy the characters until `;` isn't found
} else {
local[i] = '\0' ; // If `;` found, null terminate the copied destination.
break;
}
}
if(main[i] != '\0'){
int col = 0, row = 0;
do {
if(main[i] == ';' || main[i] == '\0') {
sending[row++][col] = '\0';
col = 0;
} else {
sending[row][col++] = main[i];
}
} while(main[i++] != '\0');
}
}
You are forgetting to take care of zero terminating the string if the ; is not found. A simple fix is tweaking your for loop so it also sees the \0 in main:
for (int i = 0; i <= strlen(main); i++) {
The standard library handles this for you. Using strchr and strncpy:
size_t length = std::strlen(main);
const char* current_pos = main;
for (int i = 0; ; ++i) {
size_t chars_remaining = length - std::distance(main, current_pos);
const char* end_of_field = std::strchr(current_pos, ';');
if (end_of_field == NULL) {
std::strncpy(local[i], current_pos, chars_remaining + 1);
// we're at the end of the input
break;
}
else {
size_t field_length = std::distance(current_pos, end_of_field);
std::strncpy(local[i], current_pos, field_length);
// don't forget to NUL-terminate the string
local[i][field_length] = '\0';
// go to next character for the next iteration through loop
current_pos = end_of_field + 1;
}
}
Personally, I prefer std::find and std::copy (from <algorithm>):
size_t length = std::strlen(main);
const char* current_pos = main;
for (int i = 0; ; ++i) {
size_t chars_remaining = length - std::distance(main, current_pos);
const char* end_of_field = std::find(current_pos, current_pos + chars_remaining, ';');
char* output_end = std::copy(current_pos, end_of_field, local[i]);
// don't forget to NUL-terminate the string
*output_end = '\0';
// if we're at the end of main, then we're done;
// we're at the end if we're on a NUL character
if (*end_of_field == '\0')
break;
// go to next character for the next iteration through loop
current_pos = end_of_field + 1;
}
Not the prettiest code I've ever written, but that's largely due to using C-style strings and pointer arithmetic, which don't look avoidable given the original question. Additionally, I haven't put in the needed checks for overflow. It's easy enough to do that, but it's even easier to use std::vector<std::string> and have the standard library worry about that for you.