I'm trying to program a split that takes in a char-array with multiple words and separates each word into their own smaller char-array. All the pointers of the smaller char-arrays are kept in a pointer array so I can return a double pointer.
Can you take a look at my code and see if you see any errors. When I try to run my program my computer gets gradually slower, after 3-4 seconds I can't move my mouse or alt+f4 my editor. So something has to be seriously wrong!
Also I'm completely new to C-programming so I will most definitely have a silly mistake in there.
char **split(char *s) {
char **result;
int wrd_cnt = 2; //I'm adding NULL at the end of the pointer-array.
//Counts the number of words to allocate memory for the pointer-array.
for(int i = 0; i < strlen(s); i++) {
if(s[i] == ' ') {
wrd_cnt++;
}
}
result = malloc(wrd_cnt * sizeof(char*));
//Counts letters in each word to allocate memory for every single small char-array with malloc.
for(int i = 0; i < strlen(s); i++) {
for(int j = 0; j < (wrd_cnt); j++) {
int char_cnt = 0;
for(int k = 0; s[i] != ' ' || s[i] != '\0'; k++, i++) {
char_cnt++;
result[j] = malloc(char_cnt * sizeof(char));
}
}
}
//Puts each word into their own place in the pointer array.
for(int i = 0; i < strlen(s); i++) {
for(int j = 0; j < (wrd_cnt); j++) {
for(int k = 0; s[i] != ' ' || s[i] != '\0'; k++, i++) {
result[j][k] = s[i];
}
}
}
result[wrd_cnt-1] = NULL;
return result;
}
In this situation the loops using j and k can be removed and instead increment and reset i, j and char_cnt based on the i loop as the s array is processed, similar to what you had done for wrd_cnt in the first loop
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char **split(char *s);
int main ( void) {
char **output = NULL;
int each = 0;
char line[99] = " string to parse for words ";
output = split ( line);
each = 0;
while ( output[each]) {
printf ( "%s\n", output[each]);
each++;
}
each = 0;
while ( output[each]) {
free ( output[each]);
each++;
}
free ( output);
exit ( 0);
}
char **split(char *s) {
char **result;
int wrd_cnt = 2; //I'm adding NULL at the end of the pointer-array.
int char_cnt = 0;
int i = 0;
int j = 0;
int k = 0;
//Counts the number of words to allocate memory for the pointer-array.
for(i = 0; i < strlen(s); i++) {
if(s[i] == ' ') {
wrd_cnt++;
}
}
if ( ( result = malloc(wrd_cnt * sizeof(char*))) == NULL) {
fprintf ( stderr, "malloc failure\n");
exit ( 1);
}
//Counts letters in each word to allocate memory for every single small char-array with malloc.
char_cnt = 1;
j = 0;
for( i = 0; i < strlen(s); i++) {
if ( s[i] == ' ') {
if ( ( result[j] = malloc(char_cnt * sizeof(char))) == NULL) {
fprintf ( stderr, "malloc failure\n");
exit ( 1);
}
j++;
char_cnt = 1;
continue;
}
char_cnt++;
}
if ( j == wrd_cnt - 2) {
//allocate for last word
if ( ( result[j] = malloc(char_cnt * sizeof(char))) == NULL) {
fprintf ( stderr, "malloc failure\n");
exit ( 1);
}
j++;
result[j] = NULL;
}
result[wrd_cnt - 1] = NULL;//just to make sure the last pointer is null
//Puts each word into their own place in the pointer array.
j = 0;
k = 0;
for( i = 0; i < strlen(s); i++) {
if ( s[i] == ' ') {
result[j][k] = '\0';//for space only so [j][0] is '\0'
k = 0;
j++;
continue;
}
result[j][k] = s[i];
k++;
result[j][k] = '\0';//for last word if there is no final space in s[]
}
return result;
}
Aside from the comments above your code scares me because of all the malloc() calls you do, one for each word. This means you must also free each word. This leaves programs open to memory leaks.
Given that this is C, which allows lots of casting, you can use a single malloc to hold both the (char *) pointer array AND the actual words.
char **split(char const *s) {
char **result; //
char *target; // where in result chars stored
size_t s_strlen = strlen(s); // length of s
int wrd_cnt = 2; //I'm adding NULL at the end of the pointer-array.
{
char const *sx;
for ( sx = s; sx = strpbrk( sx, " \t\n\r" ); sx++ )
{
wrd_cnt++;
}
}
result = malloc( (wrd_cnt * sizeof(char *)) + s_strlen + 2 );
/* allow for \0 and possible ' ' */
target = (char *)(result + wrd_cnt); /* where to save words */
strcpy( target, s ); /* copy to target known to be big enough */
if ( s_strlen > 0 && target[s_strlen-1] != ' ' )
strcat( target + s_strlen, " " ); /* assure ends in space */
{
char *tx, *tnext;
int n;
n = 0;
for ( tx = target; tnext = strpbrk( tx, " \t\n\r" ); tx = tnext + 1 )
{
result[n++] = tx; /* remember pointer */
*tnext = '\0'; /* terminate word */
}
result[n] = NULL; /* null termination */
}
return result;
}
Related
zfill algorithm is supposed to work as follows:
zfill function accepts two parameters, a string and a number,
if string length is >= the number, then it doesn't have to add anything, and it returns a copy to the string,
else, malloc enough space and add zeros before the string.
I'm trying to understand why is this solution not correct, it has two warnings:
1st warning:
for (i; i < zeros; i++) {
s[i] = "0";
}
"=": char differs in level of indirection from char[2]
2nd warning:
for (i; i < n; i++) {
s[i] = str[i];
}
buffer overrun while writing to s
char* zfill(const char* str, size_t n) {
if (str == NULL) {
return NULL;
}
char* s;
size_t length = strlen(str);
if (length >= n) {
//it doesn't have to add anything, just malloc and copy the string
size_t sum = length + 1u;
s = malloc(sum);
if (s == NULL) {
return NULL;
}
for (size_t i = 0; i < length; i++) {
s[i] = str[i];
}
s[sum] = 0;
}
else {
// add zeros before strings
size_t zeros = n - length;
size_t sum = n + 1u;
s = malloc(sum);
if (s == NULL) {
return NULL;
}
size_t i = 0;
for (i; i < zeros; i++) {
s[i] = "0";
}
for (i; i < n; i++) {
s[i] = str[i];
}
s[sum] = 0;
}
return s;
}
int main(void) {
char str[] = "hello, world!";
size_t n = 40;
char* s = zfill(str, n);
free(s);
return 0;
}
EDIT: I've solved the problem this way:
char* zfill(const char* str, size_t n) {
if (str == NULL) {
return NULL;
}
char* s;
size_t length = strlen(str);
if (length >= n) {
//it doesn't have to add anything, just malloc and copy the string
size_t sum = length + 1u;
s = malloc(sum);
if (s == NULL) {
return NULL;
}
for (size_t i = 0; i < length; i++) {
s[i] = str[i];
}
s[sum-1] = 0;
}
else {
// add zeros before strings
size_t zeros = n - length;
size_t sum = n + 1u;
s = malloc(sum);
if (s == NULL) {
return NULL;
}
size_t i = 0;
for (i; i < zeros; i++) {
s[i] = '0';
}
for (size_t j = 0; i < n; j++) {
s[i++] = str[j];
}
s[sum-1] = 0;
}
return s;
}
and it works, but I don't know why I have this warning:
for (i; i < zeros; i++) {}
statement with no effect
but when I've debugged I've noticed that this statement has an effect, because it correctly copies the correct number of zeros. I don't know why I have this warning
SO is a place of learning.
When first dealing with a coding challenge, it's best to take time to work out what's needed before starting to write code.
Below is a working version of zfill() (along with a main() that tests it.)
Read through the comments. The only thing new here is memset().
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// A trivial "helper function" determines the max of two values
int max( int a, int b ) { return a > b ? a : b; }
char *zfill( char *str, int minLen ) {
// Determine length of arbitrary string
int len = strlen( str );
// Determine max of length v. minimum desired
int allocSize = max( minLen, len );
// allocate buffer to store resulting string (with '\0')
char *obuf = (char*)malloc( allocSize + 1 );
/* omitting test for failure */
// determine start location at which to copy str
int loc = len <= minLen ? minLen - len : 0;
if( loc > 0 )
// fill buffer with enough 'zeros'
memset( obuf, '0', allocSize ); // ASCII zero!
// copy str to that location in buffer
strcpy( obuf + loc, str );
// return buffer to calling function
return obuf;
}
int main() {
// collection of strings of arbitrary length
char *strs[] = { "abc", "abcdefghijkl", "abcde", "a", "" };
// pass each one to zfill, print, then free the alloc'd buffer.
for( int i = 0; i < sizeof strs/sizeof strs[0]; i++ ) {
char *cp = zfill( strs[i], 10 );
puts( cp );
free( cp );
}
return 0;
}
Output:
0000000abc
abcdefghijkl
00000abcde
000000000a
0000000000
Here's zfill() without the comments:
char *zfill( char *str, int minLen ) {
int len = strlen( str );
int allocSize = max( minLen, len );
char *obuf = (char*)malloc( allocSize + 1 );
/* omitting test for failure */
int loc = len <= minLen ? minLen - len : 0;
if( loc > 0 )
memset( obuf, '0', loc ); // ASCII zero!
strcpy( obuf + loc, str );
return obuf;
}
You don't want to spend your time staring at lines and lines of code.
Fill your quiver with arrows that are (proven!) standard library functions and use them.
I've omitted, too, the test for zfill being passed a NULL pointer.
This code snippet
size_t sum = length + 1u;
s = malloc(sum);
//...
s[sum] = 0;
accesses memory outside the allocated character array because the valid range of indices is [0, sum). You need to write at least like
s[length] = 0;
In this code snippet
for (i; i < zeros; ++) {
s[i] = "0";
}
the expression s[i] represents a single object of the type char while on the right-hand side there is a string literal that as an expression has the type char *. You need to write at least
s[i] = '0';
using the integer character constant instead of the string literal.
In this code snippet
size_t i = 0;
for (i; i < zeros; i++) {
s[i] = "0";
}
for (i; i < n; i++) {
s[i] = str[i];
}
as the length of the string str can be less than n then this for loop
for (i; i < n; i++) {
s[i] = str[i];
}
accesses memory outside the string str.
Pay attention to that your function has redundant code. It can be written simpler.
The function can look for example the following way as shown in the demonstration program below.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char * zfill( const char *s, size_t n )
{
char *result = NULL;
if ( s != NULL )
{
size_t len = strlen( s );
n = len < n ? n : len;
result = malloc( n + 1 );
if ( result )
{
size_t i = 0;
size_t m = len < n ? n - len : 0;
for ( ; i < m; i++ )
{
result[i] = '0';
}
for ( ; i < n; i++ )
{
result[i] = s[i - m];
}
result[i] = '\0';
}
}
return result;
}
int main( void )
{
const char *s = "Hello";
size_t n = 10;
char *result = zfill( s, n );
if ( result ) puts( result );
free( result );
}
The program output is
00000Hello
Or as #Some programmer dude pointed to in his comment you can use the standard C function snprintf that alone performs the task. For example
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char * zfill( const char *s, size_t n )
{
char *result = NULL;
if ( s != NULL )
{
size_t len = strlen( s );
n = len < n ? n : len;
result = malloc( n + 1 );
if ( result )
{
int m = len < n ? n - len : 0;
snprintf( result, n + 1, "%.*d%s", m, 0, s );
}
}
return result;
}
int main( void )
{
char *p = zfill( "Hello", 5 );
if ( p ) puts( p );
free( p );
p = zfill( "Hello", 10 );
if ( p ) puts( p );
free( p );
}
The program output is
Hello
00000Hello
so you have 3 major problems in your code :
it's s[i] = '0'; not s[i] = "0";
it's s[i] = str[i - zeros]; not s[i] = str[i]; as the value of the i will be 27 in your test case : so it make sense to say s[27] because its size is about 41 but it doesn't make sense to say str[27] as its size is only about 13 in your test case , so you had to map the value 27 of i to the value 0 to be convenient to use with str
i is deprecated in first part here for (i; i < zeros; i++) , so use for (; i < zeros; i++)instead of for (i; i < zeros; i++) , but it will not cause any problem if you keep it.
and here is the full edited code :
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
char* zfill(const char* str, size_t n) {
if (str == NULL) {
return NULL;
}
char* s;
size_t length = strlen(str);
if (length >= n) {
//it doesn't have to add anything, just malloc and copy the string
size_t sum = length + 1u;
s = malloc(sum);
if (s == NULL) {
return NULL;
}
for (size_t i = 0; i < length; i++) {
s[i] = str[i];
}
s[sum] = 0;
}
else {
// add zeros before strings
size_t zeros = n - length;
size_t sum = n + 1u;
s = malloc(sum);
if (s == NULL) {
return NULL;
}
size_t i = 0;
for (; i < zeros; i++) {
s[i] = '0';
}
for (; i < n; i++) {
s[i] = str[i - zeros];
}
s[sum] = 0;
}
return s;
}
int main(void) {
char str[] = "hello, world!";
size_t n = 40;
char* s = zfill(str, n);
printf("%s\n", s);
free(s);
return 0;
}
Currently trying to work on my C (very new to it) by doing some leetcode questions. I'm puzzled by this issue, as it gives me a heap buffer overflow but only because of a single line. interpret() is called and passed a string command where 1 <= command.length <= 100, and will consist of "G", "()", and/or "(al)" in some order, with no other characters appearing.
char * interpret(char * command){
char * ret = malloc(sizeof(char) * 100);
int counter = 0;
for(int i = 0; i < sizeof(command) - 1; i++)
{
if(command[i] == 'G')
{
ret[counter] = 'G';
counter ++;
}
else if(command[i] == '(')
{
if (command[i + 1] == ')')
{
ret[counter] = 'o';
counter ++;
}
else
{
//ret[counter] = 'a'; ***********
ret[counter + 1] = 'l';
counter += 2;
}
}
ret[counter] = '\0';
}
return realloc(ret, counter * sizeof(char));
}
If the starred line is uncommented, then the entire program crashes in leetcode, but works fine on VSCode and returns the correct solution. I would appreciate any help, I'm sure it's something small I'm missing. Thanks.
ETA: Here is the leetcode problem in question
The parameter command has the pointer type char *.
So the operator sizeof applied to the pointer yields the size of the pointer instead of the length of the pointed string
for(int i = 0; i < sizeof(command) - 1; i++)
You could just write
for( size_t i = 0; command[i] != '\0'; i++)
Also it is unclear why there is used the magic number 100
char * ret = malloc(sizeof(char) * 100);
You could at first count the result characters and then allocated an array of the appropriate size and fill it.
Moreover due to this statement
ret[counter] = '\0';
(that is also unclear why it is within the for loop) you need to allocate an array with counter + 1 characters instead of counter characters as you are doing
return realloc(ret, counter * sizeof(char));
A straightforward approach can look the following way as shown in the demonstration program below.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char * interpret( const char *command )
{
size_t n = 0;
for ( size_t i = 0; command[i] != '\0'; )
{
if ( command[i] == 'G' )
{
++n;
++i;
}
else if ( strncmp( command + i, "()", 2 ) == 0 )
{
++n;
i += 2;
}
else if ( strncmp( command + i, "(al)", 4 ) == 0 )
{
n += 2;
i += 4;
}
else
{
++i;
}
}
char *result = malloc( n + 1 );
if ( result != NULL )
{
n = 0;
for ( size_t i = 0; command[i] != '\0'; )
{
if ( command[i] == 'G' )
{
result[n++] = 'G';
++i;
}
else if ( strncmp( command + i, "()", 2 ) == 0 )
{
result[n++] = 'o';
i += 2;
}
else if ( strncmp( command + i, "(al)", 4 ) == 0 )
{
result[n++] = 'a';
result[n++] = 'l';
i += 4;
}
else
{
++i;
}
}
result[n] = '\0';
}
return result;
}
int main( void )
{
char *s = interpret( "G()(al)" );
if ( s ) puts( s );
free( s );
s = interpret( "(al)G(al)()()G" );
if ( s ) puts( s );
free( s );
}
The program output is
Goal
alGalooG
Pass the size of command to interpret.
In C, when you pass a string to a function, you’re not actually passing the full string, you’re passing a pointer to the first element in the string. This becomes an issue when you do sizeof(command), as you're just getting the size of the pointer and not the full string. If you try to loop over this string as done in the question, this can either lead to an underread, if you have a string longer than sizeof(char*), or a buffer overflow, if you have a string shorter than sizeof(char*). Generally, you shouldn’t use sizeof on pointers.
To fix your code, use strlen on the string you're passing to command in the calling function and do something similar to this:
char * interpret(char * command, int size){
char * ret = malloc(sizeof(char) * 100);
int counter = 0;
for(int i = 0; i < size; i++)
{ ...
I have a function that manipulates a char*** using malloc and memcpy this way
// Convert a buffer full line to separated variables
int parseBufferToVariables(char ***variableContainer, char *bufferToParse, int maxVarSize) {
int i = 0;
// Get number of rows of the string
int numberOfRows = 0;
for (i = 0; bufferToParse[i] != '\0'; i++) {
if (bufferToParse[i] == '\n')
++numberOfRows;
}
// Get number of columns of the string
int numberOfColumns = 1;
for (i = 0; bufferToParse[i] != '\n'; i++) {
if (bufferToParse[i] == '\t')
++numberOfColumns;
}
// Allocate separated variable array
size_t dim0 = numberOfColumns, dim1 = numberOfRows, dim2 = maxVarSize;
variableContainer = malloc(sizeof *variableContainer * dim0);
if (variableContainer) {
size_t i;
for (i = 0; i < dim0; i++) {
variableContainer[i] = malloc(sizeof *variableContainer[i] * dim1);
if (variableContainer[i]) {
size_t j;
for (j = 0; j < dim1; j++) {
variableContainer[i][j] = malloc(sizeof *variableContainer[i][j] * dim2);
}
}
}
}
// Start parsing string to 3D array
int init = 0;
int numberOfVars = 0;
int numberOfLines = 0;
int sizeOfVar = 0;
int position = 0;
char emptyArray[MAXVARSIZE] = {0};
// Loop trought all lines
i = 0;
while (numberOfLines < numberOfRows) {
// Every delimiter
if (bufferToParse[i] == '\t' || bufferToParse[i] == '\n') {
// Size of the new sring
sizeOfVar = i - init;
// Set last \0 character in order to recognize as a proper string
memcpy(&variableContainer[numberOfVars][numberOfLines], emptyArray, maxVarSize);
// Copy the string to array
memcpy(&variableContainer[numberOfVars][numberOfLines], &bufferToParse[position], sizeOfVar);
// Handle pointers poisition
init = i + 1;
position += sizeOfVar + 1;
// Handle when end of line is reached
if (bufferToParse[i] == '\n') {
numberOfVars = 0;
numberOfLines++;
}
}
i++;
}
return numberOfRows;
}
And Im trying to call it in different ways:
char*** container= {0};
parseBufferToVariables (&container, inputString, MAXVARSIZE);
char*** container= {0};
parseBufferToVariables (container, inputString, MAXVARSIZE);
Even I try calling a char**** in the function:
int parseBufferToVariables(char**** variableContainer, char* bufferToParse, int maxVarSize)
But I always have a seg-fault calling the char*** outside the parseBufferToVariables function.
Any ideas?
OP is shooting for a 4 * parameter, yet other approaches are better.
The high degree of *s mask a key failing is that code needs to convey the column (# of tabs) width somehow.
Further, I see no certain null character termination in forming the _strings_as
the 2nd memcpy() is unbounded in size - may even overwrite allocation boundaries.
The idea below is that each level of allocation ends with a null.
csv = parse_file_string(const char *file_string);
Upon return, when csv[line] == NULL, there are no more lines
When csv[line][tab] == NULL, there are no more strings.
This approach also allows for a different number of strings per line.
Adjusted algorithm, pseudo C code
// return NULL on error
char ***parse_file_string(const char *file_string) {
number_lines = find_line_count(file_string);
char ***csv = calloc(number_lines + 1, sizeof *csv);
if (csv == NULL) return NULL;
for (line=0; line < number_lines; line++) {
tab_count = find_tab_count(file_string);
csv[line] = calloc(tab_count + 2, sizeof *(csv[line]));
// add NULL check
for (tab=0; tab < tab_count; tab++) {
char *end = strchr(file_string, '\t');
csv[line][tab] = malloc_string(file_string, end);
// add NULL check
file_string = end + 1;
}
char *end = strchr(file_string, '\n');
csv[line][tab++] = malloc_str(file_string, end);
// add NULL check
file_string = end + 1;
csv[line][tab] = NULL;
}
csv[line] = NULL;
return csv;
}
Usage
char ***container = parse_file_string(file_string);
for (line=0; container[line]; line++)
for (tab=0; container[line][tab]; tab++)
puts(container[line][tab]);
//free
for (line=0; container[line]; line++)
for (tab=0; container[line][tab]; tab++)
free(container[line][tab]);
free(container[line]);
free (container)
A pointer to a variable length array could be used if supported.
First get the dimensions of the contents of the buffer. This assumes that each line will have the same number of tabs.
Declare the pointer and allocate the memory.
Then parse the buffer into the allocated memory.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void getdimension ( char *buffer, int *rows, int *cols, int *size) {
int maxsize = 0;
*rows = 0;
*cols = 0;
*size = 0;
while ( *buffer) {//not the terminating zero
if ( '\n' == *buffer) {
if ( ! *rows) {//no rows counted yet
++*cols;//add a column
}
++*rows;
if ( maxsize > *size) {
*size = maxsize;
}
maxsize = 0;
}
if ( '\t' == *buffer) {
if ( ! *rows) {//no rows counted yet
++*cols;
}
if ( maxsize > *size) {
*size = maxsize;
}
maxsize = 0;
}
++maxsize;
++buffer;
}
if ( '\n' != *(buffer - 1)) {//last character is not a newline
++*rows;
if ( maxsize > *size) {
*size = maxsize;
}
}
}
void createptr ( int rows, int columns, int size, char (**ptr)[columns][size]) {
if ( NULL == ( *ptr = malloc ( sizeof **ptr * rows))) {
fprintf ( stderr, "malloc problem\n");
exit ( EXIT_FAILURE);
}
for ( int line = 0; line < rows; ++line) {
for ( int tab = 0; tab < columns; ++tab) {
(*ptr)[line][tab][0] = 0;
}
}
}
void parsebuffer ( char *buffer, int rows, int columns, int size, char (*ptr)[columns][size]) {
int eachrow = 0;
int eachcol = 0;
int eachsize = 0;
while ( *buffer) {
if ( '\n' == *buffer) {
++eachrow;
eachcol = 0;
eachsize = 0;
}
else if ( '\t' == *buffer) {
++eachcol;
eachsize = 0;
}
else {
ptr[eachrow][eachcol][eachsize] = *buffer;
++eachsize;
ptr[eachrow][eachcol][eachsize] = 0;
}
++buffer;
}
}
int main ( void) {
char line[] = "12\t34\t56\t78\t!##\n"
"abc\tdef\tghi\tjkl\t$%^\n"
"mno\tpqr\tstu\tvwx\tyz\n"
"ABC\tDEF\tGHI\tJKL\tMNOPQ\n";
int rows = 0;
int columns = 0;
int size = 0;
getdimension ( line, &rows, &columns, &size);
printf ( "rows %d cols %d size %d\n", rows, columns, size);
char (*ptr)[columns][size] = NULL;//pointer to variable length array
createptr ( rows, columns, size, &ptr);
parsebuffer ( line, rows, columns, size, ptr);
for ( int row = 0; row < rows; ++row) {
for ( int col = 0; col < columns; ++col) {
printf ( "ptr[%d][%d] %s\n", row, col, ptr[row][col]);
}
}
free ( ptr);
return 0;
}
I'm stuck with sorting letters in a string. It must be sorted in alphabetical order using double pointers.
#define SIZE 21 //defined index of the array
int _tmain(int argc, _TCHAR* argv[])
{
// an array with 21 strings
char * string[SIZE] = { "dfghgfd", "rtyukljgfds", "sdsf", "fdgdfhg", "fgfhgjghj", "nmjlkjlk", "qwasazx",
"zxdfd", "opiljkg", "vcxdfgfd", "fgfhgfhgh", "bvvh", "bb", "dfsdretr",
"reuio", "cvbmhg", "fgfdyrtyty", "fgdgdfgdfgdf", "g", "fgdfg", "ghghgfhv" };
-----------------------Access to each string in array ------------------------
int Anz, i; //Anz - 21 strings
//declared new array
char** new_string;
new_string = (char**)malloc(sizeof(string));
Anz = sizeof(string) / sizeof(char*);
for (i = 0; i < Anz; i++)
{
new_string[i] = (char*)malloc(strlen(string[i]) + 1);
strcpy(new_string[i], string[i]);
}
----------------------- sorting letters--------------------------------------
char* temp;
int k, j;
for (k = 0; k<Anz - 1; k++)
{
for (j = k + 1; j<Anz; j++)
{
if (new_string[k] > new_string[j])
{
temp = new_string[k];
new_string[k] = new_string[j];
new_string[j] = temp;
}
}
}
return 0;
}
Not sure why you need the "a" array, since you can swap the characters using the new string array. Also, using a char * to hold the length of values is sort weird but I guess it works since the string lengths are pretty short.
Not sure if you wanted to sort the letters or the words. A commented section sorts the words.
Check the return of malloc as it can fail.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SIZE 21 //defined index of the array
int main(int argc, char** argv)
{
// an array with 21 strings
char * string[SIZE] = { "dfghgfd", "rtyukljgfds", "sdsf", "fdgdfhg", "fgfhgjghj", "nmjlkjlk", "qwasazx",
"zxdfd", "opiljkg", "vcxdfgfd", "fgfhgfhgh", "bvvh", "bb", "dfsdretr",
"reuio", "cvbmhg", "fgfdyrtyty", "fgdgdfgdfgdf", "g", "fgdfg", "ghghgfhv" };
int Anz, i; //Anz - 21 strings
int width = 0, len = 0;
//declared new array
char** new_string;
Anz = sizeof(string) / sizeof(*string);
if ( NULL == ( new_string = malloc ( Anz * sizeof( *new_string)))) {
fprintf ( stderr, "malloc failed\n");
return 0;
}
for (i = 0; i < Anz; i++)
{
len = strlen ( string[i]) + 1;
if ( len > width) {
width = len;//used later when printing
}
if ( NULL == ( new_string [i] = malloc ( width))) {
fprintf ( stderr, "[i] malloc failed\n");
//free memory allocated
while ( i) {
i--;
free ( new_string[i]);
}
free ( new_string);
return 0;
}
strcpy(new_string[i], string[i]);
}
/*
//sort words
int word = 0;
while ( word < Anz - 1) {
int end = word;
int temp = end + 1;
while ( end >= 0 && 0 > strcmp ( new_string[temp], new_string[end])) {
char *hold = new_string[temp];
new_string[temp] = new_string[end];
new_string[end] = hold;
end--;
temp--;
}
word++;
}
word = 0;
while ( word < Anz) {
printf ( "Anz[%2d] is %s\n", word, new_string[word]);
word++;
}
*/
//sort letters in word
char swap;
int sorted;
int prior;
int each;
int start;
word = 0;
while ( word < Anz)
{
start = 0;//new_string[Anz][0]
sorted = start;
prior = start;
each = start + 1;//new_string[Anz][1]
printf ( "Anz[%2d] is %-*s", word, width, new_string[word]);
while ( '\0' != new_string[word][each]) {
while ( prior >= 0 && new_string[word][each] < new_string[word][prior]) {
swap = new_string[word][each];
new_string[word][each] = new_string[word][prior];
new_string[word][prior] = swap;
each--;//move toward start of string
prior--;
}
sorted++;//move toward end of string
prior = sorted;
each = prior + 1;
}
printf ( " sorted %s\n", new_string[word]);
word++;
}
//release allocated memory
word = 0;
while ( word < Anz) {
free ( new_string[word]);
word++;
}
free ( new_string);
return 0;
}
I'm trying to create a function that can insert a char array of length len into a given index in a string type. However, it acts ways that it shouldn't.
When trying to insert at the end of the initial array, the contents of buf are inserted at the beginning of the string, in reverse order.
i.e. inserting world into Hello becomes dlrowHello
When trying to insert in the middle of the initial case, the output is worldHello
str->bytes.data is the array being modified.
str->bytes.usage is the length of that array.
bool string_insert(string_t * const str, size_t index,
const char * const buf, size_t len)
{
bool success = false;
uint8_t orig_size = str->bytes.usage;
uint8_t * temp;
if (str->len < index){
return false;
}
if(str->bytes.usage+len>=str->bytes.usage){
temp = malloc(sizeof(char) * (str->bytes.usage + len));
}
else {
temp = malloc(sizeof(char) * (str->bytes.usage));
}
if (temp == NULL){
success = false;
}
else{
if (index == 0){ //inserts at beginning
for (int k = 0; k < len; k++){
temp[k] = buf[k];
}
for (int j = len; j < str->bytes.usage+len; j++){
temp[j] = str->bytes.data[j - len];
}
}
else if (index == str->bytes.usage){ //inserts at end
for (int h = 0; h < str->bytes.usage; h++){
temp[h] = str->bytes.data[h];
}
for (int g = 0; g < len; g++){
temp[g+str->bytes.usage] = buf[g];
}
}
else{ //inserts in the middle
for (int i = 0; i < index; i++){
temp[i] = str->bytes.data[i];
}
for(int i = index; i < index + len; i++){
temp[i] = buf[i-index];
}
for(int i = index + len; i < str->bytes.usage+len; i++){
temp[i] = str->bytes.data[i-len];
}
}
string_free(str);
str->bytes.data = temp;
str->bytes.dim = 2*str->bytes.usage;
str->bytes.usage = orig_size+len;
success = true;
}
return success;
}
Basically, I just need to know if I'm blind and missing something obvious.
When index is 0, it is not in reverse order?
if (index == 0){ //inserts at beginning of array
for (int k = 0; k < len; k++){
temp[k] = buf[k];
changed to
if (index == 0){ //inserts at beginning of array
for (int k = 0; k < len; k++){
temp[k] = buf[len - k - 1];
// as i understand it:
// str->bytes.data = allocated memory
// str->bytes.dim = size of the allocated memory
// str->bytes.usage= size of the used memory
//
#define BLOCK_SIZE 32
bool string_insert(string_t * const str, size_t index,
const char * buf, size_t len)
{
int max_size;
int i;
char *start;
char *end;
char *tmp=str->bytes.data;
max_size=str->bytes.usage+len;
if(max_size>=str->bytes.dim){
// realloc by BLOCK_SIZE steps
max_size=((max_size/BLOCK_SIZE)+1)*BLOCK_SIZE;
tmp=realloc(tmp,max_size);
if(!tmp) return 0; //false
//update fields with new values
str->bytes.data=tmp;
str->bytes.dim=max_size;
}
start=tmp+index;
end=tmp+(str->bytes.usage);
//shift content after the insertion point
while(end>start){
end--;
*(end+len)=*end;
}
// no comments
strncpy (start,buf,len);
str->bytes.usage+=len;
return 1;
}
Rather than the posted convolutions, you could try something similar to this
Assuming:
Not normal C strings
the string_t instances do not contain enough unused memory to
hold both the strings
string 2 is to be inserted at the offset into string 1
the string_t is defined as:
typedef struct
{
char * pStr;
int strLen;
} string_t;
caveat: not compiled, nor tested
string_t *string_insert(
const string_t * originalStr,
const string_t * strToInsert,
size_t index )
{
string_t *newString = NULL;
if( NULL != (newString = malloc( sizeof( string_t )) ) )
{ // then malloc successful
newString->pStr = NULL;
newString->strLen = 0;
// allocate enough room.
if(NULL != (newString->pStr =
malloc( originalStr->strlen + strToInsert->strlen )
{// then mallooc successful
newString->strLen =
origianlStr->strlen + strToInsert->strLen;
//copy first part of originalStr to newString
for( int i = 0; i < index; i++ )
{
newString->pStr[i] = originalStr->pStr[i];
}
// copy string to insert
for( int j=0; j<strToInsert->strlen; j++ )
{
newStr->pStr[i+j] = strToInsert-pStr[j];
}
// copy last part of originalStr to newStr
for ( int k = 0; k<newString->strLen; k++ )
{
newStr->pStr[i+j+k] = origianalStr->pStr[index+k];
}
}
else
{
free( newString );
newString = NULL;
}
}
return newString
} // end function: string_insert
You have
if (str->len < index){
, but you fail to assign anything to str->len.