Clear char array in C without any standard library - c

I'm working on a class project that would require me to make unique strings and I want to concatenate a number to a string. However I do NOT have access to C Standard Library (memset, malloc, etc.). I made this which works:
char* concat(char* name, int num) {
int i, j;
char newName[50], stack[5];
for(i=0; name[i]!='\0'; ++i) {
newName[i] = name[i];
}
for (j=0; num>=1 || num==0; j++) {
stack[j] = (num % 10) + '0';
num = num / 10;
if (num==0) break;
}
while (j>=0) {
newName[i++] = stack[j--];
}
name[0] = '\0';
return newName;
}
But then as I tested it with multiple strings, I realized that newName was being reused over and over. For ex.
This test file outputs the following:
int main() {
char* rebecca = concat("rebecca", 1);
char* bill = concat("bill", 2);
Write(rebecca); /* bill2ca1 */
Write(bill); /* bill2ca1 */
}
It successfully appends the 1 to rebecca, but then when I call concat on bill, it overwrites the first 5 letter but keeps the same chars from before in newName.
QUESTION: How to clear a char array so the next time it's called it will be set to empty, or dynamically allocate it (without using C Standard Library)?

Without using malloc, you can simply put the memory on the stack of the calling function, to keep in the scope where it is needed. It's easier to add the buffer pointer to the argument list like so:
char* concat(char *newName, char* name, int num) {
int i, j;
char stack[5];
:
:
}
int main() {
char rebecca[50];
char bill[50];
concat(rebecca, "rebecca", 1);
concat(bill, "bill", 2);
write(rebecca);
write(bill);
}
Generally speaking, assign memory where it will be used. Embedded programming (which might need to run for months without a reboot) avoids malloc like the plague, just because of the risk of memory leaks. You then need to assign extra space since you may not know the size at compile time, and then ideally check for running past the end of the buffer. Here we know the string sizes and 50 chars is more than enough.
Edit:
The other issue is that you're not null terminating. The print will go until it hits 0x00. Your line
name[0] = '\0';
should be
newName[i] = '\0';

You've got a major issue that you're overlooking. In your function, newName is a local variable (array) and you're returning it from the function. This invokes undefined behavior. The beauty of UB is that, sometime it appears to work as expected.
You need to take a pointer and allocate memory dynamically instead, if you want to return it from your concat() function. Also, in the main(), after using it, you need to free() it.
A better alternative, maybe, if you choose to do so, is
Define the array in the caller.
Pass the array to the function.
Inside the function, memset() the array before you perform any other operation.
One thing to remember, this way, every call to the function will clean the previous result.
EDIT:
If you cannot use memset(), in the main, you can use a for loop like
for (i = 0; i < sizeof(arr)/sizeof(arr[0]); i++)
arr[i] = 0;
to clear the array before passing it on next time.

You're returning the address of a local variable. Since the variable goes out of scope when the function returns, this invokes undefined behavior.
You function should dynamically allocate memory for the result of the concatenation, then return that buffer. You'll need to be sure to free that buffer later to prevent a memory leak:
char* concat(char* name, int num) {
int i, j;
char *newName, stack[5];
// allocate enough space for the existing string and digits for a 64-bit number
newName = malloc(strlen(name) + 30);
for(i=0; name[i]!='\0'; ++i) {
newName[i] = name[i];
}
for (j=0; num>=1 || num==0; j++) {
stack[j] = (num % 10) + '0';
num = num / 10;
if (num==0) break;
}
while (j>=0) {
newName[i++] = stack[j--];
}
newName[i] = '\0';
return newName;
}
int main() {
char* rebecca = concat("rebecca", 1);
char* bill = concat("bill", 2);
Write(rebecca);
Write(bill);
free(rebecca);
free(bill);
}

Related

Dynamically increasing C string's size

