C scanf - unknown array size - c

I want to read values (float) into an array but I don't know number of values.
My input is this
Enter values: 1.24 4.25 1.87 3.45 .... etc
How can I load this input to an array? I know that input ends when enterring 0 or EOF.
while(0 or EOF){
scanf("%f", &variable[i])
i++;
}
Thank you.

You can dynamically allocate the array and then reallocate the memory for it when the previously allocated buffer is full. Note that the conversion specifier %f in the format string of scanf reads and discards the leading whitespace characters. From the man page of scanf -
scanf returns the number of items successfully matched and assigned
which can be fewer than provided for, or even zero in the event of an
early matching failure. The value EOF is returned if the end of input
is reached before either the first successful conversion or a matching
failure occurs.
This means that scanf will return EOF only when it encounters EOF as the first input when it is called because EOF must be preceded with a newline '\n' else it won't work (depending on the OS). Here's a small program to demonstrate how you can do it.
#include <stdio.h>
#include <stdlib.h>
int main(void) {
size_t len = 4;
float *buf = malloc(len * sizeof *buf);
if(buf == NULL) { // check for NULL
printf("Not enough memory to allocate.\n");
return 1;
}
size_t i = 0;
float *temp; // to save buf in case realloc fails
// read until EOF or matching failure occurs
// signal the end of input(EOF) by pressing Ctrl+D on *nix
// and Ctrl+Z on Windows systems
while(scanf("%f", buf+i) == 1) {
i++;
if(i == len) { // buf is full
temp = buf;
len *= 2;
buf = realloc(buf, len * sizeof *buf); // reallocate buf
if(buf == NULL) {
printf("Not enough memory to reallocate.\n");
buf = temp;
break;
}
}
}
if(i == 0) {
printf("No input read\n");
return 1;
}
// process buf
for(size_t j = 0; j < i; j++) {
printf("%.2f ", buf[j]);
// do stuff with buff[j]
}
free(buf);
buf = NULL;
return 0;
}

I guess your actual concern is the unknown number of floats that user is going to input. You can use pointer to float, do malloc for some predefined size, if your limit has reached while taking an input then do a realloc to increase the memory. You need to take care of previously accepted data while doing a reaccloc.

You need a dynamic allocation of your array since you don't know its size at compile time.
// INITIAL_SIZE can be the average expected size of your array
#define INITIAL_SIZE 4
// size will track the current maximum size of youe array.
size_t size = INITIAL_SIZE;
// dynamic allocation for variable
float* variable = malloc(sizeof(float)*size);
// check that the allocation happened correctly
assert(variable != NULL);
// i contains the current actual size of your array
int i = 0;
while (0 or EOF) {
if (i >= size) {
// if the array is getting bigger than its max size, resize it.
size *= 2;
// This will reallocate enough memory for variable.
variable = realloc(variable, sizeof(float)*size);
// check that the allocation happened correctly;
assert(variable != NULL);
// (NB: It IS important to affect variable to the result of
// realloc, you can't simply realloc as in some cases the
// original pointer will become invalid!)
}
scanf("%f", &variable[i])
i++;
}
As an aside, please note that variable is not a wonderful variable name. Use a name describing what your variable is used for.
EDIT: corrected the size in realloc to alloc size*2 floats and avoid it break horribly, as unwind pointed out.

i = 0;
while(scanf("%f", &variable[i])!=-1)
{
i++;
}
scanf returns -1 when it tries to read after EOF.

Related

How to handle buffer error while validating user input

