Return the contiguous block in c - 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;
}

Related

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.

Buffer Overflow - Char Array not removed from stack after exiting function

I am trying to concatenate a few strings to a buffer. However, if I call the function repeatedly, the size of my buffer will keep growing.
void print_message(char *str) {
char message[8196];
sender *m = senderlist;
while(m) {
/* note: stricmp() is a case-insensitive version of strcmp() */
if(stricmp(m->sender,str)==0) {
strcat(message,m->sender);
strcat(message,", ");
}
m = m->next;
}
printf("strlen: %i",strlen(message));
printf("Message: %s\n",message);
return;
}
The size of message will continuously grow until the length will be 3799.
Example:
1st. call: strlen = 211
2nd call: strlen = 514
3rd call: strlen = 844
...
nth call: strlen = 3799
nth +1 call: strlen = 3799
nth +2 call: strlen = 3799
My understanding was, that statically allocated variables like char[] will automatically be freed upon exiting the function, and I'm not dynamically allocating anything on the heap.
And why will suddenly stop growing at 3799 bytes? Thanks for any pointers.
Add one more statement after the buffer definition
char message[8196];
message[0] = '\0';
Or initialize the buffer when it is defined
char message[8196] = { '\0' };
or
char message[8196] = "";
that is fully equivalent to the previous initialization.
The problem with your code is that the compiler does not initialize the buffer if you wiil not specify initialization explicitly. So array message contains some garbage but function strcat at first searches the terminating zero in the buffer that to append a new string. So your program has undefined behaviour.
What you are seeing is the growing of the senderlist or likely garbage in message. Fortunately not exceeding 8196.
The message array must start with the empty string. At the moment doing a strcat adds to garbage.
char message[8196];
sender *m = senderlist;
int len = 0;
*message = '\0';
while(m) {
/* note: stricmp() is a case-insensitive version of strcmp() */
if(stricmp(m->sender,str)==0) {
int sender_len = strlen(m->sender);
if (len + sender_len + 2 + 1 < sizeof(message)) {
strcpy(message + len, m->sender);
len += sender_len;
strcpy(message + len, ", ");
len += 2;
} else {
// Maybe appending "..." instead (+ 3 + 1 < ...).
break;
}
}
m = m->next;
}
printf("strlen: %i",strlen(message));
printf("Message: %s\n",message);
"Deallocation" is not the same as wiping the data; in fact, C generally leaves the data unerased for performance reasons.

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.

Having trouble reading strings from stdin

I need to create program that takes input from stdin in this format:
abcde //number of characters in word = number of words => square shape
fghij
klmno
pqrst
uvwxy
// \n separates first half from second
word1word //any amount of characters, any amount of words
word
word2
sdf
// \n to end input
My code works, but only about 50% of the time. I have couple of example inputs, that I use for testing, but for some of them my readwords function fails.
Here is my function, that reads words. Since I have no idea how many words or how long they are going to be, I use dynamic arrays and getchar() function.
void readWords(char **p,int *n,int w) /* before calling: n = 50; w = 20; p = 50x20 char array */
{
int i = 0,j = 0,x;
char tmp,prevtmp;
while (1)
{
prevtmp = tmp;
tmp = getchar();
if ((prevtmp == '\n' && tmp == '\n') || feof(stdin))
break; /* no more words to read */
if (tmp == '\n') /* end of word */
{
p[i][j] = '\0'; /* add \0 to create string format */
i++;
j = 0;
if (i == *n) /* if there is more words than there is space for them, double the size */
if (realloc(p,*n*2) != NULL)
*n*=2;
continue;
}
p[i][j] = tmp;
j++;
if (j == w) /* if width of word is larger than allocated space, double it */
{
for (x = 0; x < *n;x++);
if(realloc (p[x],w*2) != NULL);
w=w*2;
}
}
*n = i;
}
This is example of input for which this works (note:this function only reads second half after line with only \n):
dsjellivhsanxrr
riemjudhgdffcfz
<skipping>
atnaltapsllcelo
ryedunuhyxhedfy
atlanta
saltlakecity
<skipping 15 words>
hartford
jeffersoncity
And this is input that my function doesn't read properly:
<skipping>
...oywdz.ykasm.pkfwb.zazqy...
....ynu...ftk...zlb...akn....
missouri
delaware
<skipping>
minnesota
southdakota
What my function reads from this input:
e
yoming
xas
florida
lvania
ana
ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ
There is no difference between those two inputs (except different words and different amount and length of words), the first half gets read properly no matter what, but only the second half bugs out. How do I fix this?
P.S. sorry for long post, in case you want to see full input without skipped bytes, here is pastebin: http://pastebin.com/hBGn2tej
realloc() returns the address of the newly allocated memory, it does not update the argument passed into it. So this (and the other use of realloc()) is incorrect:
if (realloc(p,*n*2) != NULL)
and will results in the code accessing memory incorrectly, causing undefined behaviour. Store the result of realloc() to a temporary variable and check for non-NULL before updating p. The argument to realloc() also indicates the number of bytes, not the number of elements so the size argument calculation is incorrect as p is an array of char* so it should be realloc(p, sizeof(char*) * (*n * 2));. However, the change to p would not be visible to the caller. Also note that the only legal arguments to realloc() are pointers obtained from a previous call to malloc(), realloc() or calloc(). The comment p = 50x20 char array in the code suggests this is not the case.
Here is a small example that allocates an array of char* which should be helpful:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
void f(char*** p)
{
/* Allocate space for two 'char*' elements.
Add a NULL pointer element as sentinel value
so caller knows where to find end of list. */
*p = malloc(sizeof(**p) * 3);
/* Allocate space for the two strings
and populate. */
(*p)[0] = malloc(10);
(*p)[1] = malloc(10);
strcpy((*p)[0], "hello");
strcpy((*p)[1], "world");
(*p)[2] = NULL;
/* Add a third string. */
char** tmp = realloc(*p, sizeof(**p) * 4);
if (tmp)
{
*p = tmp;
(*p)[2] = malloc(10);
strcpy((*p)[2], "again");
(*p)[3] = NULL;
}
}
int main()
{
char** word_list = 0;
f(&word_list);
if (word_list)
{
for (int i = 0; word_list[i]; i++)
{
printf("%s\n", word_list[i]);
free(word_list[i]);
}
}
free(word_list);
return 0;
}
Additionally:
prevtmp has an unknown value upon its first use.
getchar() actually returns an int and not a char.

