Memory allocating and memory leaks: C - c

I'm fairly new with C. I have a function called kstrfrom that needs to create and return a new kstring object that contains a copy of the contents of a null-terminated C string, including the null terminator.
The .length member of the returned kstring should be the length of cstr, plus one for the null terminator. The .data member should be a pointer to newly-allocated memory, into which you have copied the contents of cstr, including the null byte at the end.
If there is an error allocating memory, this function should call abort() or throw an uncaught exception.
kstring kstrfrom(const char *cstr)
{
int length=1;
while(*cstr!='\0')
{
cstr+=1;
length+=1;
}
int i = 0;
kstring cdest={NULL,0};
cdest.data = malloc(length+1);
if(cdest.data == '\0')
{
abort();
}
else{
while(cstr[i] != '\0')
{
cdest.data[i] = cstr[i];
i++;
}
}
cdest.data[i] = '\0';
cdest.data[++i] = '\0';
return cdest;
}
I've ran a few test cases:
Test 9/ 26: kstrfrom gives correct length skipped (0)
Test 10/ 26: kstrfrom contains null byte succeeded (1)
Test 11/ 26: kstrfrom contains correct data skipped (0)
Test 12/ 26: kstrfrom copies, not shares, data skipped (0)
As you can see I need help with giving correct link, containing correct data and copying data.

At the end of
while(*cstr!='\0')
{
cstr+=1;
length+=1;
}
You have lost the initial value of cstr
try
int length=1;
char * tmp = cstr;
while(*tmp!='\0')
{
tmp+=1;
length+=1;
}
You are not setting the length member...
cdest.length = length + 1;
Returning the kstring is problematic.
kstring res;
res = kstrfrom( "My String" ); /* works */
kstrfrom( "Another string" ); /* leaks memory */
Other comments are describing how you are ignoring language features.
Your code can be achieved more easily with...
kstring kstrfrom(const char *cstr)
{
kstring cdest={NULL,0};
cdest.data = strdup( cstr );
if( cdest.data == NULL ){
abort();
}
cdest.length = strlen( cstr ) + 1; /* not done in original example */
return cdest;
}

Related

Cannot access empty string from array of strings in C

