1

so far i have this code:

char *str;
scanf ("%s", &str);
printf("%s",&str);
system("pause");

it works, but the problem is after i press a key to continue the program (or end it), i get the following error:

Run-Time Check Failure #2 - Stack around the variable 'str' was corrupted.

i have never played around with char pointers before and i would like to get an explanation to why i am getting this error and how to improve the format of which i receive a string input from the user. i know there are easier ways to this, but my ultimate goal is to get the simplest code to determine the size of the input, and not limit the user.

thanks in advance

ziggyyoyo
  • 81
  • 1
  • 1
  • 6

5 Answers5

6

Your 2nd part of the question (determining length of input beforehand) isn't possible with a simple scanf (except going more complex and reading/appending chunk by chunk).

But at least the code below fixes your overwrite of random memory.

char buffer[256];
char *str= buffer;
scanf ("%s", str);  // or scanf ("%s", buffer);  or scanf("%s", &buffer[0]);
printf("%s", str);  // or printf("%s", buffer);  or printf("%s", &buffer[0]);
system("pause");

You could use a very large buffer beforehand, and then trim the result back to given length, but you need max. length to start with.

char *str= (char *)calloc(1,10000);
scanf ("%.9999s", str);
str= (char *)realloc(str, strlen(str)+1);  // release excess memory 
printf("%s", str); 
free(str);
system("pause");
Nicholaz
  • 1,419
  • 9
  • 11
  • this method is easy and i know how to do. the thing that im looking for is to get the buffer to be the exact size as the string minus the delimiter – ziggyyoyo Jun 04 '13 at 17:06
  • ziggy: there is no easy way to do that (getting the string without knowing how long). You need to provide a max possible length, then maybe you can realloc it with the length of the result – Nicholaz Jun 04 '13 at 17:08
  • And your above version only "works" because you are telling the system to write the input into memory that doesn't belong to you. The runtime notices that some of its own memory is getting overwritten and that's why you get the error message. – Nicholaz Jun 04 '13 at 17:09
  • 1
    Then start your question maybe with a version that at least works, and don't distract people with glaringly obviously faulty code. – Nicholaz Jun 04 '13 at 17:11
  • i have mentioned in my post that its faulty as i have no experience in this particular field. i have stated that my ultimate assignment is to do more or less what magnisium suggested. sorry if i did anything wrong – ziggyyoyo Jun 04 '13 at 17:13
  • excellent, just one problem at calloc: says i cant use a void* to initialize char*, any ideas? – ziggyyoyo Jun 04 '13 at 17:23
  • edited (add "(char*)" right of the "=". But that is C++ then, not C – Nicholaz Jun 04 '13 at 17:24
  • i am limited to a c compiler. is there not a c cast? – ziggyyoyo Jun 04 '13 at 17:27
  • The cast (char *) works in C too, but should not be necessary. – Nicholaz Jun 04 '13 at 17:29
  • nevermind, it compiled. however, the string did not display. why? – ziggyyoyo Jun 04 '13 at 17:29
  • @ziggyyoyo: you may need to add a newline to the output or a call to `fflush`; see my answer below for an explanation why. – John Bode Jun 04 '13 at 17:30
  • removing the %.9999%s and replaced it with a regular %s fixed it. what are the consequences of doing so? – ziggyyoyo Jun 04 '13 at 17:30
  • sorry, my fault, it should be "%.9999s" ... the extra "%" made it a faulty pattern/placeholder for scanf and it didn't scan anything. – Nicholaz Jun 04 '13 at 17:31
  • sorry for not paying attention. i understand now. thank you very much nicholaz – ziggyyoyo Jun 04 '13 at 17:32
2

You've declared str as a pointer, but you haven't given it a valid location to point to; it initially contains some random value that may or may not be a writable memory address. You still need to allocate a block of memory large enough to hold your input, either by declaring an array or by allocating memory with malloc or calloc.

Secondly, the %s conversion specifier expects the corresponding argument to have type char *; since str has that type, you don't need to use the & operator.

So, you'll need to do something like

char str[SOME_SIZE]; 

or

char *str = malloc( SOME_SIZE );

where SOME_SIZE is large enough to hold the string you enter plus one extra character for the 0 terminator.

You'd read and write the input as follows:

scanf( "%s", str ); 
printf( "%s", str ); 

You don't need the & for either call. The %s conversion specifier expects the corresponding argument to have type char *. Under most circumstances1, an expression of array type is converted to an expression of pointer type, and the value of the expression is the address of the first element in the array. If you declared str as an array, then in the scanf and printf calls the expression str2 will be converted to a char *. If you declared str as a char *, it's already the expected type.

Now, output is (typically) line-buffered, meaning it won't show up on your console unless there's a newline or the length of the string exceeds the buffer length. You will either want to add a newline to your print statement:

printf( "%s\n", str );

or call fflush on the standard output stream:

printf( "%s", str );
fflush( stdout );

to make sure your output gets displayed.


1 - The exceptions to this rule occur when the array expression is the operand of the sizeof. _Alignof, or unary & operators, or is a string literal being used to initialize another array in a declaration.

2 - Remember, this conversion operation applies to expressions, not objects. You'll occasionally hear someone claim that "arrays are pointers", but this isn't true; arrays and pointers are different animals completely.
John Bode
  • 119,563
  • 19
  • 122
  • 198
1

"my ultimate goal is to get the simplest code to determine the size of the input, and not limit the user."

Yeah, therein lies the problem, once you do that things start to get tricky. You can’t simply use an uninitialized pointer like that to get “as many” characters as you want. The simplest options are:

char *str = malloc(1000);
scanf ("%999s", str);

or

char str[1000];
scanf ("%999s", &str);

of course this puts a constraint on the amount of input. (btw: You should really evaluate if there is a number which will satisfy this condition.)

Now there are other options, but they start to move away from being portable. If you're using GCC there's a "dynamic" specificer that can be used:

char *str;
scanf("%ms", str); // dynamically allocate space for the size of input from the user

But using this now ties your hands to the compiler being used (also the version, this works for c99, earlier needs "%as")

Other than that, you can read the input in 1 character at a time and append it to a string you grow yourself, but that's an OS specific solution as you'll need to use ncurses and the getch() function on *nix or _kbhit() and _getch(); in windoze.

Mike
  • 47,263
  • 29
  • 113
  • 177
  • someone here suggested its possible to reallocate after i get an input which would suit my goal. thank you for your input. – ziggyyoyo Jun 04 '13 at 17:21
  • @ziggyyoyo - Yes, that works, but keep in mind with that approach technically the user is still limited on the input size, the `realloc()` will only "shrink" the buffer back down to a size matching what was inputted. – Mike Jun 04 '13 at 17:27
  • Actually is `scanf("%ms", &str)` – roschach Jan 31 '19 at 12:27
0

Printf takes a pointer, not an address to a pointer. You should be using it like this:

 scanf("%s", str)
 printf("%s", str);



 char * str = malloc(100);  //allocates 100 bytes

 scanf("%###c", str) //Gets line from STDIN. ### indicates how many to read MAXIMUM, so scanf("%2c", str) will read maximum 2 characters into str.  Don't use s, since then you read until every whiteline.

 int i = 0;
 while(str[i] != NULL){

 i++;    

 }

 realloc(buf, i+1); //make the string fit tightly. I'm unsure if it will be null     terminated

//Maybe add null termination //You will have to make sure they don't try

KrisSodroski
  • 2,796
  • 3
  • 24
  • 39
  • that doesnt work, i get: Run-Time Check Failure #3 - The variable 'str' is being used without being initialized. i tried that before too, this is the only variation that worked for me so far. – ziggyyoyo Jun 04 '13 at 16:59
  • You need to allocate space to str. char * str = malloc(size of(whatsizedoyouwant)); – KrisSodroski Jun 04 '13 at 17:01
  • the thing is i want a custom sized string. i wanted to just get its size after the user inputted it. – ziggyyoyo Jun 04 '13 at 17:02
  • You can use realloc if you run out of space. Thats the way it works in C. – KrisSodroski Jun 04 '13 at 17:02
  • is there no way i could allocate the exact size i need? – ziggyyoyo Jun 04 '13 at 17:04
  • Sure, it depends on the situation. If you're getting input from another one of your processes, you make the protocol so you know how long the messages are. You can also end your transmission with a character and that way you know its done. You'll still have to allocate, and then realloc to get it EXACTLY right without a ton of overhead (aka, calling realloc for every character). So just allocate a ton of space, and then when you know the size (null terminated), then just realloc it to the exact size. – KrisSodroski Jun 04 '13 at 17:06
  • can you rewrite the code for me please, magn3s1um? i would like to try what you suggested with scanf and reallocate the space. i will appreciate it very much if you could – ziggyyoyo Jun 04 '13 at 17:09
0

You are scanning the str with out first allocating memory to it. But, by your luck it starts storing from a random address and hence printed well but your runtime system recognized that you ran into a memory area which you do not have priveleges.

Here is the solution: This is subjected to realloc fail when it can't acquire sufficient contigous memory

 #include<stdio.h>
#include<malloc.h>
#define MAX 100
int main(){
    char *str=malloc(sizeof(char)*MAX);
    int size=0;
    int capacity=MAX;
    char c;
    int i=0;
    while((c=getchar())!='\n'){
        if(size==MAX-1){
            capacity=2*MAX;
            realloc(str,capacity);
        }
        str[i]=c;
        i++;
    }
    str[i]='\0';
    printf("%s",str);
    system("pause");    
}
pinkpanther
  • 4,770
  • 2
  • 38
  • 62
  • i should mention that it prints the string just as i got it with an input only with an error. – ziggyyoyo Jun 04 '13 at 17:04
  • @ziggyyoyo I have updated reasons for error and I will come with solution to the second part wait – pinkpanther Jun 04 '13 at 17:10
  • @ziggyyoyo here we go.... see you may also use `gets` which is dangerous or `fgets` which is safer with loop combination – pinkpanther Jun 04 '13 at 17:24
  • got same problem as with nicholaz's suggestion, @ malloc, void* cannot be used to initialize char* – ziggyyoyo Jun 04 '13 at 17:26
  • @ziggyyoyo I've tested the code it's working :) you're probably wrong....read here http://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc I was thinking like you few hours before – pinkpanther Jun 04 '13 at 17:27
  • @ziggyyoyo I used to think like you about void* and char*, but I got to know that completely safe to use when I was reading a stackoverflow post and that not using cast is recommended by them... – pinkpanther Jun 04 '13 at 17:33
  • @ziggyyoyo that's needed in `C++` but not in `C` – pinkpanther Jun 04 '13 at 17:39