#include<string>
#include<cstring>
class Solution {
void shift_left(char* c, const short unsigned int bits) {
const unsigned short int size = sizeof(c);
memmove(c, c+bits, size - bits);
memset(c+size-bits, 0, bits);
}
public:
string longestPalindrome(string s) {
char* output = new char[s.length()];
output[0] = s[0];
string res = "";
char* n = output;
auto e = s.begin() + 1;
while(e != s.end()) {
char letter = *e;
char* c = n;
(*++n) = letter;
if((letter != *c) && (c == &output[0] || letter != (*--c)) ) {
++e;
continue;
}
while((++e) != s.end() && c != &output[0]) {
if((letter = *e) != (*--c)) {
const unsigned short int bits = c - output + 1;
shift_left(output, bits);
n -= bits;
break;
}
(*++n) = letter;
}
string temp(output);
res = temp.length() > res.length()? temp : res;
shift_left(output, 1);
--n;
}
return res;
}
};
input string longestPalindrome("babad");
the program works fine and prints out "bab" as the longest palindrome but there's a heap overflow somewhere. Error like this appears:
Read of size 6 at ...memory address... thread T0
"babad" is size 5 and after going over this for an hour. I don't see the point where the iteration ever exceeds 5
There is 3 pointers here that iterate.
e as the element of string s.
n which is the pointer to the next char of output.
and c which is a copy of n and decrements until it reaches the address of &output[0].
maybe it's something with the memmove or memset since I've never used it before.
I'm completely lost
TL;DR : mixture of char* and std::string are not really good idea if you don't understand how exactly it works.
If you want to length of string you cant do this const unsigned short int size = sizeof(c); (sizeof will return size of pointer (which is commonly 4 on 32-bit machine and 8 on 64-bit machine). You must do this instead: const size_t size = strlen(c);
Address sanitizers is right that you (indirectly) are trying to get an memory which not belongs to you.
How does constructor of string from char* works?
Answer: char* is considered as c-style string, which means that it must be null '\0' terminated.
More details: constructor of string from char* calls strlen-like function which looks like about this:
https://en.cppreference.com/w/cpp/string/byte/strlen
int strlen(char *begin){
int k = 0;
while (*begin != '\0'){
++k;
++begin;
}
return k;
}
If c-style char* string does not contain '\0' it cause accessing memory which doesn't belongs to you.
How to fix?
Answer (two options):
not use mixture of char* and std::string
char* output = new char[s.length()]; replace with char* output = new char[s.length() + 1]; memset(output, 0, s.length() + 1);
Also you must delete all memory which you newed. So add delete[] output; before return res;
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 problem with memory access. When i = 0, Visual Studio throws an exception with reference to a line as labelled in the following code.
Can't access at 0x00AD8B3B and 0x00AD8B3B equals scr+np-i
How can I fix the for-loop body so as to fix this issue?
int o_strrev(char* scr)
{
int np = strlen(scr) - 1;
char tmp;
if (!scr) return -1;
if (!*scr) return -1;
for (int i = 0; i < np / 2; i++)
{
tmp = scr[np-i];
scr[np-i] = scr[i]; # this line
scr[i] = tmp;
}
return 0;
}
As pointed out by #Revolver_Ocelot, you are probably passing a const char* from a string literal. Since these are, by definition, constant, you can't modify them in the way you're trying to. You need some way to convert const char* into a non constant char*. Something along these lines will work for you:
string str = "string";
char* cstr = new char[str.length() + 1];
strcpy(cstr, str.c_str());
I've compiled your code in g++ using non constant char* and it works fine. Just remember to deallocate your char* when you're done with it. We don't want memory leaks ;)
I see you are trying to reverse a string.
Might I suggest a simpler way
void reverse(char *string_var)
{
int length, c;
char *begin, *end, temp;
length = strlen(string_var);
begin = string_var;
end = string_var;
for (c = 0; c < length - 1; c++)
end++;
for (c = 0; c < length/2; c++)
{
temp = *end;
*end = *begin;
*begin = temp;
begin++;
end--;
}
}
Make sure you pass character array of the form
char word[64]
Or maybe by recursion
void reverse(char *x, int begin, int end)
{
char c;
if (begin >= end)
return;
c = *(x+begin);
*(x+begin) = *(x+end);
*(x+end) = c;
reverse(x, ++begin, --end);
}
You're most likely calling o_strrev with a string literal, some thing like:
o_strrev("This ain't gonna work!");
This will compile with most compilers and at most generate a warning. Try cranking up your warning level.
Instead, you want to make a dynamic copy of your string on the heap and work with a char* pointer to that, something like:
string str = "This is gonna work!!"
size_t len = str.size();
char* my_string = new char[len + 1];
strncpy(my_string, str.data(), len)
my_string[len] = '\0';
o_strrev(my_string);
// ...
delete[] my_string;
My C++ class is going over C-style strings and working with pointers. I'm to write a function that has three parameters: a char * s1, a const char * s2, and a size_t max, which represents that maximum size of the s1 buffer. I am to append the characters in s2 to the end of s1. The directions advise me to make sure there is only one '\0' at the end of the combined characters and I am not to go beyond the end of the buffer I'm asked to copy to. The function will return a pointer to the first character in s1.
I cannot use any functions in the standard library. What I can use are pointers, pointer arithmetic or array notation.
I've started, but not sure where to go.
const char * myFunction (char * s1, const char * s2, size_t max)
{
char * begin = s1;
while (*s1) s1++;
while ((s1 < begin + max - 1) && (*s2 != '\0')) {
*s1++ = *s2++;
}
return s1;
}
Not sure what to do after reaching the end of s1. How would I put s2 to the end of s1?
I cannot use any functions in the standard library.
The only required one would be strlen(), I think. Roll your own:
#define min(a,b) ((a) < (b)) ? (a) : (b)
size_t str_length(const char* s)
{
size_t len = 0;
for( ; *s; ++s, ++len);
return len;
}
const char * append (char * s1, const char * s2, size_t max)
{
if(!s1 || !s2) //If either of strings is NULL, nothing to be done
return s1;
size_t s1_length = str_length(s1);
if(s1_length < max - 1) //If there is a room in s1...
{
size_t s2_length = str_length(s2); //Get length of s2
size_t append_size = min(s2_length, max - s1_length - 1); //Make sure we won't copy more than buffer can hold
if(append_size > 0)
{
memcpy(s1 + s1_length, s2, append_size); //This will overwrite null terminator in 's1'
s1[s1_length + append_size] = 0;
}
}
return s1;
}
Test:
int main(void)
{
char dest[64] = "This is a test";
append(dest, " of strings appending", 64);
printf("%s (length: %d)\n", dest, str_length(dest));
append(dest, " of appending too long string, that will probably not fit inside destination buffer", 64);
printf("%s (length: %d)\n", dest, str_length(dest));
return 0;
}
Output:
This is a test of strings appending (length: 35)
This is a test of strings appending of appending too long strin (length: 63)
Here is working sample.
I'm reading a string from a file so it's in the form of a char array. I need to tokenize the string and save each char array token as a uint8_t hex value in an array.
char* starting = "001122AABBCC";
// ...
uint8_t[] ending = {0x00,0x11,0x22,0xAA,0xBB,0xCC}
How can I convert from starting to ending? Thanks.
Here is a complete working program. It is based on Rob I's solution, but fixes several problems has been tested to work.
#include <string>
#include <stdio.h>
#include <stdlib.h>
#include <vector>
#include <iostream>
const char* starting = "001122AABBCC";
int main()
{
std::string starting_str = starting;
std::vector<unsigned char> ending;
ending.reserve( starting_str.size());
for (int i = 0 ; i < starting_str.length() ; i+=2) {
std::string pair = starting_str.substr( i, 2 );
ending.push_back(::strtol( pair.c_str(), 0, 16 ));
}
for(int i=0; i<ending.size(); ++i) {
printf("0x%X\n", ending[i]);
}
}
strtoul will convert text in any base you choose into bytes. You have to do a little work to chop the input string into individual digits, or you can convert 32 or 64bits at a time.
ps uint8_t[] ending = {0x00,0x11,0x22,0xAA,0xBB,0xCC}
Doesn't mean anything, you aren't storing the data in a uint8 as 'hex', you are storing bytes, it's upto how you (or your debugger) interpretes the binary data
With C++11, you may use std::stoi for that :
std::vector<uint8_t> convert(const std::string& s)
{
if (s.size() % 2 != 0) {
throw std::runtime_error("Bad size argument");
}
std::vector<uint8_t> res;
res.reserve(s.size() / 2);
for (std::size_t i = 0, size = s.size(); i != size; i += 2) {
std::size_t pos = 0;
res.push_back(std::stoi(s.substr(i, 2), &pos, 16));
if (pos != 2) {
throw std::runtime_error("bad character in argument");
}
}
return res;
}
Live example.
I think any canonical answer (w.r.t. the bounty notes) would involve some distinct phases in the solution:
Error checking for valid input
Length check and
Data content check
Element conversion
Output creation
Given the usefulness of such conversions, the solution should probably include some flexibility w.r.t. the types being used and the locale required.
From the outset, given the date of the request for a "more canonical answer" (circa August 2014) liberal use of C++11 will be applied.
An annotated version of the code, with types corresponding to the OP:
std::vector<std::uint8_t> convert(std::string const& src)
{
// error check on the length
if ((src.length() % 2) != 0) {
throw std::invalid_argument("conversion error: input is not even length");
}
auto ishex = [] (decltype(*src.begin()) c) {
return std::isxdigit(c, std::locale()); };
// error check on the data contents
if (!std::all_of(std::begin(src), std::end(src), ishex)) {
throw std::invalid_argument("conversion error: input values are not not all xdigits");
}
// allocate the result, initialised to 0 and size it to the correct length
std::vector<std::uint8_t> result(src.length() / 2, 0);
// run the actual conversion
auto str = src.begin(); // track the location in the string
std::for_each(result.begin(), result.end(), [&str](decltype(*result.begin())& element) {
element = static_cast<std::uint8_t>(std::stoul(std::string(str, str + 2), nullptr, 16));
std::advance(str, 2); // next two elements
});
return result;
}
The template version of the code adds flexibility;
template <typename Int /*= std::uint8_t*/,
typename Char = char,
typename Traits = std::char_traits<Char>,
typename Allocate = std::allocator<Char>,
typename Locale = std::locale>
std::vector<Int> basic_convert(std::basic_string<Char, Traits, Allocate> const& src, Locale locale = Locale())
{
using string_type = std::basic_string<Char, Traits, Allocate>;
auto ishex = [&locale] (decltype(*src.begin()) c) {
return std::isxdigit(c, locale); };
if ((src.length() % 2) != 0) {
throw std::invalid_argument("conversion error: input is not even length");
}
if (!std::all_of(std::begin(src), std::end(src), ishex)) {
throw std::invalid_argument("conversion error: input values are not not all xdigits");
}
std::vector<Int> result(src.length() / 2, 0);
auto str = std::begin(src);
std::for_each(std::begin(result), std::end(result), [&str](decltype(*std::begin(result))& element) {
element = static_cast<Int>(std::stoul(string_type(str, str + 2), nullptr, 16));
std::advance(str, 2);
});
return result;
}
The convert() function can then be based on the basic_convert() as follows:
std::vector<std::uint8_t> convert(std::string const& src)
{
return basic_convert<std::uint8_t>(src, std::locale());
}
Live sample.
uint8_t is typically no more than a typedef of an unsigned char. If you're reading characters from a file, you should be able to read them into an unsigned char array just as easily as a signed char array, and an unsigned char array is a uint8_t array.
I'd try something like this:
std::string starting_str = starting;
uint8_t[] ending = new uint8_t[starting_str.length()/2];
for (int i = 0 ; i < starting_str.length() ; i+=2) {
std::string pair = starting_str.substr( i, i+2 );
ending[i/2] = ::strtol( pair.c_str(), 0, 16 );
}
Didn't test it but it looks good to me...
You may add your own conversion from set of char { '0','1',...'E','F' } to uint8_t:
uint8_t ctoa(char c)
{
if( c >= '0' && c <= '9' ) return c - '0';
else if( c >= 'a' && c <= 'f' ) return 0xA + c - 'a';
else if( c >= 'A' && c <= 'F' ) return 0xA + c - 'A';
else return 0;
}
Then it will be easy to convert a string in to array:
uint32_t endingSize = strlen(starting)/2;
uint8_t* ending = new uint8_t[endingSize];
for( uint32_t i=0; i<endingSize; i++ )
{
ending[i] = ( ctoa( starting[i*2] ) << 4 ) + ctoa( starting[i*2+1] );
}
This simple solution should work for your problem
char* starting = "001122AABBCC";
uint8_t ending[12];
// This algo will work for any size of starting
// However, you have to make sure that the ending have enough space.
int i=0;
while (i<strlen(starting))
{
// convert the character to string
char str[2] = "\0";
str[0] = starting[i];
// convert string to int base 16
ending[i]= (uint8_t)atoi(str,16);
i++;
}
uint8_t* ending = static_cast<uint8_t*>(starting);
Can someone tell me what's wrong with the following?
I'm trying to add characters to a character array. name is a pointer to a character array in the MyString class.
void MyString::add_chars(char* c)
{
if(l < strlen(c)+strlen(name))
name = resize(name, l, sizeof(c));
int i,j;
for(i=0; i<strlen(c); i++) {
name[i+l-1] = c[i];
l++;
}
}
char* MyString::resize(char* vptr, int currentsize, int extra) {
char* temp = new char[currentsize + extra];
int i;
for (i = 0; i < currentsize; i++) {
temp[i] = vptr[i];
}
vptr = temp;
return vptr;
}
And in main:
MyString g ("and");
g.add_chars("baasdf");
cout << g.get_name() << "\n";
But get_name returns "andb". How can I fix my code?
Edit:
Updated code, still same result..
void StringList::add_chars(char* c)
{
char* my_new_string = resize(name, l, sizeof(char));
if( my_new_string != NULL )
{
delete [] name;
name = my_new_string;
}
int i,j;
for(i=0; i<strlen(c); i++) {
name[i+l-1] = c[i];
l++;
}
name[l-1] = '\0';
}
char* StringList::resize(char* vptr, int currentsize, int extra) {
char* temp = new char[currentsize + extra + 1];
int i;
for (i = 0; i < currentsize; i++) {
temp[i] = vptr[i];
}
vptr = temp;
return vptr;
}
This line is wrong:
name = resize(name, l, sizeof(c));
You should not take the sizeof(char*), which your c variable is, but you should do sizeof(char) or just 1.
Also, make sure that you do +1 on the size to take care of the zero termination char at the end of your string.
How can I fix my code?
Don't fix it. Throw it away and use vector<char> or just string.
But I insist, how can I fix my code!?
OK, OK, here is how...
Get a nice debugger, for example this one.
Step carefully through the code, constantly inspecting the variables and comparing them with what you expect them to be.
When you reach the call to resize, take note of sizeof(c) (assigned to extra parameter of resize). When you realize it is not what you expected, ask yourself: what is the purpose of sizeof, and you'll understand why.
BTW, you also have a memory leak and a very poor performance due all these strlens.
Firstly, am I right in assuming that this is a learning exercise for you in learning "how to create your own string class"? C++ has already got a built-in string type which you should always prefer for the most part.
the sizeof operator yields the size (in bytes) of its operand, which in this case is c whose type is char* - it looks like what you're actually after is the length of a null-terminated character array (a "C" string") - you're already using strlen, so I'd suggest you simply want to use that again. (taking a null-terminator into account too)
name = resize(name, l, strlen(c) + 1);
Note, that your code looks as if it suffers from memory leaks. You're assigning a new value to your name variable without clearing up whatever existed there first.
if(l < strlen(c)+strlen(name))
{
char* my_new_string = resize(name, l, strlen(c));
if( my_new_string != NULL )
{
delete [] name;
name = my_new_string;
}
}
EDIT: As other replies have pointed out, there's still plenty wrong with the code which could be resolved using C++'s string and vector.
Here's one possible way you could implement add_chars
void MyString::add_chars(char* c)
{
if( c != NULL && name != NULL )
{
size_t newlength = strlen(c) + strlen(name) + 1;
char* newstring = new char[newlength];
if( newstring != NULL )
{
size_t namelength = strlen(name);
size_t remaining = newlength - namelength;
strncpy( newstring, name, newlength );
strncpy( &newstring[namelength] , c, remaining );
delete [] name;
name = newstring;
}
}
}