I'm using an array of strings in C to hold arguments given to a custom shell. I initialize the array of buffers using:
char *args[MAX_CHAR];
Once I parse the arguments, I send them to the following function to determine the type of IO redirection if there are any (this is just the first of 3 functions to check for redirection and it only checks for STDIN redirection).
int parseInputFile(char **args, char *inputFilePath) {
char *inputSymbol = "<";
int isFound = 0;
for (int i = 0; i < MAX_ARG; i++) {
if (strlen(args[i]) == 0) {
isFound = 0;
break;
}
if ((strcmp(args[i], inputSymbol)) == 0) {
strcpy(inputFilePath, args[i+1]);
isFound = 1;
break;
}
}
return isFound;
}
Once I compile and run the shell, it crashes with a SIGSEGV. Using GDB I determined that the shell is crashing on the following line:
if (strlen(args[i]) == 0) {
This is because the address of arg[i] (the first empty string after the parsed commands) is inaccessible. Here is the error from GDB and all relevant variables:
(gdb) next
359 if (strlen(args[i]) == 0) {
(gdb) p args[0]
$1 = 0x7fffffffe570 "echo"
(gdb) p args[1]
$2 = 0x7fffffffe575 "test"
(gdb) p args[2]
$3 = 0x0
(gdb) p i
$4 = 2
(gdb) next
Program received signal SIGSEGV, Segmentation fault.
parseInputFile (args=0x7fffffffd570, inputFilePath=0x7fffffffd240 "") at shell.c:359
359 if (strlen(args[i]) == 0) {
I believe that the p args[2] returning $3 = 0x0 means that because the index has yet to be written to, it is mapped to address 0x0 which is out of the bounds of execution. Although I can't figure out why this is because it was declared as a buffer. Any suggestions on how to solve this problem?
EDIT: Per Kaylum's comment, here is a minimal reproducible example
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include <sys/stat.h>
#include<readline/readline.h>
#include<readline/history.h>
#include <fcntl.h>
// Defined values
#define MAX_CHAR 256
#define MAX_ARG 64
#define clear() printf("\033[H\033[J") // Clear window
#define DEFAULT_PROMPT_SUFFIX "> "
char PROMPT[MAX_CHAR], SPATH[1024];
int parseInputFile(char **args, char *inputFilePath) {
char *inputSymbol = "<";
int isFound = 0;
for (int i = 0; i < MAX_ARG; i++) {
if (strlen(args[i]) == 0) {
isFound = 0;
break;
}
if ((strcmp(args[i], inputSymbol)) == 0) {
strcpy(inputFilePath, args[i+1]);
isFound = 1;
break;
}
}
return isFound;
}
int ioRedirectHandler(char **args) {
char inputFilePath[MAX_CHAR] = "";
// Check if any redirects exist
if (parseInputFile(args, inputFilePath)) {
return 1;
} else {
return 0;
}
}
void parseArgs(char *cmd, char **cmdArgs) {
int na;
// Separate each argument of a command to a separate string
for (na = 0; na < MAX_ARG; na++) {
cmdArgs[na] = strsep(&cmd, " ");
if (cmdArgs[na] == NULL) {
break;
}
if (strlen(cmdArgs[na]) == 0) {
na--;
}
}
}
int processInput(char* input, char **args, char **pipedArgs) {
// Parse the single command and args
parseArgs(input, args);
return 0;
}
int getInput(char *input) {
char *buf, loc_prompt[MAX_CHAR] = "\n";
strcat(loc_prompt, PROMPT);
buf = readline(loc_prompt);
if (strlen(buf) != 0) {
add_history(buf);
strcpy(input, buf);
return 0;
} else {
return 1;
}
}
void init() {
char *uname;
clear();
uname = getenv("USER");
printf("\n\n \t\tWelcome to Student Shell, %s! \n\n", uname);
// Initialize the prompt
snprintf(PROMPT, MAX_CHAR, "%s%s", uname, DEFAULT_PROMPT_SUFFIX);
}
int main() {
char input[MAX_CHAR];
char *args[MAX_CHAR], *pipedArgs[MAX_CHAR];
int isPiped = 0, isIORedir = 0;
init();
while(1) {
// Get the user input
if (getInput(input)) {
continue;
}
isPiped = processInput(input, args, pipedArgs);
isIORedir = ioRedirectHandler(args);
}
return 0;
}
Note: If I forgot to include any important information, please let me know and I can get it updated.
When you write
char *args[MAX_CHAR];
you allocate room for MAX_CHAR pointers to char. You do not initialise the array. If it is a global variable, you will have initialised all the pointers to NULL, but you do it in a function, so the elements in the array can point anywhere. You should not dereference them before you have set the pointers to point at something you are allowed to access.
You also do this, though, in parseArgs(), where you do this:
cmdArgs[na] = strsep(&cmd, " ");
There are two potential issues here, but let's deal with the one you hit first. When strsep() is through the tokens you are splitting, it returns NULL. You test for that to get out of parseArgs() so you already know this. However, where your program crashes you seem to have forgotten this again. You call strlen() on a NULL pointer, and that is a no-no.
There is a difference between NULL and the empty string. An empty string is a pointer to a buffer that has the zero char first; the string "" is a pointer to a location that holds the character '\0'. The NULL pointer is a special value for pointers, often address zero, that means that the pointer doesn't point anywhere. Obviously, the NULL pointer cannot point to an empty string. You need to check if an argument is NULL, not if it is the empty string.
If you want to check both for NULL and the empty string, you could do something like
if (!args[i] || strlen(args[i]) == 0) {
If args[i] is NULL then !args[i] is true, so you will enter the if body if you have NULL or if you have a pointer to an empty string.
(You could also check the empty string with !(*args[i]); *args[i] is the first character that args[i] points at. So *args[i] is zero if you have the empty string; zero is interpreted as false, so !(*args[i]) is true if and only if args[i] is the empty string. Not that this is more readable, but it shows again the difference between empty strings and NULL).
I mentioned another issue with the parsed arguments. Whether it is a problem or not depends on the application. But when you parse a string with strsep(), you get pointers into the parsed string. You have to be careful not to free that string (it is input in your main() function) or to modify it after you have parsed the string. If you change the string, you have changed what all the parsed strings look at. You do not do this in your program, so it isn't a problem here, but it is worth keeping in mind. If you want your parsed arguments to survive longer than they do now, after the next command is passed, you need to copy them. The next command that is passed will change them as it is now.
In main
char input[MAX_CHAR];
char *args[MAX_CHAR], *pipedArgs[MAX_CHAR];
are all uninitialized. They contain indeterminate values. This could be a potential source of bugs, but is not the reason here, as
getInput modifies the contents of input to be a valid string before any reads occur.
pipedArgs is unused, so raises no issues (yet).
args is modified by parseArgs to (possibly!) contain a NULL sentinel value, without any indeterminate pointers being read first.
Firstly, in parseArgs it is possible to completely fill args without setting the NULL sentinel value that other parts of the program should rely on.
Looking deeper, in parseInputFile the following
if (strlen(args[i]) == 0)
contradicts the limits imposed by parseArgs that disallows empty strings in the array. More importantly, args[i] may be the sentinel NULL value, and strlen expects a non-NULL pointer to a valid string.
This termination condition should simply check if args[i] is NULL.
With
strcpy(inputFilePath, args[i+1]);
args[i+1] might also be the NULL sentinel value, and strcpy also expects non-NULL pointers to valid strings. You can see this in action when inputSymbol is a match for the final token in the array.
args[i+1] may also evaluate as args[MAX_ARGS], which would be out of bounds.
Additionally, inputFilePath has a string length limit of MAX_CHAR - 1, and args[i+1] is (possibly!) a dynamically allocated string whose length might exceed this.
Some edge cases found in getInput:
Both arguments to
strcat(loc_prompt, PROMPT);
are of the size MAX_CHAR. Since loc_prompt has a length of 1. If PROMPT has the length MAX_CHAR - 1, the resulting string will have the length MAX_CHAR. This would leave no room for the NUL terminating byte.
readline can return NULL in some situations, so
buf = readline(loc_prompt);
if (strlen(buf) != 0) {
can again pass the NULL pointer to strlen.
A similar issue as before, on success readline returns a string of dynamic length, and
strcpy(input, buf);
can cause a buffer overflow by attempting to copy a string greater in length than MAX_CHAR - 1.
buf is a pointer to data allocated by malloc. It's unclear what add_history does, but this pointer must eventually be passed to free.
Some considerations.
Firstly, it is a good habit to initialize your data, even if it might not matter.
Secondly, using constants (#define MAX_CHAR 256) might help to reduce magic numbers, but they can lead you to design your program too rigidly if used in the same way.
Consider building your functions to accept a limit as an argument, and return a length. This allows you to more strictly track the sizes of your data, and prevents you from always designing around the maximum potential case.
A slightly contrived example of designing like this. We can see that find does not have to concern itself with possibly checking MAX_ARGS elements, as it is told precisely how long the list of valid elements is.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_ARGS 100
char *get_input(char *dest, size_t sz, const char *display) {
char *res;
if (display)
printf("%s", display);
if ((res = fgets(dest, sz, stdin)))
dest[strcspn(dest, "\n")] = '\0';
return res;
}
size_t find(char **list, size_t length, const char *str) {
for (size_t i = 0; i < length; i++)
if (strcmp(list[i], str) == 0)
return i;
return length;
}
size_t split(char **list, size_t limit, char *source, const char *delim) {
size_t length = 0;
char *token;
while (length < limit && (token = strsep(&source, delim)))
if (*token)
list[length++] = token;
return length;
}
int main(void) {
char input[512] = { 0 };
char *args[MAX_ARGS] = { 0 };
puts("Welcome to the shell.");
while (1) {
if (get_input(input, sizeof input, "$ ")) {
size_t argl = split(args, MAX_ARGS, input, " ");
size_t redirection = find(args, argl, "<");
puts("Command parts:");
for (size_t i = 0; i < redirection; i++)
printf("%zu: %s\n", i, args[i]);
puts("Input files:");
if (redirection == argl)
puts("[[NONE]]");
else for (size_t i = redirection + 1; i < argl; i++)
printf("%zu: %s\n", i, args[i]);
}
}
}

C using malloc and realloc to dynamically increase string length

Currently learning memory management in C, and I am currently running into issues increasing string length as a loop iterates.
The method I am trying to figure out logically works like this:
// return string with "X" removed
char * notX(char * string){
result = "";
if(for int = 0; i < strlen(string); i++){
if (string[i] != 'X') {
result += string[i];
}
}
return result;
}
Simple enough to do in other languages, but managing the memory in C makes it a bit challenging. Difficulties I run into is when I use malloc and realloc to initialize and change size of my string. In my code I currently tried:
char * notX(char * string){
char* res = malloc(sizeof(char*)); // allocate memory for string of size 1;
res = ""; // attempted to initialize the string. Fairly certain this is incorrect
char tmp[2]; // temporary string to hold value to be concatenated
if(for int = 0; i < strlen(string); i++){
if (string[i] != 'X') {
res = realloc(res, sizeof(res) + sizeof(char*)); // reallocate res and increasing its size by 1 character
tmp[0] = string[i];
tmp[1] = '\0';
strcat(res, tmp);
}
}
return result;
}
Note, I have found success in initializing result to be some large array like:
char res[100];
However, I would like to learn how to address this issue with out initializing an array with a fixed size since that might potentially be wasted memory space, or not enough memory.
realloc needs the number of bytes to allocate. size is incremented for each character added to res. size + 2 is used to provide for the current character being added and the terminating zero.
Check the return of realloc. NULL means a failure. Using tmp allows the return of res if realloc fails.
char * notX(char * string){
char* res = NULL;//so realloc will work on first call
char* tmp = NULL;//temp pointer during realloc
size_t size = 0;
size_t index = 0;
while ( string[index]) {//not the terminating zero
if ( string[index] != 'X') {
if ( NULL == ( tmp = realloc(res, size + 2))) {//+ 2 for character and zero
fprintf ( stderr, "realloc problem\n");
if ( res) {//not NULL
res[size] = 0;//terminate
}
return res;
}
res = tmp;//assign realloc pointer back to res
res[size] = string[index];
++size;
}
++index;//next character
}
if ( res) {//not NULL
res[size] = 0;//terminate
}
return res;
}
2 main errors in this code:
the malloc and the realloc function with parameter that call sizeof(char*). In this case the result of sizeof(char*) is the size of a pointer, not of a char, so you have to substitute the char* with char in the sizeof function.
res = ""; is incorrect. You primarly have a memory leak because you lose the pointer to the just allocated memory in malloc function, secondary but not less important, you have an undefined behavior when call realloc function over res initialized as an empty string ( or better a constant string), after the above initialization the memory is no longer dinamically managed. To substitute this initialization i think a memset to 0 is the best solution.

Char array of array values to pointer array

I need put array of array chat values in to pointer array. for that first I used code like this and it works for me.
char *current_tag_lists[20];
char current_tag_list1[]="0005808897";
char current_tag_list2[]="0009953997";
char current_tag_list3[]="0000116600";
current_tag_lists[0] = current_tag_list1;
current_tag_lists[1] = current_tag_list2;
current_tag_lists[2] = current_tag_list3;
so I can access the value by index current_tag_lists[0].
But my actual requirement is to add these value in run time as follows. This is a example code.
char *current_tag_lists[20];
while(k<6)
{
char RFID_input_characters[12];
while(a<13){
if(a==12){
current_tag_lists[k]=RFID_input_characters;
a=0;
k++;
break;
}
else{
RFID_input_characters[a]='a'; // this example in my code it get value like this
a++;
}
}
}
But the problem is that "current_tag_lists" does not store all the values. it only store the current value. It every time replace the previous value. i need to keep the values as my above example and need to access from index (current_tag_lists[0]).
Can anyone please help me.
This is my actual code.
while(k<6)//while(!(c1==1))
{
char RFID_input_characters[12]={0};
while(a<14){
if (a == 0) {
ReceiveByteSerially();
a++;
}
else if (a == 13 ) {
ReceiveByteSerially();
current_tag_lists[k] = malloc(strlen(RFID_input_characters) + 1);
strcpy(current_tag_lists[k], RFID_input_characters);
Lcd_Set_Cursor(1,1);
Lcd_Write_String(RFID_input_characters);
Lcd_Set_Cursor(2,1);
Lcd_Write_String(current_tag_lists[k]);
a = 0;
k++;
break;
}
else if(k%2!=0 && a==1){
char x=ReceiveByteSerially();
if((x!=0x02)&&(x!=0X03)){
a++;
}
}
else{
char x=ReceiveByteSerially();
if((x!=0x02)&&(x!=0X03)){
if(k%2 !=0){
RFID_input_characters[a-2] = x;
}
else if(a<12){
RFID_input_characters[a-1] = x;
}
a++;
}
}
}
}
please only look the if(a==13).
This is my error log.
C:\Program Files (x86)\Microchip\xc8\v1.33\sources\common\strcpy.c:19: error: (1466) registers unavailable for code generation of this expression
(908) exit status = 1
make[2]: *** [dist/default/production/Super_Smart_Backpack.X.production.hex] Error 1
make[1]: *** [.build-conf] Error 2
make: *** [.build-impl] Error 2`
`nbproject/Makefile-default.mk:119: recipe for target 'dist/default/production/Super_Smart_Backpack.X.production.hex' failed
make[2]: Leaving directory 'F:/Irushi-final/Super Smart Backpack.X/Super Smart Backpack.X'
nbproject/Makefile-default.mk:78: recipe for target '.build-conf' failed
make[1]: Leaving directory 'F:/Irushi-final/Super Smart Backpack.X/Super Smart Backpack.X'
nbproject/Makefile-impl.mk:39: recipe for target '.build-impl' failed
BUILD FAILED (exit value 2, total time: 1s)
given the posted code:
char *current_tag_lists[20];
while(k<6)
{
char RFID_input_characters[12];
while(a<13){
if(a==12){
current_tag_lists[k]=RFID_input_characters;
a=0;
k++;
break;
}
else{
RFID_input_characters[a]='a'; // this example in my code it get value like this
a++;
}
}
}
There are several details that need attention:
1) need to initialize the current_tag_lists[] to all NULLs to make it easy when later the malloc'd strings need to be passed to free()
char *current_tag_lists[2] = { NULL };
2) each string needs a unique place to be stored:
char *temp =NULL;
if( NULL == (temp = malloc( 12 ) ) )
{ // then malloc failed
perror( "malloc failed" );
cleanup(); // frees all malloc'd areas
exit( EXIT_FAILURE );
}
// implied else, malloc successful
regarding this line:
while( a < 13 )
the max number of characters per entry is (per the original code and the above mallocf() 12. C references offsets into arrays as 0...(array len -1)
So 12 (a<13) would be accessing beyond the upper bound of the array.
Leading to undefined behaviour, which can/will lead to a seg fault event.
Suggest the following code:
#include <stdlib.h> // exit, EXIT_FAILURE
#include <string.h> // malloc, free
#define MAX_TAG_LISTS (20)
#define MAX_RFID_LEN (12)
char *current_tag_lists[ MAX_TAG_LISTS ];
// initialize current_tag_lists[] to make cleanup simple
memset( current_tag_lists, '\0', sizeof(current_tag_lists));
char *temp =NULL;
for( int k = 0; k < MAX_TAG_LISTS; k++ )
{
if( NULL == (temp = malloc( MAX_RFID_LEN ) ) )
{ // then malloc failed
perror( "malloc failed" );
cleanup( current_tag_lists ); // frees all malloc'd areas
exit( EXIT_FAILURE );
}
for( int a = 0; a < MAX_RFID_LEN; a++ )
{
temp[a]='a'; // this example in my code it get value like this
} // end for
current_tag_lists[k]=temp;
temp = NULL;
} // end for
void cleanup( char *current_tag_lists )
{
int i;
for( i = 0; i < MAX_TAG_LISTS; i++)
{
// note: ok to pass NULL to free
free( current_tag_lists[i] );
}
} // end function: cleanup
This is how you can do it:
If I got you correctly.
// First you create a normal null terminated string and copy some test string to it
char RFID_input_characters[12]="test";
....
// Now since you have array of pointers, you can allocate some space
// and set k-th pointer point to it
current_tag_lists[k] = malloc(strlen(RFID_input_characters) + 1);
// Now you can copy something to the memory you allocated
strcpy(current_tag_lists[k], RFID_input_characters);
don't forget to free later.
The way you had it always you were setting each pointer point to the same address - that is starting address of array RFID_input_characters (the assignment you had there was not copying the string, just setting k-th pointer point to the start of RFID_input_characters array. To copy strings you can use strcpy or its safer versions for instance).
On a microcontroller you may not want to use malloc and your algorithm seems to have very determined needs, as it's normal for microcontrollers. In your algorithm
char *current_tag_lists[20];
while(k<6)
{
char RFID_input_characters[12];
you define some numbers. First you should use symbols for the constants 20,6 and 12:
enum tag_list_params { tag_list_len=20, rfid_len=12, num_input_cycles=6 };
and replace them in your code.
Now you define the space
typedef char rfid_t[rfid_len];
rfid_t tag_list_space[tag_list_len];
then you can point with current_tag_list into the tag_list_space
char* current_tag_lists[20];
of course you could use the &tag_list_space[a] directly, but you can do
current_tag_lists[k]=(char*)&tag_list_space[k];
to use the variables as you defined them. When you write into the tag list, you can simply point into the space too
char* RFID_input_characters;
for(k=0;k<num_input_cycles;k++)
{
current_tag_lists[k]=(char*)&tag_list_space[k]; /* save to your target structure */
RFID_input_characters = (char*)&tag_list_space[k];
for(a=0; a<rfid_len;++a) {
RFID_input_characters[a] = 'a';
}
}
The full test program might look similar to this:
/* enum tag_list_params { tag_list_len=20, rfid_len=12, num_input_cycles=6 }; */
#define tag_list_len 20
#define rfid_len 12
#define num_input_cycles 6
typedef char rfid_t[rfid_len];
char* current_tag_lists[tag_list_len]; /* this is the parameter you want to modify outside
you can define it as
extern char** current_tag_list;
if you export it in a header file */
static rfid_t tag_list_space[tag_list_len]; /* if you define the space inside of the function,
the memory will get lost on function exit */
void all_a(void)
{
int k, a;
char* RFID_input_characters;
for(k=0;k<num_input_cycles;k++)
{
current_tag_lists[k]=(char*)&tag_list_space[k]; /* save to your target structure */
RFID_input_characters = (char*)&tag_list_space[k];
for(a=0; a<rfid_len;++a) {
RFID_input_characters[a] = 'a';
}
}
}
void main(void)
{
all_a();
}
From gdb output when all_a is ready:
(gdb) p current_tag_lists
$1 = {0x601080 <tag_list_space> 'a' <repeats 72 times>,
0x60108c <tag_list_space+12> 'a' <repeats 60 times>,
0x601098 <tag_list_space+24> 'a' <repeats 48 times>,
0x6010a4 <tag_list_space+36> 'a' <repeats 36 times>,
0x6010b0 <tag_list_space+48> 'a' <repeats 24 times>,
0x6010bc <tag_list_space+60> 'a' <repeats 12 times>,
0x0 <repeats 14 times>}
Actually char *current_tag_lists[20]; is an array of pointer to chars.
In this line current_tag_lists[k]=RFID_input_characters; you are storing the pointer RFID_input_characters in current_tag_lists[k] and not its contents. Since current_tag_lists[k] is pointing to memory address of RFID_input_characters , changing the contents of RFID_input_characters gets reflected in current_tag_lists[k].
You can change your code like this:
char *current_tag_lists[20];
while(k<6)
{
char RFID_input_characters[12];
a=0;
while(a<13){
if(a==12){
current_tag_lists[k]=malloc(12);
strcpy(current_tag_lists[k],RFID_input_characters);
break;
}
else{
RFID_input_characters[a]='a'; // this example in my code it get value like this
a++;
}
}
k++;
}
Don't forget to free the memory allocated using malloc or you could end up using all your memory resources.

return a space-less string from a function

I have a fucntion which in it I want to return a string (i.e array of chars) with no spaces at all. This is my code, which in my understanding is not right:
char *ignoreSpace( char helpArr[], int length ){
int i = 0; int j = 0;
char withoutSpace[length];
while ( i < length ){
/*if not a space*/
if ( isspace( helpArr[i] ) == FALSE )
withoutSpace[j] = helpArr[i];
i++;
}
return *withoutSpace;
}
My intention in the line:
return *withoutSpace;
Is to return the content of the array withoutSpace so I could parse a string with no spaces at all.
Can you please tell me how can I make it any better?
Your current solution will lose the result of withoutSpace when the function returns as it is only defined in that function's scope.
A better pattern would be to accept a third argument to the function which is a pointer to a char[] to write the result into - in much the same way the standard functions do, (eg strcpy.
char* ignoreSpace(char* src, char* dst, int length) {
// copy from src to dst, ignoring spaces
// ...
// ...
return dst;
}
Try this (assuming null terminated string)
void ignoreSpace(char *str) {
int write_pos = 0, read_pos = 0;
for (; str[read_pos]; ++read_pos) {
if (!isspace(str[read_pos]) {
str[write_pos++] = str[read_pos];
}
}
str[write_pos] = 0;
}
You cannot return a pointer to a local variable from a function, because as soon as you leave the function all local variables are detroyed and no longer valid.
You must either
Allocate space with malloc in your function and return a pointer
to that allocated memory
not return a pointer from the function butmodify directly the
original string.
First solution :
char *ignoreSpace(char helpArr[], int length)
{
int i=0; int j=0;
char *withoutSpace = malloc(length) ;
while(i <= length)
{
/*if not a space*/
if(isspace(helpArr[i]) == FALSE)
withoutSpace[j++] = helpArr[i];
i++;
}
return withoutSpace;
}
Second solution:
char *ignoreSpace(char helpArr[], int length)
{
int i=0; int j=0;
while(i <= length)
{
/*if not a space*/
if(isspace(helpArr[i]) == FALSE)
helpArr[j++] = helpArr[i];
i++;
}
return helpArr;
}
There are some other small correction in my code. Finding out which ones is left as an exercise to the reader.
You don't increment j, ever. In the case that the current character of the source string is not a space, you probably would like to store it in your output string and then also increment the j by one; so that you'd store the next possible character into the next slot instead of overwriting the 0th one again and again.
So change this:
...
withoutSpace[j] = helpArr[i];
...
into this:
...
withoutSpace[j++] = helpArr[i];
...
And then also append your withoutSpace with a 0 or '\0' (they are the same), so that any string processing function may know its end. Also return the pointer, since you should do that, not the *withoutSpace or withoutSpace[0] (they are the same):
char *ignoreSpace( char helpArr[], int length ){
int i = 0; int j = 0;
char * withoutSpace = malloc( length * sizeof * withoutSpace ); // <-- changed this
while ( i < length ){
/*if not a space*/
if ( isspace( helpArr[i] ) == FALSE )
withoutSpace[j++] = helpArr[i]; // <-- replaced j with j++
i++;
}
withoutSpace[j] = 0; // <-- added this
return withoutSpace;
}
And then you should be good to go, assuming that you can have variable-length arrays.
Edit: Well, variable-length arrays or not, you better just use dynamic memory allocation by using malloc or calloc or something, because else, as per comments, you'd be returning a local pointer variable. Of course, this requires you to manually free the allocated memory in the end.

Return the contiguous block in c

I create an array (char *charheap;) of length 32 bytes in the heap, and initialize all the elements to be \0. Here is my main function:
int main(void) {
char *str1 = alloc_and_print(5, "hello");
char *str2 = alloc_and_print(5, "brian");
}
char *alloc_and_print(int s, const char *cpy) {
char *ncb = char_alloc(s);// allocate the next contiguous block
if (ret == NULL) {
printf("Failed\n");
} else {
strcpy(ncb, cpy);
arr_print();// print the array
}
return ncb;
}
Here is what I implement:
/char_alloc(s): find the FIRST contiguous block of s+1 NULL ('\0')
characters in charheap that does not contain the NULL terminator
of some previously allocated string./
char *char_alloc(int s) {
int len = strlen(charheap);
for (int i = 0; i < len; i++) {
if (charheap[0] == '\0') {
char a = charheap[0];
return &a;
} else if (charheap[i] == '\0') {
char b = charheap[i+1];
return &b;
}
}
return NULL;
}
Expected Output: (\ means \0)
hello\\\\\\\\\\\\\\\\\\\\\\\\\\\
hello\brian\\\\\\\\\\\\\\\\\\\\\
This solution is completely wrong and I just print out two failed. :(
Actually, the char_alloc should return a pointer to the start of contiguous block but I don't know how to implement it properly. Can someone give me a hint or clue ?
Your function is returning a pointer to a local variable, therefore the caller receives a pointer to invalid memory. Just return the pointer into the charheap, which is what you want.
return &charheap[0]; /* was return &a; which is wrong */
return &charheap[i+1]; /* was return &b; which is wrong */
Your for loop uses i < len for the terminating condition, but, since charheap is \0 filled, strlen() will return a size of 0. You want to iterate through the whole charheap, so just use the size of that array (32 in this case).
int len = 32; /* or sizeof(charheap) if it is declared as an array */
The above two fixes should be enough to get your program to behave as you expect (see demonstration).
However, you do not place a check to make sure there is enough room in your heap to accept the allocation check. Your allocation should fail if the distance between the start of the available memory and the end of the charheap is less than or equal to the desired size. You can enforce this easily enough by setting the len to be the last point you are willing to check before you know there will not be enough space.
int len = 32 - s;
Finally, when you try to allocate a third string, your loop will skip over the first allocated string, but will overwrite the second allocated string. Your loop logic needs to change to skip over each allocated string. You first check if the current location in your charheap is free or not. If it is not, you advance your position by the length of the string, plus one more to skip over the '\0' terminator for the string. If the current location is free, you return it. If you are not able to find a free location, you return NULL.
char *char_alloc(int s) {
int i = 0;
int len = 32 - s;
while (i < len) {
if (charheap[i] == '\0') return &charheap[i];
i += strlen(charheap+i) + 1;
}
return NULL;
}

Resources