I need to read in user input as an integer to pass it to my other function. If I use my validation (code below), it crashes after 4 bad inputs. I'm not completely sure if this is even a buffer error or not. But I also didn't find a proper way to validate my input and handle the errors. I didn't use scanf(%d) on purpose because I wanted to dodge the warning CLion is giving me when using it. I hope someone here can explain to me why my code is crashing after 4 bad inputs and how to fix it, or show me an alternative way.
char *userInput = malloc(100);
long amountOfPlayers;
//Todo: More Validation needed, bufferoverflow
for (int i = 0; i < sizeof(userInput) / sizeof(*userInput); i++) {
char *end;
printf("Please enter the amount of players: ");
scanf("%s", userInput);
amountOfPlayers = strtol(userInput, &end, 10);
if (end == userInput) {
printf("wasn't a number\n");
}
else if (end[0] != '\0') {
printf("trailing characters after number %ld: %s\n", amountOfPlayers, end);
}
else
return init_playerList(amountOfPlayers);
}
userInput is a pointer, not an array, so sizeof(userInput) returns the size of a pointer, typically 4 bytes. sizeof(*userInput) is sizeof(char), which is 1. So sizeof(userInput) / sizeof(*userInput) is 4, which means your for loop only executes 4 times. See How to find the 'sizeof' (a pointer pointing to an array)?
There's no need for a for loop, just use while (true). You're not doing anything that iterates over the elements of userInput, it's just the buffer.
There's also no reason to allocate it with malloc(), you can simply declare:
char userInput[100];
You have a memory leak because you never free(userInput) before returning from the function. But if you declare it as an array this is not necessary.
TO prevent buffer overflow you should use:
scanf("%100s", userInput);
sizeof(userInput) / sizeof(*userInput) won't return the number of elements, because userInput is a pointer, not an array. This only works for pure arrays. In case of pointer is always return the same value: size of a pointer divided by the size of the object.
int size = 100;
char *userInput = malloc(size);
if(userInput == NULL)
{
// error handling
}
for (int i = 0; i < n; i++) {
....
}
would be correct.

printf statement error

my last printf statement is not printing I'm not sure what's wrong since I'm using getchar to read the standard input, I must also use %s for this problem. I'm trying to make the last printf statement print the exact input from standard input.
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#define INITIAL_BUFFER_SIZE 16
// Input: pointer to the the old_Buffer i.e. *buffer and the pointer to the old_Buffer size i.e. *buffer_size
// Output: pointer to the new_Buffer i.e. *temp
// Summary: Function to increase the buffer size to double whenever the old_Buffer reaches the max possible size
char* IncreaseBuffer(char *buffer,int *buffer_size){
// Creating a new buffer of double the size
int temp_sz = *buffer_size;
temp_sz = 2*temp_sz;
char *temp;
temp = realloc(buffer, temp_sz);
// If the allocation is not successful, then exit the program and print the error
if (!temp) {
printf("Error: Unable to Realloc\n");
exit(EXIT_FAILURE);
}
// Update the buffer size as per the new buffer and return the new buffer instead of the old buffer
*buffer_size = temp_sz;
return temp;
}
int main(void){
// INITIAL BUFFER
char *myString;
myString = malloc(INITIAL_BUFFER_SIZE);
int curr_size = INITIAL_BUFFER_SIZE;
// TEMPORARY CHARACTER TO STORE THE READ CHARACTER
char ch;
printf("Enter a string: ");
// Number of buffer increases
int buff_inc = 0;
// Length of the string
int len = 0;
while((ch=getchar())!=EOF){
if(ch == '\n'){
// If character read is a new line, then we break and assume that we have got our string to print by now
break;
}else{
if(len >= curr_size - 1){
// If length of the string is greater than the buffer size, then we increase the buffer size
// Also increment the number of buffer increases
myString = IncreaseBuffer(myString, &curr_size);
buff_inc++;
}
// Append the read character to the end of the buffer
myString[len++] = ch;
}
}
printf("String size: %d\n", len);
printf("Buffer increases: %d\n", buff_inc);
printf("You entered: %s\n",myString);
return 0;
}
You must not free the old buffer after calling realloc. That is bad. It has already been resized and memory allocation taken care of. It might even keep the same address after reallocation.
Just remove the following lines:
// Free the old buffer
free(buffer);
buffer = NULL;
And, already pointed out in the comments, you forgot to terminate your string. Do this after the loop:
myString[len] = '\0';
Trimmed down after just seeing #paddy's answer, but for the record this is from the realloc docs:
On success, returns the pointer to the beginning of newly allocated memory. The returned pointer must be deallocated with free() or realloc(). The original pointer ptr is invalidated and any access to it is undefined behavior (even if reallocation was in-place).
On failure, returns a null pointer. The original pointer ptr remains valid and may need to be deallocated with free() or realloc().

reading a file of strings to a multidimensional array to access later

