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

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 }

Related

Clear char array in C without any standard library

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

Parsing input in C and automatically allocating space for characters

I started learning programming with Python a couple months ago, and I decided to learn C because I am interested in lower level languages that let me interact closer to the computers hardware. I'm trying to get some input from a user in C, and I am writing my own little input parser. I'm having some trouble here:
#include <stdio.h>
char prompt()
{
char resp[]; // Create a variable for the users response
for (int i = 0; i < 1000; ++i)
{
char letter = getchar(); // Get character
if (letter == '\n') // If the user hits enter break the loop
{
break;
}
else // Otherwise append the character to the response array
{
resp[i] = letter;
}
}
return resp[]; // Return the response array
}
int main() {
return 0;
}
I'm receiving errors with this code. The errors specifically say:
error: definition of variable with array type needs an explicit size or an initializer
char resp[];
I take it that I must define a set value for an array or assign it something right away. I don't understand how I can grow the character array as the user types the input if arrays in C must have a defined value. I am thinking that using pointers or memory management might work, but I haven't learned a lot about these things yet so if you do have a solution involving pointers, it would be of great help to me if you could briefly explain what the code is doing. In the meantime I will try and find a solution.
You probably want to allocate memory, here's how you would do it>
#include <stdio.h>
#include <stdlib.h>
char* prompt()
{
char* resp; // Create a variable for the users response
int i;
resp = malloc(sizeof(char));//allocate space for one char
for (i = 0; i < 1000; ++i)
{
resp = realloc(resp, sizeof(char)*(i+1));//allocate space for one more char
char letter = getchar(); // Get character
if (letter == '\n') // If the user hits enter break the loop
{
break;
}
else // Otherwise append the character to the response array
{
resp[i] = letter;
}
}
return resp; // Return the response array
}
int main() {
char* answer = prompt();
printf("answer is %s\n", answer);
return 0;
}
And this will work but it is highly NOT recommended to do it like this because you never free() your allocated memory.
EDIT>
How to do it? You might want to avoid creating a new function and do it all in your main(), in that case, when you no longer need your variable simply call free(resp);
There isn't a dynamic runtime in C. You need to explicitly allocate resources because your code essentially translates directly into machine instructions.
Here's an example.
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
int prompt(const size_t bufsize, char response[bufsize]) {
if (fgets(response, bufsize, stdin) == NULL)
return -1; // error
return 0;
}
int main() {
const size_t bufsize = 1024;
char* response = malloc(bufsize); // Create a variable for the users response
if (!response)
return -1; // error
if (prompt(bufsize, response))
return -1; // error
printf("%s", response);
free(response);
return 0;
}

C -- Deallocating memory from a calling function

My main question is, is my scheme just plain bad practice? Can it be done? Should it be done?
I'm writing a little dinky key-value pair "dictionary" structure just to familiarize my self with C. One of the functions I've written is intended to return an array of strings of all the values associated with a provided key. The function definition:
char** get_values(const struct dict* d, const char* key)
{
// if key does not exist
if(contains_key(d, key) == 0)
{
return NULL;
}
// count the number of times the key occurs in the dict
int count = count_keys(d, key);
// make a return value
char** key_values = alloc_list(count);// definition included below
int curr_count = 0;
// fill return array
for(int pos = 0; pos < DSIZE; pos++)
{// if current key in dict matches requested key...
if(strcmp(d->keys[pos], key) == 0)
{
// add current value to return array
strcpy(key_values[curr_count], d->values[pos]);
curr_count++;
}
}
return key_values;
}
This function allocates the memory for the string array:
static char** alloc_list(int count)
{
// Allocate list of strings
char** slist = (char**)malloc(sizeof(char*) * count);
// if allocation was great success...
if(slist)
{
// ... allocate memory for each string
for(int pos = 0; pos < DSIZE; pos++)
{
slist[pos] = (char*)malloc(DSIZE * sizeof *slist[pos]);
}
}
return slist;
}
Then in main():
add(&dictionary, "a", "a");
add(&dictionary, "a", "aa");
add(&dictionary, "a", "aaa");
char** a_val = get_values(&dictionary, "a"); // TODO: how do I free this!
assert(strcmp(a_val[0], "a") == 0);
assert(strcmp(a_val[1], "aa") == 0);
assert(strcmp(a_val[2], "aaa") == 0); // all these assertions pass
free(*a_val); // with * omitted, execution aborts, with * included, no warnings
// from gcc, yes I am stabbing in the dark here.
a_val = NULL;
I don't believe the last two lines are doing what I hope they are doing, when I print the values of a_val[0-2] in gdb, they are still there.
I realize I could fix this problem by allocating a string array in main(), and then change get_values() to accept the array and then let get_values() do its business, and then free() the allocated array of strings when I am done with it. But before I go ahead and do that, I was just wanted to see if and how one goes about deallocating memory from a calling function. I've read a little bit about it, and all that was said was "it is the programmers responsibility to deallocate memory in the calling function", but the book did not say how for a situation such as this.
Thanks in advance for any help!
In order to properly deallocate a_val you will need first a for-loop to free/deallocate the char arrays allocated previously and then free the double pointer (i.e., a_val). Otherwise you will create a memory leak since the memory pointed by elements/pointers of a_val will be unreferenced/orphaned:
char** a_val = get_values(&dictionary, "a");
...
for(int pos = 0; pos < DSIZE; pos++) {
free(a_val[pos]);
}
free(a_val);
Stating free(*a_val); is equivalent as stating free(a_val[0]). Thus only the first string of a_val is going to be deallocated.

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

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