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;
}
Related
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)
I am trying to read multiple lines from stdin, in which even lines are strings and odd lines are numbers separated by spaces. I'm trying to read the numbers as integers and the strings as... strings. It's part of a school project but it's not the entire thing; I managed the rest of the stuff but I can't manage to actually GET the strings and ints from stdin.
I add every name to experiments when i is even (I try to use it as a line number)
I tried using malloc to append a string n and store it as an int in a a 2d array data when I encounter a space, using int a to navigate through the line.
And then the printing part is just to try to show it works and.. it doesn't. I'm not busting any array's length and I felt like I watched out for malloc but I spent more than 15 hours on this part and nothing good is coming out of it. I wondered if someone could give me a hint.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char **argv) {
char *experiments[100];
int data[10][20];
char name[101];
int i=0;
int j=0;
char *n;
char *g;
fgets(name, 100, stdin);
while ((strstr(name,"*** END ***")!=0)&&(name!=NULL)){
if((i%2)==0){
experiments[i/2]=name;
name[0]='\0';
}
else {
int a = 0;
while ((name[a]!='\n')&&(a<100)){
if (name[a]!=' '){
size_t len = strlen(n);
g = malloc(len + 1 + 1);
strcpy(g,n);
g[strlen(n)-2] = name[a];
g[strlen(n)-1] = '\0';
n[0]='\0';
*n = *g;
free( g );
a+=1;
}
else {
data[j][i]=*n;
j+=1;
n[0]='\0';
a+=1;
}
}
}
i+=1;
fgets(name,100, stdin );
}
int k=0;
for(k=0;k<=i;k+=1){
printf("printing\n");
printf("%s\n", experiments[k]);
if (experiments[k][0]=='\0') {
printf("oh shoot!");
}
}
return(0);}
You seem to have fundamental confusions regarding:
Do you know the saying "Give me six hours to chop down a tree and I will spend the first four sharpening the axe"? There are many problems here, and they're probably caused by a blunt axe. Whatever book you're using to learn C is failing to teach you. I recommend K&R 2E. Don't forget to do the exercises.
Yuck! Neither for nor return are functions! Please, for the love of life, if you want other people to read your code, make it presentable for them! It would help if your code were consistently indented, too.
Arrays (e.g. it's impossible for name!=NULL to evaluate false, so that expression is pointless), pointers and the implicit conversion from array to pointer that occurs in experiments[i/2]=name;. To clarify, every time you assign like that, the different elements will point to the same place, and the values stored within that place will be overwritten when you next call fgets.
malloc; you've used it in the wrong place, and the way you used it reinvents automatic storage duration (that is, all of your variables). You might as well just not use it at all.
fgets; its mode of failure leads to horrible crashes in your program.
Strings; see above.
Start by reading K&R 2E and doing the exercises as I mentioned earlier... Once you've completed that book I reckon you'll have a fine chance at filling in the blanks for this program:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char **argv) {
char *experiments[100] = { 0 };
int data[10][20] = { 0 };
char name[101] = { 0 };
size_t i = 0, j = 0;
fgets(name, sizeof name, stdin);
while (strstr(name,"*** END ***") != 0){
if(i%2 == 0){
experiments[i / 2] = malloc(strlen(name) + 1);
if (experiments[i / 2] == NULL) {
puts("OOPS! malloc failure!");
return 0;
}
strcpy(experiments[i / 2], name);
}
else {
/* XXX: I have no idea what any of this was meant to do *
* ... but it was all HORRIBLY WRONG so I removed it. *
* Try again once you've read K&R 2E and done the *
* exercises... */
}
i++;
fgets(name, sizeof name, stdin);
}
for (size_t k = 0; k < i / 2; k++){
puts("Printing...");
puts(experiments[k]);
if (experiments[k][0] == '\0') {
puts("oh shoot!");
}
free(experiments[k]);
}
return 0;
}
What I want to do is: The user inputs a string with commas; for example: 123456,44,55,,66
and I want to separate it and store in a new array without the commas; for example:
m[0][]={123456}, m[1][]={44}, m[2][]={55}, m[3][]={}, m[4][]={66}
123456 is the student ID number, 44 is the mark for 1st module, 55 is the mark for 2nd module, NULL means that the student didn't take that 3rd module, and 66 is the mark for 4th module.
How can I exactly do that? What I know is that by detecting double commas, it means the student didn't take that 3rd module.
Here is what I have written so far:
#include <stdio.h>
#include <string.h>
#include <ctype.h>
void copystring(char m[],char temp[]);
main()
{
char temp[10000];
char m[10000][10000];
gets(temp);
copystring(m,temp);
printf("%s\n",m);
return 0;
}
void copystring(char m[],char temp[])
{
int i;
int j;
for (j=0;j<(strlen(temp));j++)
{
for (i=0;i<(strlen(temp));i++)
{
if (temp[i]!=*(",")&&temp[i]!=*(" "))
{
m[j][i]=temp[i];
}
}
}
}
I have edited my code so that you can understand how to declare a function prototype containing 2D array as parameter. Also use fgets() instead of gets(). The function returns the number of marks read , i.e. an integer. I think this might help. Run the code and look on the man pages or google to understand fgets() better.
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define SIZE 1000
int stringcopy(char m[][SIZE],char temp[]);
main()
{
char temp[SIZE],m[100][SIZE];
fgets(temp,SIZE,stdin);
int num=stringcopy(m,temp);
int i;
for(i=0;i<num;++i)
printf("%s\n",m[i]);
return 0;
}
int stringcopy(char m[][SIZE],char temp[]) {
int len=strlen(temp);
int i,j=0,k=0;
for(i=0;i<len;++i) {
if(temp[i]!=',')
m[j][k++]=temp[i];
else {
m[j][k]='\0';
++j;
k=0;
}
}
m[j][k-1]='\0';
return j+1;
}
You have to solve your problem in different steps,
The first step is to check how many tokens you have in your input string to be able to allocate enough space to store an array of tokens.
Then you should extract the tokens of the input strings in your tokens string array. To extract the tokens from your input string, you can use the strtok function from <string.h>.
Finally you can use your tokens however you want, like converting them to long in your case.
EDIT: given the requirements, here is a small implementation of what you could do. I don't check the returns of the malloc, you maybe should do it.
int main(int argc, char** argv) {
int i;
char* input_string = /* your input string for somewhere */;
char** tokens;
int tokens_count = 0;
char* input_ptr = input_string;
char* tmp_token;
size_t tmp_token_length;
// count the occurences of your separtor to have the number of elements
for(; input_ptr[tokens_count]; input_ptr[tokens_count] == ',' ? tokens_count++ : input_ptr++);
if(tokens_count == 0) {
// no tokens found, what do you want to do here ?
}
else {
// build our tokens array
tokens = malloc(sizeof(*tokens) * tokens_count);
i = 0;
tmp_token = strtok(input_string, ',');
while(tmp_token != NULL) {
tmp_token_length = strlen(tmp_token);
if(tmp_token_length != 0) {
tokens[i] = malloc(tmp_token_length);
strcpy(tokens[i], tmp_token);
}
else {
tokens[i] = NULL;
}
i++;
tmp_token = strtok(input_string, ',');
}
// populate your array of arrays of integers
long** m = malloc(sizeof(long*) * tokens_count);
for(i=0; i<tokens_count; i++) {
char* tmp_token = tokens[i];
if(tmp_token == NULL) {
m[i] = NULL;
}
else {
m[i] = malloc(sizeof(long));
m[i][0] = strtol(tmp_token, NULL, 10);
}
}
}
}
However, you should probably change your data structure by using structures instead of a massive array.
Try to use scanf for getting input, your copystring function seems fine; but if there is a problem then debug it to see what the problem is.
for (j=0;j<(strlen(temp));j++)
{
for (i=0;i<(strlen(temp));i++)
{
if (temp[i]!=(',')&&temp[i]!=(' '))
{
m[j][i]=temp[i];
}
else{
m[j][i]='\0';break;// must end a string with null character.
}
}
}
and for priting use
printf("%s",m[0]);// make a loop for it
You can read entire string using fgets, or scanf and then use strtok(string, ",") to get substrings between commas.
To detect if student has missed some entry, there are many ways, few of them are:
1) Check no. of sub-strings you get before strtok returns NULL.
2) You can search for substring ,, using strstr in the input string.
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;
}
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.