I am really having a problem understanding dynamically allocated arrays.
I am attempting to read a text file of strings to a 2d array so I can sort them out later. right now as my code stands it throws seg faults every once in a while. Which means I'm doing something wrong. I've been surfing around trying to get a better understanding of what malloc actually does but I want to test and check if my array is being filled.
my program is pulling from a text file with nothing but strings and I am attempting to put that data into a 2d array.
for(index = 0; index < lines_allocated; index++){
//for loop to fill array 128 lines at a time(arbitrary number)
words[index] = malloc(sizeof(char));
if(words[index] == NULL){
perror("too many characters");
exit(2);
}
//check for end of file
while(!feof(txt_file)) {
words = fgets(words, 64, txt_file);
puts(words);
//realloc if nessesary
if (lines_allocated == (index - 1)){
realloc(words, lines_allocated + lines_allocated);
}
}
}
//get 3rd value placed
printf("%s", words[3]);
since this just a gist, below here ive closed and free'd the memory, The output is being displayed using puts, but not from the printf from the bottom. an ELI5 version of reading files to an array would be amazing.
Thank you in advance
void *malloc(size_t n) will allocate a region of n bytes and return a pointer to the first byte of that region, or NULL if it could not allocate enough space. So when you do malloc(sizeof(char)), you're only allocating enough space for one byte (sizeof(char) is always 1 by definition).
Here's an annotated example that shows the correct use of malloc, realloc, and free. It reads in between 0 and 8 lines from a file, each of which contains a string of unknown length. It then prints each line and frees all the memory.
#include <stdio.h>
#include <stdlib.h>
/* An issue with reading strings from a file is that we don't know how long
they're going to be. fgets lets us set a maximum length and discard the
rest if we choose, but since malloc is what you're interested in, I'm
going to do the more complicated version in which we grow the string as
needed to store the whole thing. */
char *read_line(void) {
size_t maxlen = 16, i = 0;
int c;
/* sizeof(char) is defined to be 1, so we don't need to include it.
the + 1 is for the null terminator */
char *s = malloc(maxlen + 1);
if (!s) {
fprintf(stderr, "ERROR: Failed to allocate %zu bytes\n", maxlen + 1);
exit(EXIT_FAILURE);
}
/* feof only returns 1 after a read has *failed*. It's generally
easier to just use the return value of the read function directly.
Here we'll keep reading until we hit end of file or a newline. */
while ('\n' != (c = getchar())) {
if (EOF == c) {
/* We return NULL to indicate that we hit the end of file
before reading any characters, but if we've read anything,
we still want to return the string */
if (0 == i) return NULL;
break;
}
if (i == maxlen) {
/* Allocations are expensive, so we don't want to do one each
iteration. As such, we're always going to allocate more than
we need. Exactly how much extra we allocate depends on the
program's needs. Here, we just add a constant amount. */
maxlen += 16;
/* realloc will attempt to resize the memory pointed to by s,
or copy it to a newly allocated region of size maxlen. If it
makes a copy, it will free the old version. */
char *p = realloc(s, maxlen + 1);
if (!p) {
/* If the realloc fails, it does not free the old version, so we do it here. */
free(s);
fprintf(stderr, "ERROR: Failed to allocate %zu bytes\n", maxlen + 1);
exit(EXIT_FAILURE);
}
s = p;//set the pointer to the newly allocated memory
}
s[i++] = c;
}
s[i] = '\0';
return s;
}
int main(void) {
/* If we wanted to, we could grow the array of strings just like we do the strings
themselves, but for brevity's sake, we're just going to stop reading once we've
read 8 of them. */
size_t i, nstrings = 0, max_strings = 8;
/* Each string is an array of characters, so we allocate an array of char*;
each char* will point to the first element of a null-terminated character array */
char **strings = malloc(sizeof(char*) * max_strings);
if (!strings) {
fprintf(stderr, "ERROR: Failed to allocate %zu bytes\n", sizeof(char*) * max_strings);
return 1;
}
for (nstrings = 0; nstrings < max_strings; nstrings++) {
strings[nstrings] = read_line();
if (!strings[nstrings]) {//no more strings in file
break;
}
}
for (i = 0; i < nstrings; i++) {
printf("%s\n", strings[i]);
}
/* Free each individual string, then the array of strings */
for (i = 0; i < nstrings; i++) {
free(strings[i]);
}
free(strings);
return 0;
}
I haven't looked too closely so I could be offering an incomplete solution.
That being said, the error is probably here:
realloc(words, lines_allocated + lines_allocated);
realloc if succesful returns the new pointer, if you're lucky it can allocate the adjacent space (which wouldn't cause a segfault).
words = realloc(words, lines_allocated + lines_allocated);
would solve it, although you probably need to check for errors.

reading an unbounded line from the console with scanf

