13

I am getting "Bus Error" trying to read stdin into a char* variable. I just want to read whole stuff coming over stdin and put it first into a variable, then continue working on the variable.

My Code is as follows:

char* content;
char* c;
while( scanf( "%c", c)) {
 strcat( content, c);
}

fprintf( stdout, "Size: %d", strlen( content));

But somehow I always get "Bus error" returned by calling cat test.txt | myapp, where myapp is the compiled code above.

My question is how do i read stdin until EOF into a variable? As you see in the code, I just want to print the size of input coming over stdin, in this case it should be equal to the size of the file test.txt.

I thought just using scanf would be enough, maybe buffered way to read stdin?

2
  • Why not just use stat() to get the file size, then use the file size + 1 to (try to) malloc the buffer and then read() to the buffer? Commented Sep 27, 2013 at 4:58
  • "string variable" makes no sense in C. There's no string type in C (and variables must have a type) -- instead there's a convention that a NUL-terminated sequence of characters in an array is called a "string". So to "store a string in a variable", that variable must really be an array or a pointer to an array that you're going to use to hold the string. Commented Dec 6, 2024 at 8:23

5 Answers 5

22

First, you're passing uninitialized pointers, which means scanf and strcat will write memory you don't own. Second, strcat expects two null-terminated strings, while c is just a character. This will again cause it to read memory you don't own. You don't need scanf, because you're not doing any real processing. Finally, reading one character at a time is needlessly slow. Here's the beginning of a solution, using a resizable buffer for the final string, and a fixed buffer for the fgets call

#define BUF_SIZE 1024
char buffer[BUF_SIZE];
size_t contentSize = 1; // includes NULL
/* Preallocate space.  We could just allocate one char here, 
but that wouldn't be efficient. */
char *content = malloc(sizeof(char) * BUF_SIZE);
if(content == NULL)
{
    perror("Failed to allocate content");
    exit(1);
}
content[0] = '\0'; // make null-terminated
while(fgets(buffer, BUF_SIZE, stdin))
{
    char *old = content;
    contentSize += strlen(buffer);
    content = realloc(content, contentSize);
    if(content == NULL)
    {
        perror("Failed to reallocate content");
        free(old);
        exit(2);
    }
    strcat(content, buffer);
}

if(ferror(stdin))
{
    free(content);
    perror("Error reading from stdin.");
    exit(3);
}

EDIT: As Wolfer alluded to, a NULL in your input will cause the string to be terminated prematurely when using fgets. getline is a better choice if available, since it handles memory allocation and does not have issues with NUL input.

Sign up to request clarification or add additional context in comments.

4 Comments

Gives invalid conversion from ‘void*’ to ‘char*’ [- fpermissive] on char *content = malloc(sizeof(char) * BUF_SIZE); and content = realloc(content, contentSize); with gcc 4.8.1 ...
Also it fails when presented with 0x00 in stdin.
@Wolfer, gcc's C compiler has no -fpermissive. Are you perhaps compiling it as C++; that would explain the error? The tags say C. You're right that fgets does not play well with NUL characters. It does not causes any undefined behavior, but you won't know and the string will be prematurely terminated. I'll add a note about getline to the question.
Whoops, my bad, sorry.
9

Since you don't care about the actual content, why bother building a string? I'd also use getchar():

int    c;
size_t s = 0;

while ((c = getchar()) != EOF)
{
  s++;
}

printf("Size: %zu\n", s);

This code will correctly handle cases where your file has '\0' characters in it.

3 Comments

The OP said: "I just want to read whole stuff coming over stdin and put it first into a variable, then continue working on the variable." I think the length() call on it is just an example. But your comment about the file having '\0' characters is an important one -- their whole idea of putting it in a single variable may be a bit flawed there.
c should be declared as int to include the range of char plus EOF.
@Brooks, I must have missed that when reading. I think the answer stands as a reasonable example anyway.
7

Your problem is that you've never allocated c and content, so they're not pointing anywhere defined -- they're likely pointing to some unallocated memory, or something that doesn't exist at all. And then you're putting data into them. You need to allocate them first. (That's what a bus error typically means; you've tried to do a memory access that's not valid.)

(Alternately, since c is always holding just a single character, you can declare it as char c and pass &c to scanf. No need to declare a string of characters when one will do.)

Once you do that, you'll run into the issue of making sure that content is long enough to hold all the input. Either you need to have a guess of how much input you expect and allocate it at least that long (and then error out if you exceed that), or you need a strategy to reallocate it in a larger size if it's not long enough.

Oh, and you'll also run into the problem that strcat expects a string, not a single character. Even if you leave c as a char*, the scanf call doesn't make it a string. A single-character string is (in memory) a character followed by a null character to indicate the end of the string. scanf, when scanning for a single character, isn't going to put in the null character after it. As a result, strcpy isn't going to know where the end of the string is, and will go wandering off through memory looking for the null character.

Comments

2

The problem here is that you are referencing a pointer variable that no memory allocated via malloc, hence the results would be undefined, and not alone that, by using strcat on a undefined pointer that could be pointing to anything, you ended up with a bus error!

This would be the fixed code required....

char *content = malloc (100 * sizeof(char));
char c;
if (content != NULL){
   content[0] = '\0'; // Thanks David!
   while ((c = getchar()) != EOF)
   {
       if (strlen(content) < 100){
           strcat(content, c);
           content[strlen(content)-1] = '\0';
       }
   }
}
/* When done with the variable */
free(content);

The code highlights the programmer's responsibility to manage the memory - for every malloc there's a free if not, you have a memory leak!

Edit: Thanks to David Gelhar for his point-out at my glitch! I have fixed up the code above to reflect the fixes...of course in a real-life situation, perhaps the fixed value of 100 could be changed to perhaps a #define to make it easy to expand the buffer by doubling over the amount of memory via realloc and trim it to size...

3 Comments

This code has a bug: the first time through the loop you're calling "strlen" on uninitialized data. (and of course you should be checking for buffer overruns if more than 100 chars of input are present)
As Jon Purdy noted on one of the other posts, you need to declare c as int, not char, so that the possible EOF result will be in range. Otherwise you'll go into an infinite loop. Also, your strcat call expects two null-terminated strings, not a string and a single character. As written, it won't compile.
Quick note on <pre> blocks here; for some reason they add excessive margins to code blocks at the top on bottom, and AFAIK they do not permit a language to be specified, which affects syntax colouration. So the standard Markdown code device is indeed objectively the better choice.
0

Assuming that you want to get (shorter than MAXL-1 chars) strings and not to process your file char by char, I did as follows:

#include <stdio.h>
#include <string.h>
#define MAXL 256

main(){
  char s[MAXL];
  s[0]=0;
  scanf("%s",s);
  while(strlen(s)>0){
    printf("Size of %s : %d\n",s,strlen(s));
    s[0]=0;
    scanf("%s",s);
  };
}

1 Comment

strlen returns a size_t which must be printed using %zu, not `%d

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.