Appending a char to a char* in C?

I'm trying to make a quick function that gets a word/argument in a string by its number:
char* arg(char* S, int Num) {
char* Return = "";
int Spaces = 0;
int i = 0;
for (i; i<strlen(S); i++) {
if (S[i] == ' ') {
Spaces++;
}
else if (Spaces == Num) {
//Want to append S[i] to Return here.
}
else if (Spaces > Num) {
return Return;
}
}
printf("%s-\n", Return);
return Return;
}
I can't find a way to put the characters into Return. I have found lots of posts that suggest strcat() or tricks with pointers, but every one segfaults. I've also seen people saying that malloc() should be used, but I'm not sure of how I'd used it in a loop like this.
I will not claim to understand what it is that you're trying to do, but your code has two problems:
You're assigning a read-only string to Return; that string will be in your
binary's data section, which is read-only, and if you try to modify it you will get a segfault.
Your for loop is O(n^2), because strlen() is O(n)
There are several different ways of solving the "how to return a string" problem. You can, for example:
Use malloc() / calloc() to allocate a new string, as has been suggested
Use asprintf(), which is similar but gives you formatting if you need
Pass an output string (and its maximum size) as a parameter to the function
The first two require the calling function to free() the returned value. The third allows the caller to decide how to allocate the string (stack or heap), but requires some sort of contract about the minumum size needed for the output string.
In your code, when the function returns, then Return will be gone as well, so this behavior is undefined. It might work, but you should never rely on it.
Typically in C, you'd want to pass the "return" string as an argument instead, so that you don't have to free it all the time. Both require a local variable on the caller's side, but malloc'ing it will require an additional call to free the allocated memory and is also more expensive than simply passing a pointer to a local variable.
As for appending to the string, just use array notation (keep track of the current char/index) and don't forget to add a null character at the end.
Example:
int arg(char* ptr, char* S, int Num) {
int i, Spaces = 0, cur = 0;
for (i=0; i<strlen(S); i++) {
if (S[i] == ' ') {
Spaces++;
}
else if (Spaces == Num) {
ptr[cur++] = S[i]; // append char
}
else if (Spaces > Num) {
ptr[cur] = '\0'; // insert null char
return 0; // returns 0 on success
}
}
ptr[cur] = '\0'; // insert null char
return (cur > 0 ? 0 : -1); // returns 0 on success, -1 on error
}
Then invoke it like so:
char myArg[50];
if (arg(myArg, "this is an example", 3) == 0) {
printf("arg is %s\n", myArg);
} else {
// arg not found
}
Just make sure you don't overflow ptr (e.g.: by passing its size and adding a check in the function).
There are numbers of ways you could improve your code, but let's just start by making it meet the standard. ;-)
P.S.: Don't malloc unless you need to. And in that case you don't.
char * Return; //by the way horrible name for a variable.
Return = malloc(<some size>);
......
......
*(Return + index) = *(S+i);
You can't assign anything to a string literal such as "".
You may want to use your loop to determine the offsets of the start of the word in your string that you're looking for. Then find its length by continuing through the string until you encounter the end or another space. Then, you can malloc an array of chars with size equal to the size of the offset+1 (For the null terminator.) Finally, copy the substring into this new buffer and return it.
Also, as mentioned above, you may want to remove the strlen call from the loop - most compilers will optimize it out but it is indeed a linear operation for every character in the array, making the loop O(n**2).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *arg(const char *S, unsigned int Num) {
char *Return = "";
const char *top, *p;
unsigned int Spaces = 0;
int i = 0;
Return=(char*)malloc(sizeof(char));
*Return = '\0';
if(S == NULL || *S=='\0') return Return;
p=top=S;
while(Spaces != Num){
if(NULL!=(p=strchr(top, ' '))){
++Spaces;
top=++p;
} else {
break;
}
}
if(Spaces < Num) return Return;
if(NULL!=(p=strchr(top, ' '))){
int len = p - top;
Return=(char*)realloc(Return, sizeof(char)*(len+1));
strncpy(Return, top, len);
Return[len]='\0';
} else {
free(Return);
Return=strdup(top);
}
//printf("%s-\n", Return);
return Return;
}
int main(){
char *word;
word=arg("make a quick function", 2);//quick
printf("\"%s\"\n", word);
free(word);
return 0;
}

Resources