I need to read a finite yet unbounded-in-length string.
We learned only about scanf so I guess I cannot use fgets.
Anyway, I've ran this code on a an input with length larger than 5.
char arr[5];
scanf("%s", arr);
char *s = arr;
while (*s != '\0')
printf("%c", *s++);
scanf keeps scanning and writing the overflowed part, but it seems like an hack. Is that a good practice? If not, how should I read it?
Note: We have learned about the alloc functions family.
Buffer overflows are a plague, of the most famous and yet most elusive bugs. So you should definitely not rely on them.
Since you've learned about malloc() and friends, I suppose you're expected to make use of them.
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
// Array growing step size
#define CHUNK_SIZE 8
int main(void) {
size_t arrSize = CHUNK_SIZE;
char *arr = malloc(arrSize);
if(!arr) {
fprintf(stderr, "Initial allocation failed.\n");
goto failure;
}
// One past the end of the array
// (next insertion position)
size_t arrEnd = 0u;
for(char c = '\0'; c != '\n';) {
if(scanf("%c", &c) != 1) {
fprintf(stderr, "Reading character %zu failed.\n", arrEnd);
goto failure;
}
// No more room, grow the array
// (-1) takes into account the
// nul terminator.
if(arrEnd == arrSize - 1) {
arrSize += CHUNK_SIZE;
char *newArr = realloc(arr, arrSize);
if(!newArr) {
fprintf(stderr, "Reallocation failed.\n");
goto failure;
}
arr = newArr;
// Debug output
arr[arrEnd] = '\0';
printf("> %s\n", arr);
// Debug output
}
// Append the character and
// advance the end index
arr[arrEnd++] = c;
}
// Nul-terminate the array
arr[arrEnd++] = '\0';
// Done !
printf("%s", arr);
free(arr);
return 0;
failure:
free(arr);
return 1;
}
%as or %ms(POSIX) can be used for such purpose If you are using gcc with glibc.(not C standard)
#include <stdio.h>
#include <stdlib.h>
int main(void){
char *s;
scanf("%as", &s);
printf("%s\n", s);
free(s);
return 0;
}
scanf is the wrong tool for this job (as for most jobs). If you are required to use this function, read one char at a time with scanf("%c", &c).
You code misuses scanf(): you are passing arr, the address of an array of pointers to char instead of an array of char.
You should allocate an array of char with malloc, read characters into it and use realloc to extend it when it is too small, until you get a '\n' or EOF.
If you can rewind stdin, you can first compute the number of chars to read with scanf("%*s%n", &n);, then allocate the destination array to n+1 bytes, rewind(stdin); and re-read the string into the buffer with scanf("%s", buf);.
It is risky business as some streams such as console input cannot be rewinded.
For example:
fpos_t pos;
int n = 0;
char *buf;
fgetpos(stdin, &pos);
scanf("%*[^\n]%n", &n);
fsetpos(stdin, &pos);
buf = calloc(n+1, 1);
scanf("%[^\n]", buf);
Since you are supposed to know just some basic C, I doubt this solution is what is expected from you, but I cannot think of any other way to read an unbounded string in one step using standard C.
If you are using the glibc and may use extensions, you can do this:
scanf("%a[^\n]", &buf);
PS: all error checking and handling is purposely ignored, but should be handled in you actual assignment.
Try limiting the amount of characters accepted:
scanf("%4s", arr);
It's just that you're writing beyond arr[5]. "Hopefully" you're keeping writing on allocated memory of the process, but if you go beyond you'll end up with a segmentation fault.
Consider
1) malloc() on many systems only allocates memory, not uses it. It isn't until the memory is assigned that the underlining physical memory usage occurs. See Why is malloc not "using up" the memory on my computer?
2) Unbounded user input is not realistic. Given that some upper bound should be employed to prevent hackers and nefarious users, simple use a large buffer.
If you system can work with these two ideas:
char *buf = malloc(1000000);
if (buf == NULL) return NULL; // Out_of_memory
if (scanf("%999999s", buf) != 1) { free(buf); return NULL; } //EOF
// Now right-size buffer
size_t size = strlen(buf) + 1;
char *tmp = realloc(buf, size);
if (tmp == NULL) { free(buf); return NULL; } // Out_of_memory
return tmp;
Fixed up per #chqrlie comments.

How do I use scanf when I dont know how many values it will assign in C?

