I am trying to implement function that searches for match between two C-style strings, as a part of an exercise from "Programming: Principles and Practice using C++".
However, I am getting a runtime unhandled exception:
Access violation reading location 0x008ff000.
that breaks at a bad pointer value (indicated in the code).
#include <iostream>
char* find (char* s, char* x) {
// variable that stores the first char of matching substring of s
char* match = nullptr;
// control variable indicating full match between x and substring of s
bool full_match = false;
if (s == nullptr || x == nullptr) return match;
size_t len_s = my_strlen(s);
size_t len_x = my_strlen(x);
// x must be shorter than s
if (len_s < len_x) return match;
// pointers to beginning and end of s and x, used for traversal loops
char *s_begin = s;
char *s_end = s + len_s;
char *x_begin = x;
char *x_end = x + len_x;
// traverse s
for (char* i = s_begin; s_begin != s_end; ++i) {
// test for match between s and the first char of x
if (*i == *x_begin) {
//-----------^
// Code breaks here. Hovering above shows: 0x008ff000 <Bad Ptr>
// assign s's matching char
match = i;
// if x's lenght is 1 return match
if (len_x == 1) return match;
// define new pointer to the next element of s
char *i_next = i + 1;
// traverse the rest of x
for (char* j = x_begin + 1; j != x_end; ++j) {
// if there is even one mismatch brake loop and continue traversing s
if (*i_next != *j) break;
// if the rest of x matches the rest of s, switch to full_match
else if (j == x_end - 1) full_match = true;
// increment x
++i_next;
}
// when x traversed and there is full_match, return the first matching char
if (full_match) return match;
}
}
// return nullptr to indicate no match
return nullptr;
}
//====================================================
int main () {
try {
char* source = "abcde\0";
char* target = "c\0";
char *match_found = find(source, target);
if(match_found) std::cout << *match_found << '\n';
} catch (std::exception &e) {
std::cerr << e.what() << std::endl;
getchar();
}
getchar();
}
Why is the pointer char* i not initialized to s_begin? What am I doing wrong?
Your loop condition is wrong. What you have is an infinite loop:
for (char* i = s_begin; s_begin != s_end; ++i)
Since s_begin will never equal s_end i ends up incrementing outside of the string. Change it to:
for (char* i = s_begin; i != s_end; ++i)
Related
When I run the program, I get exception "heap has been corrupted" after completion of the function
I have read that this exception may cause if you are using memory that has been freed, or when you are writing to index which is out of array index. But none of the cases applies here. I have read other answers of some problems but it didn't help much.
`char fileNametoExport[26]="d:\\FOlder1\\part1.ipt";
char WorkingFolderName[260] ="d:\\folder";
int start = rFind(fileNametoExport, '\\');
int finish = rFind(fileNametoExport, '.');
if (start == -1)
start = 0;
char partname[260];
strcpy(partname,substr(fileNametoExport, start, finish));
::AfxMessageBox((LPCTSTR)partname);
char xtfile[260];
char xmltxtfile[260];
strcpy(xtfile, strcat(WorkingFolderName, partname));
strcat(xtfile, "__Default.x_t");
strcpy(xmltxtfile, WorkingFolderName);
strcat(xmltxtfile,"_XT_SE_INV_Default_SOLID_0_Solid1_xt.xmt_txt");`
function rfind() to find occurence of char in char array-
int rFind(char* s, char c)
{
int sz = 0;
char *tmp = s;
while (*tmp != '\0')
{
sz++;
tmp++;
}
for (int i = sz - 1; i >= 0; i--)
{
if (*(s + i) == c)
return i;
}
return -1;
}
function substr() to get substring from position x to y (y exclusive)
char* substr(char* s, const int b, const int f)
{
char *str = new char[f - b];
int t = 0;
for (int i = b; i != f; i++)
{
str[t] = s[i];
t++;
}
str[t] = '\0';
return str;
}
P.S- While giving input I ensure that fileNametoExport always contains '.' and '\'.
Your program do not check lengths of input strings. You can receive a string longer than your buffer and program will fail.
If your program get fileNametoExport = "d:\\somefolder\\somefilewithoutdot" , finish will be -1 and program fail at strcpy(partname,substr(fileNametoExport, start, finish)); .
Program writes after buffer in char* substr(char* s, const int b, const int f) at line
str[t] = '\0';
because t at this point equal f-b , size of str buffer.
Function _ASSERTE( _CrtCheckMemory( ) ); from <crtdbg.h> very useful when searching for bugs like this. Put it around suspicious code and it fails after your bug. It works only in debug.
I have a character array like below:
char array[] = "AAAA... A1... 3. B1.";
How can I split this array by the string "..." in Arduino? I have tried:
ptr = strtok(array, "...");
and the output is the following:
AAAA,
A1,
3,
B1
But I actually want output to be
AAAA,
A1,
3.B1.
How to get this output?
edit:
My full code is this:
char array[] = "AAAA... A1... 3. B1.";
char *strings[10];
char *ptr = NULL;`enter code here`
void setup()
{
Serial.begin(9600);
byte index = 0;
ptr = strtok(array, "..."); // takes a list of delimiters
while(ptr != NULL)
{
strings[index] = ptr;
index++;
ptr = strtok(NULL, "..."); // takes a list of delimiters
}
for(int n = 0; n < index; n++)
{
Serial.println(strings[n]);
}
}
The main problem is that strtok does not find a string inside another string. strtok looks for a character in a string. When you give multiple characters to strtok it looks for any of these. Consequently, writing strtok(array, "..."); is exactly the same as writing strtok(array, ".");. That is why you get a split after "3."
There are multiple ways of doing what you want. Below I'll show you an example using strstr. Unlike strtokthe strstr function do find a substring inside a string - just what you are looking for. But.. strstr is not a tokenizer so some extra code is required to print the substrings.
Something like this should do:
int main()
{
char array[] = "AAAA... A1... 3. B1...";
char* ps = array;
char* pf = strstr(ps, "..."); // Find first substring
while(pf)
{
int len = pf - ps; // Number of chars to print
printf("%.*s\n", len, ps);
ps = pf + 3;
pf = strstr(ps, "..."); // Find next substring
}
return 0;
}
You can implement your own split as strtok except the role of the second argument :
#include <stdio.h>
#include <string.h>
char * split(char *str, const char * delim)
{
static char * s;
char * p, * r;
if (str != NULL)
s = str;
p = strstr(s, delim);
if (p == NULL) {
if (*s == 0)
return NULL;
r = s;
s += strlen(s);
return r;
}
r = s;
*p = 0;
s = p + strlen(delim);
return r;
}
int main()
{
char s[] = "AAAA... A1... 3. B1.";
char * p = s;
char * t;
while ((t = split(p, "...")) != NULL) {
printf("'%s'\n", t);
p = NULL;
}
return 0;
}
Compilation and execution:
/tmp % gcc -g -pedantic -Wextra s.c
/tmp % ./a.out
'AAAA'
' A1'
' 3. B1.'
/tmp %
I print between '' to show the return spaces, because I am not sure you want them, so delim is not only ... in that case
Because you tagged this as c++, here is a c++ 'version' of your code:
#include <iostream>
using std::cout;
using std::endl;
#include <vector>
using std::vector;
#include <string>
using std::string;
class T965_t
{
string array;
vector<string> strings;
public:
T965_t() : array("AAAA... A1... 3. B1.")
{
strings.reserve(10);
}
~T965_t() = default;
int operator()() { return setup(); } // functor entry
private: // methods
int setup()
{
cout << endl;
const string pat1 ("... ");
string s1 = array; // working copy
size_t indx = s1.find(pat1, 0); // find first ... pattern
// start search at ---------^
do
{
if (string::npos == indx) // pattern not found
{
strings.push_back (s1); // capture 'remainder' of s1
break; // not found, kick out
}
// else
// extract --------vvvvvvvvvvvvvvvvv
strings.push_back (s1.substr(0, indx)); // capture
// capture to vector
indx += pat1.size(); // i.e. 4
s1.erase(0, indx); // erase previous capture
indx = s1.find(pat1, 0); // find next
} while(true);
for(uint n = 0; n < strings.size(); n++)
cout << strings[n] << "\n";
cout << endl;
return 0;
}
}; // class T965_t
int main(int , char**) { return T965_t()(); } // call functor
With output:
AAAA
A1
3. B1.
Note: I leave changing "3. B1." to "3.B1.", and adding commas at end of each line (except the last) as an exercise for the OP if required.
I looked for a split function and I didn't find one that meets my requirement, so I made one and it works for me so far, of course in the future I will make some improvements, but it got me out of trouble.
But there is also the strtok function and better use that.
https://www.delftstack.com/es/howto/arduino/arduino-strtok/
I have the split function
Arduino code:
void split(String * vecSplit, int dimArray,String content,char separator){
if(content.length()==0)
return;
content = content + separator;
int countVec = 0;
int posSep = 0;
int posInit = 0;
while(countVec<dimArray){
posSep = content.indexOf(separator,posSep);
if(posSep<0){
return;
}
countVec++;
String splitStr = content.substring(posInit,posSep);
posSep = posSep+1;
posInit = posSep;
vecSplit[countVec] = splitStr;
countVec++;
}
}
Llamada a funcion:
smsContent = "APN:4g.entel;DOMAIN:domolin.com;DELAY_GPS:60";
String vecSplit[10];
split(vecSplit,10,smsContent,';');
for(int i = 0;i<10;i++){
Serial.println(vecSplit[i]);
}
String input:
APN:4gentel;DOMAIN:domolin.com;DELAY_GPS:60
Output:
APN:4g.entel
DOMAIN:domolin.com
DELAY_GPS:60
RESET:true
enter image description here
I tried using following code sample given in Tour of C++ which uses nullptr to break loop over zero terminated string. However, my sample program doesn't seem to stop in the loop.
Excerpt from the book:
first version of code from book:
```
int count_x(char∗ p, char x)
// count the number of occurrences of x in p[]
// p is assumed to point to a zero-terminated array of char (or to nothing)
{
if (p==nullptr) return 0;
int count = 0;
for (; p!=nullptr; ++p)
if (∗p==x)
++count;
return count;
}
```
second simplified version
```int count_x(char* p, char x)
// count the number of occurrences of x in p[]
// p is assumed to point to a zero-terminated array of char (or to
// nothing)
{
int count = 0;
while (p) {
if (*p==x)
++count;
++p;
}
return count;
}```
statement following code in the book:
The while-statement executes until its condition becomes false.
A test of a pointer (e.g., while (p)) is equivalent to comparing the pointer to the null pointer (e.g.,
while (p!=nullptr)).
My program using same structure:
char name[] = "ABCD";
char *p = name;
int count = 0;
int loopc = 0;
while (p)
{
loopc++;
if(*p == '\0')
cout << "zero found\n";
else
cout << *p << "\n";
//emergency stop
if (loopc == 12)
break;
p++;
}
expected:
Should stop after printing name.
actual:
A
B
C
D
zero found
zero found
zero found
zero found
zero found
zero found
zero found
zero found
Thanks for all the useful comments.
It seems the author had given wrong example in the earlier edition (1st) which was later corrected in second edition released in 2018.
Corrected version from new edition:
int count_x(char∗ p, char x)
// count the number of occurrences of x in p[]
// p is assumed to point to a zero-terminated array of char (or to nothing)
{
if (p==nullptr) return 0;
int count = 0;
for (; *p!=0; ++p)
if (∗p==x)
++count;
return count;
}
the first version should return 0 when you pass it nullptr. In the for loop however you are passing once.
There is only one char* (consider using std::string btw) anyway...
Here is my quick fix, try to understand it:
int count_x(char* c_ptr, char c) {
if (c_ptr == nullptr) return 0;
int count = 0;
/* Note that I check if *c_ptr is '\0' that's the **value**
* pointed to by c_ptr
*/
for (; *c_ptr != '\0'; ++c_ptr) // '\0' is a nul character
if (*c_ptr == c) ++count;
return count;
}
int foo(const std::string& word, char letter) noexcept {
int count = 0;
for (const auto& c: word) { // for all const char ref in word
if (c == letter) ++count;
}
return count;
}
int main() {
int a = count_x("acbccc", 'c');
int b = foo("acbccc", 'c');
std::cout << a << '\n' << b;
}
Feel free to ask if you have any question.
Cheers.
I am programming my custom string class with multiple methods. The issue is that the comparison method does not work as I intend. Instead of doing nothing when the two char arrays differ, an if conditional still proceeds in my main function.
There are no errors given when I compile with g++. The code is syntactically correct, however logically faulty. I know this because I can give the compare method two char arrays which differ in content, and it will not matter whether they differ this way, as the main function will run the if conditional for "s8.compare(s7) == 1" regardless if the result in the compare method is not true.
I will post the entire code below. Any help is greatly appreciated.
string.h
class Str {
private:
char *value;
int length;
int capacity;
//Doubles the size of the string when called.
void growArray();
//If the two strings are uneven, get absolute value of difference in length.
int difference(int a, int b);
//Calculates the size of a character array, passed in as an argument
int getCharArrSize(const char *v);
public:
Str();
explicit Str(const char *STR);
void copy(Str s);
void concatenate(Str s);
bool compare(Str s);
void print();
};
//Str constructor
Str::Str() {
//Assign value, capacity, and length to any new Str object
value = new char[100];
capacity = 100;
length = 0;
}
//Pass STR object as a pointer to string object constructor
Str::Str(const char *STR) {
length = getCharArrSize(STR);
capacity = 100;
value = new char[capacity];
//Copy contents from STR to string object
for (int i = 0; i < length; i++)
value[i] = STR[i];
}
//Doubles the size of the string when called.
void Str::growArray() {
const char *tmp = value;
capacity *= 2;
value = new char[capacity];
for (int i = 0; i < length; i++)
value[i] = tmp[i];
}
//If the two strings are uneven, get absolute value of difference in length.
int Str::difference(int a, int b) {
int d = 0;
if (a > b) d = a - b;
else if (b > a) d = b - a;
return d;
}
//Calculates the size of a character array, passed in as an argument
int Str::getCharArrSize(const char *v) {
int c = 0;
while (v[c] != '\0') {
c++;
}
return c;
}
//Overwrites the data of the string array with the data contained in s
void Str::copy(Str s) {
//Check ability for empty string object to hold Str s contents
if (capacity > s.length) {
//Copy over each element until s length is reached
for (int i = 0; i < s.length ; i++)
value[i] = s.value[i];
//Set string object length to copy's size
length = getCharArrSize(value);
} else { growArray(); }
}
//Concatenate Str s onto string object
void Str::concatenate(Str s) {
//Check ability for string object to hold itself and concatenated chars
if (capacity > length + s.length) {
//Fill string object with s object until end of combined lengths if necessary
for (int i = 0; i < length + s.length; i++)
value[length + i] = s.value[i];
//Set length based on chars in concatenated string object
length = getCharArrSize(value);
} else { growArray(); }
}
//Compare each element in Str s against string for similarities
bool Str::compare(Str s) {
if (length == s.length) {
if (*value == *s.value) {
while ((*value != value[length]) && (*s.value != s.value[s.length])) {
value++;
s.value++;
}
return true;
} else return false;
} else {
difference(length, s.length);
}
}
//Print function
void Str::print() {
std::cout << value << std::endl;
}
main.cpp
#include"string.h"
int main() {
Str s1("Hello ");
Str s2("World");
Str s3(", my ");
Str s4("Name ");
Str s5("is ");
Str s6("Chad!");
Str s7;
s7.copy(s1);
s7.concatenate(s2);
s7.concatenate(s3);
s7.concatenate(s4);
s7.concatenate(s5);
s7.concatenate(s6);
s7.print();
std::cout << "\n\n";
Str s8("Hello World, My Name is Chad!");
if (s8.compare(s7) == 1) {
std::cout << "They Match!" << std::endl;
}
Str s9("I dont match....");
if (s9.compare(s8) == 0) {
std::cout << "I differ by " << s8.compare(s6) << " characters" << std::endl;
}
}
The above code returns a result that appears correct, however changing (s8.compare(s7) == 1) to something like (s8.compare(s5) == 1) returns 'They match!' when I am trying to check each individual element in the char arrays against one another, and only return true if they are the same length and each character matches in the arrays.
Your program has undefined behavior since Str::compare does not have a return statement in one of the branches.
bool Str::compare(Str s) {
if (length == s.length) {
...
} else {
// Missing return statement.
difference(length, s.length);
}
}
Perhaps you want to change that line to:
return (difference(length, s.length) == 0);
Your loop is running without a comparison. You compare the initial values in the char array and then loop through the rest without comparison. So you will return true every time the initial values are equal.
Below the loop runs after the same length is determined then every char is compared. If they are not equal then the function will return false. Otherwise the function will return true.
bool Str::compare(Str s) {
if (length == s.length) {
while ((*value != value[length]) && (*s.value != s.value[s.length])) {
if (*value == *s.value) {
value++;
s.value++;
} else {
return false;//will return false as soon as a comparison is false
}
}
return true;
} else {
difference(length, s.length);
}
}
You also need to return a boolean from the difference function. If you want to return ints from that function switch to a int return on the compare function and use 0 and 1s as their boolean counterparts.
I been assigned this exercise at college, but I don't know how to implement the recursion structure ( " ??? " in the code ). In the if-cycle I should match the first character in the array with the last and apply the recursion in order to reach the central character, but I don't know how to setup the code. The main function code compiles perfectly.
#include <iostream>
using namespace std;
const int DIM = 8;
bool is_palindrome (char* first, char* last)
{
if (first == last)
{
???
}
else
return false;
}
int main()
{
char a[DIM] = {'i','n','g','e','g','n','i','\0'};
char *first = &a[DIM] + 1;
char *last = &a[DIM] -1;
if (is_palindrome(first, last))
cout << " the char array is palindrome ";
else
cout << " the char array is not palindrome ";
return 0;
}
First of all, you will need to compare the values pointed to by the pointers, not the pointers themselves
if (*first == *last)
Second, you can advance the first and decrease the last to move one character:
// inside if
++first;
--last;
and call the function again with the new values of the pointers:
return is_palindrome(first, last);
You will also need to ensure that you do not go past the array when you actually get a palindrome, so add this check to the beginning of is_palindrome()
if (last < first) {
return true;
}
Also, in main() you need to initialize your pointers this way:
char* first = &a[0];
char* last = &[DIM-2];
The way you wrote it first already points past the array, while last points to the ending '\0', which will not match to any of the other characters.
using namespace std;
const int DIM = 8;
bool is_palindrome ( char* first , char* last )
{
if ( *first == '\0' )
{
return false;
}
else if ( first >= last )
{
return true;
}
else if ( *first == *last )
{
return is_palindrome(first + 1, last - 1);
}
else
{
return false;
}
}
int main ()
{
char a[DIM] = {'i','n','g','e','g','n','i','\0'};
char *first = a;
char *last = &a[DIM] - 2;
if ( is_palindrome ( first , last ) )
{
cout << " the char array is palindrome ";
}
else
{
cout << " the char array is not palindrome ";
}
return 0;
}