I'm currently creating a program that captures user's keypresses and stores them in a string. I wanted the string that stores the keypresses to be dynamic, but i came across a problem.
My current code looks something like this:
#include <stdio.h>
#include <stdlib.h>
typedef struct Foo {
const char* str;
int size;
} Foo;
int main(void)
{
int i;
Foo foo;
foo.str = NULL;
foo.size = 0;
for (;;) {
for (i = 8; i <= 190; i++) {
if (GetAsyncKeyState(i) == -32767) { // if key is pressed
foo.str = (char*)realloc(foo.str, (foo.size + 1) * sizeof(char)); // Access violation reading location xxx
sprintf(foo.str, "%s%c", foo.str, (char)i);
foo.size++;
}
}
}
return 0;
}
Any help would be appreciated, as I don't have any ideas anymore. :(
Should I maybe also allocate the Foo object dynamically?
First, in order to handle things nicely, you need to define
typedef struct Foo {
char* str;
int size
} Foo;
Otherwise, Foo is really annoying to mutate properly - you invoke undefined behaviour by modifying foo->str after the realloc call in any way.
The seg fault is actually caused by sprintf(foo.str, "%s%c", foo.str, (char)i);, not the call to realloc. foo.str is, in general, not null-terminated.
In fact, you're duplicating work by calling sprintf at all. realloc already copies all the characters previously in f.str, so all you have to do is add a single character via
f.str[size] = (char) i;
Edit to respond to comment:
If we wanted to append to strings (or rather, two Foos) together, we could do that as follows:
void appendFoos(Foo* const first, const Foo* const second) {
first->str = realloc(first->str, (first->size + second->size) * (sizeof(char)));
memcpy(first->str + first->size, second->str, second->size);
first->size += second->size;
}
The appendFoos function modifies first by appending second onto it.
Throughout this code, we leave Foos as non-null terminated. However, to convert to a string, you must add a final null character after reading all other characters.
const char *str - you declare the pointer to const char. You cant write to the referenced object as it invokes UB
You use sprintf just to add the char. It makes no sense.
You do not need a pointer in the structure.
You need to set compiler options to compile **as C language" not C++
I would do it a bit different way:
typedef struct Foo {
size_t size;
char str[1];
} Foo;
Foo *addCharToFoo(Foo *f, char ch);
{
if(f)
{
f = realloc(f, sizeof(*f) + f -> size);
}
else
{
f = realloc(f, sizeof(*f) + 1);
if(f) f-> size = 0
}
if(f) //check if realloc did not fail
{
f -> str[f -> size++] = ch;
f -> str[f -> size] = 0;
}
return f;
}
and in the main
int main(void)
{
int i;
Foo *foo = NULL, *tmp;
for (;;)
{
for (i = 8; i <= 190; i++)
{
if (GetAsyncKeyState(i) == -32767) { // if key is pressed
if((tmp = addCharToFoo(f, i))
{
foo = tmp;
}
else
/* do something - realloc failed*/
}
}
}
return 0;
}
sprintf(foo.str, "%s%c", foo.str, (char)i); is ill-formed: the first argument cannot be const char *. You should see a compiler error message.
After fixing this (make str be char *), then the behaviour is undefined because the source memory read by the %s overlaps with the destination.
Instead you would need to use some other method to append the character that doesn't involve overlapping read and writes (e.g. use the [ ] operator to write the character and don't forget about null termination).

After creating an array through dynamic allocation, a problem occurs when changing the memory size through realloc in C

I am practicing C language.
I wanted to use dynamic allocation to use only the size of the string I input as memory and check whether the input string was properly saved.
So, I wrote the following code using malloc and realloc functions.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void str_copy(char* str_array_f) {
void* tmp;
char buf;
unsigned char arr_size = 1;
unsigned char arr_cur = 0;
while ((buf = getchar())) {
if (buf == '\n') {
break;
}
str_array_f[arr_cur++] = (char)buf;
tmp = realloc(str_array_f, ((arr_size++) * sizeof(char)) + sizeof(char));
if (tmp != 0) {
str_array_f = tmp;
}
else {
printf("memory leak error occur! \n");
break;
}
}
str_array_f[arr_size - 1] = 0x00;
}
void main() {
int contiune = 1;
while (contiune) {
char* str_array = malloc(sizeof(char) + sizeof(char));
printf("Please type something : ");
str_copy(str_array);
printf("'str_array' have this : %s \n", str_array);
printf("-------------------------------------------------\n");
if (str_array[0] == '1') {
contiune = 0;
}
free(str_array);
}
}
And, as a result of the performance,
The following problems have occurred.
Strange values sometimes appear from the 5th character of the intermittently printed value
(To reproduce this issue, it is recommended to remove the while loop and try repeatedly)
In the case of repeatedly receiving a value by using the while loop, an error occurs after 4 repetitions.
If the allocated memory of tmp, which is a void type pointer, is released after line 22(e.g., 'free(tmp);'), when executed, no output and an error occurs immediately.
For the above 3 problems, I am not sure what is the cause and how to fix it.
Please let me know if there is a solution.
And, if there is a bad coding method in my code in terms of efficiency or various aspects, I would appreciate it if you let me know.
*Programming execution environment : Visual studio 2019
to explain what you're doing wrong I'm going to use a minimal example here
void change_x(int x) {
x = 2;
}
int main() {
int x = 1;
change_x(x);
printf("%i\n", x); // it'll print 1 not 2
return 0;
}
here the integer x is copied when the function is called and changing it won't really change the x in main. similarly you are doing in your code that str_array_f = tmp; it really won't change the str_array but the copied value. and you're trying to free a pointer that was reallocated before.
the fix for the example above is not to pass the value x instead pass the address of x (which is equivalent to pass by reference in other languages)
void change_x(int* x) {
*x = 2;
}
int main() {
int x = 1;
change_x(&x);
printf("%i\n", x); // it'll print 1 not 2
return 0;
}
and for your code
void str_copy(char** str_array_f) {...} // change the parameter
*str_array_f = tmp; // de reference and use it.
str_copy(&str_array); // call with it's address
And one more thing, don't reallocate more often it's not efficient. instead just just allocate your "array" type with a minimum size and when it's filled reallocate it with the size of 2 times of it (or 1.5 if you like)

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;
}