These are the instructions:
"Read characters from standard input until EOF (the end-of-file mark) is read. Do not prompt the user to enter text - just read data as soon as the program starts."
So the user will be entering characters, but I dont know how many. I will later need to use them to build a table that displays the ASCII code of each value entered.
How should I go about this?
This is my idea
int main(void){
int inputlist[], i = -1;
do {++i;scanf("%f",&inputlist[i]);}
while(inputlist[i] != EOF)
You said character.So this might be used
char arr[10000];
ch=getchar();
while(ch!=EOF)
{
arr[i++]=ch;
ch=getchar();
}
//arr[i]=0; TO make it a string,if necessary.
And to convert to ASCII
for(j=0;j<i;j++)
printf("%d\n",arr[j]);
If you are particular in using integer array,Use
int arr[1000];
while(scanf("%d",&arr[i++])!=EOF);
PPS:This works only if your input is one character per line.
scanf returns EOF on EOF
You have a reasonable attempt at a start to the solution, with a few errors. You can't define an array without specifying a size, so int inputlist[] shouldn't even compile. Your scanf() specifier is %f for float, which is wrong twice (once because you declared inputlist with an integer type, and twice because you said your input is characters, so you should be telling scanf() to use %c or %s), and really if you're reading input unconditionally until EOF, you should use an unconditional input function, such as fgets() or fread(). (or read(), if you prefer).
You'll need two things: A place to store the current chunk of input, and a place to store the input that you've already read in. Since the input functions I mentioned above expect you to specify the input buffer, you can allocate that with a simple declaration.
char input[1024];
However, for the place to store all input, you'll want something dynamically allocated. The simplest solution is to simply malloc() a chunk of storage, keep track of how large it is, and realloc() it if and when necessary.
char *all_input;
int poolsize=16384;
all_input = malloc(pool_size);
Then, just loop on your input function until the return value indicates that you've hit EOF, and on each iteration of the loop, append the input data to the end of your storage area, increment a counter by the size of the input data, and check whether you're getting too close to the size of your input storage area. (And if you are, then use realloc() to grow your storage.)
You could read the input by getchar until reach EOF. And you don't know the size of input, you should use dynamic size buffer in heap.
char *buf = NULL;
long size = 1024;
long count = 0;
char r;
buf = (char *)malloc(size);
if (buf == NULL) {
fprintf(stderr, "malloc failed\n");
exit(1);
}
while( (r = getchar()) != EOF) {
buf[count++] = r;
// leave one space for '\0' to terminate the string
if (count == size - 1) {
buf = realloc(buf,size*2);
if (buf == NULL) {
fprintf(stderr, "realloc failed\n");
exit(1);
}
size = size * 2;
}
}
buf[count] = '\0';
printf("%s \n", buf);
return 0;
Here is full solution for your needs with comments.
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
// Number of elements
#define CHARNUM 3
int main(int argc, char **argv) {
// Allocate memory for storing input data
// We calculate requested amount of bytes by the formula:
// NumElement * SizeOfOneElement
size_t size = CHARNUM * sizeof(int);
// Call function to allocate memory
int *buffer = (int *) calloc(1, size);
// Check that calloc() returned valid pointer
// It can: 1. Return pointer in success or NULL in faulire
// 2. Return pointer or NULL if size is 0
// (implementation dependened).
// We can't use this pointer later.
if (!buffer || !size)
{
exit(EXIT_FAILURE);
}
int curr_char;
int count = 0;
while ((curr_char = getchar()) != EOF)
{
if (count >= size/sizeof(int))
{
// If we put more characters than now our buffer
// can hold, we allocate more memory
fprintf(stderr, "Reallocate memory buffer\n");
size_t tmp_size = size + (CHARNUM * sizeof(int));
int *tmp_buffer = (int *) realloc(buffer, tmp_size);
if (!tmp_buffer)
{
fprintf(stderr, "Can't allocate enough memory\n");
exit(EXIT_FAILURE);
}
size = tmp_size;
buffer = tmp_buffer;
}
buffer[count] = curr_char;
++count;
}
// Here you get buffer with the characters from
// the standard input
fprintf(stderr, "\nNow buffer contains characters:\n");
for (int k = 0; k < count; ++k)
{
fprintf(stderr, "%c", buffer[k]);
}
fprintf(stderr, "\n");
// Todo something with the data
// Free all resources before exist
free(buffer);
exit(EXIT_SUCCESS); }
Compile with -std=c99 option if you use gcc.
Also you can use getline() function which will read from standard input line by line. It will allocate enough memory to store line. Just call it until End-Of-File.
errno = 0;
int read = 0;
char *buffer = NULL;
size_t len = 0;
while ((read = getline(&buffer, &len, stdin)) != -1)
{ // Process line }
if (errno) { // Get error }
// Process later
Note that if you are using getline() you should anyway use dynamic allocated memory. But not for storing characters, rather to store pointers to the strings.

Resources