c while loop treats local variable in loop as global, why?

below is a small C application. It will ask you for a word to input. It stops asking when has attained four unique words. But in the form shown below it won't run properly until you uncomment the relevant lines.
#include <stdio.h>
#include <string.h>
#define WORDS_COUNT 4
int main()
{
char* words[WORDS_COUNT];
int words_added = 0;
while (words_added<WORDS_COUNT)
{
puts ("\n-------enter a word-------");
char response[250];
scanf("%s", response);
int i;
int duplicate_flag = 0;
for (i=0; i < words_added; i++)
{
if (strcmp(words[i], response) == 0)
{
duplicate_flag = 1;
break;
};
};
if (duplicate_flag == 0)
{
//char tmp[250];
//strcpy(tmp, response);
words[words_added] = response; //words[words_added] = tmp;
puts("that's new!");
words_added ++;
} else {
puts("you've said that already...");
};
};
return 0;
};
The major difference as you can see is between words[words_added] = response and words[words_added] = tmp.
Why would the tmp variable work and not the response?
I'm guessing that response will have the exact same address every iteration, and tmp will get a new address every iteration. but why? yet they were both declared in same the while loop???
When you assign words[words_added] = response you're copying the address (not the contents) of response into the array. So in the original form, your code should see the second word and every subsequent word as duplicates. When you use tmp the code compares each new response to the previous tmp that was stored in (every location of) words[], then copies it into tmp if it's not a duplicate.
So I suspect that your code will detect a duplicate that immediately follows the original, but not one that occurs 2 or more words later.
The words array contains 4 pointers, but no memory has been allocated to those pointers.
You need to allocate memory for each element of the words array, and then copy each string into it:
if (duplicate_flag == 0)
{
words[words_added++] = strdup(response); // allocates mem and copies the string
puts("that's new!");
} else {
...
}
Then be sure to free the memory at the end of your program:
for (i = 0; i < words_added; ++i) {
free(words[i]);
}
You're doing it wrong - you're pointing an array of pointers - words[words_added] - at a variable that changes on every iteration - response
You need to allocate storage for words[words_added] on each iteration, before you strcpy: strcpy(words[words_added], response);
P.S. Don't put semi-colons after closing braces }

pass array by reference

I cant figure out where I am messing up. I am passing an array of character pointers. Inside the function I am trying to use strtok to break up a string into smaller pieces to be assigned to the char * array. I can try printing it off in the function and it all shows up correctly. As soon as I try to print it back in main I just get garbage.
#include <stdio.h>
#include <string.h>
#define CMDLEN 100
#define MAXARG 5
void prompt();
int getCommand (char* cmdAndParameters[]);
int main() {
int numArgs = 0;
char* cmdAndParameters [MAXARG];
while (true){
prompt ();
numArgs = getCommand (cmdAndParameters);
}
}
void prompt() {
printf("shell> ");
}
int getCommand(char* cmdAndParameters[]){
int argNum = 0;
bool foundArg = true;
char* delimiters = " \t\n";
char userRequest[CMDLEN];
fgets(userRequest,sizeof(userRequest), stdin);
if ((cmdAndParameters[argNum] = strtok(userRequest, delimiters)) != NULL)
{
argNum++;
for (; argNum < MAXARG && foundArg; argNum++) {
if ((cmdAndParameters[argNum] = strtok(NULL,delimiters))
== NULL)
{
foundArg = false;
}
// merely to test output remove later
else {printf("%s\n", cmdAndParameters[argNum]);}
}
}
return argNum;
}
In this case, your inner array of chars is allocated "automatic", which is to say, on the stack. When you do the strtok, you're assigning a pointer to memory allocated on the stack, and then returning -- which means the memory is no longer allocated.
Move the userRequest array into file scope (ie, outside a block) or make the allocation 'static' and you'll have a better shot.
Update
Well, it's a little more than that, now that I look again.
First of all, you can clean it up considerably if you use a while loop, something like
argNum = 0;
while((cmdAndParameters[argNum++] = strtok(userRequest, delimiters)) != NULL)
; /* all the work is happening in the conditional part of the while */
or even a for loop as
for(argNum = 0;
(cmdAndParameters[argNum] = strtok(userRequest, delimiters)) != NULL);
argNum++)
; /* still all the work is in the for */
and now if argNum > 0 you know you found something.
Second, you need to think about how and when you're allocating memory. Your cmdAndParameters array is allocated when main starts (on the stack, it's "automatic") so it's around as long as your program, you're okay there. But your userRequest array is allocated auto in getCommand; when getCommand returns, the memory is deallocated; the stack pointer moves back over it and you have no guarantees any longer. So when you do the strtok, you're saving pointers into stack, which can lead to no good.
Do you want
for (; argNum < MAXARG && foundArg; argNum++)
or something like
for(argCntr = argNum; argCntr < MAXARG && foundArg; argCntr++)
Hope that helps.

Resources