Print one character from string - c

I'm facing an issue connected with printing one char from string in c.
The function takes from users two variables - number (number which should print character from string) and string. When I put as a string "Martin" and number is 5 then the output is "i". But when the number is larger than the string length something goes wrong and I actually don't know what's wrong.
PS. If the number is longer than string size it should print "Nothing".
void printLetter() {
char * string = (char*)malloc(sizeof(char));
int n;
printf("Number:\n");
scanf("%i", &n);
printf("String:\n");
scanf("%s", string);
if(n > strlen(string)) {
printf("nothing");
} else {
printf("%c\n", string[n+1]);
}
free(string);
}

There is no need for dynamic allocation here, since you do not know the length of the string in advance, so just do:
void printLetter() {
char string[100]; // example size 100
...
scanf("%99s", string); // read no more than your array can hold
}
A fun exercise would be to count the length of the string, allocate dynamically exactly as mush space as you need (+1 for the null terminator), copy string to that dynamically allocated space, use it as you wish, and then free it.
Moreover this:
printf("%c\n", string[n+1]);
should be written as this:
printf("%c\n", string[n-1]);
since you do not want to go out bounds of your array (and cause Undefined Behavior), or print two characters next of the requested character, since when I ask for the 1st character, you should print string[0], when I ask for the 2nd, you should print string[1], and so on. So you see why we need to print string[n-1], when the user asks for the n-th letter.
By the way, it's common to use a variable named i, and not n as in your case, when dealing with an index. ;)
In your code, this:
char * string = malloc(sizeof(char));
allocates memory for just one character, which is no good, since even if the string had one letter only, where would you put the null terminator? You know that strings in C should (almost) always be NULL terminated.
In order to allocate dynamically memory for a string of size N, you should do:
char * string = malloc((N + 1) * sizeof(char));
where you allocate space for N characters, plus 1 for the NULL terminator.

Couple of problems...
sizeof(char) is generally 1 byte. Hence malloc() is allocating only one byte of memory to string. Perhaps a larger block of memory is required? "Martin", for example, will require at least 6 bytes, plus the string termination character (seven bytes total).
printf("%c\n", string[n+1]) is perhaps not quite right...
String: Martin\0
strlen= 6
Offset: 0123456
n = 5... [n+1] = 6
The character being output is the string terminator '\0' at index 6.
This might work better:
void printLetter() {
char * string = malloc(100 * sizeof(char));
int n;
printf("Number:\n");
scanf("%i", &n);
printf("String:\n");
scanf("%s", string);
if(n > strlen(string)) {
printf("nothing");
} else {
printf("%c\n", string[n-1]);
}
free(string);
}

You are facing buffer overflow.
Take a look to this question, so it will show you how to manage your memory properly in such situation: How to prevent scanf causing a buffer overflow in C?
Alternatively you can ask for number of letter and allocate only that much memory + 1. Then fgets(string, n,stdin); because you don't need rest of the string :-)

Related

string gets filled with garbage

i got a string and a scanf that reads from input until it finds a *, which is the character i picked for the end of the text. After the * all the remaining cells get filled with random characters.
I know that a string after the \0 character if not filled completly until the last cell will fill all the remaining empty ones with \0, why is this not the case and how can i make it so that after the last letter given in input all the remaining cells are the same value?
char string1 [100];
scanf("%[^*]s", string1);
for (int i = 0; i < 100; ++i) {
printf("\n %d=%d",i,string1[i]);
}
if i try to input something like hello*, here's the output:
0=104
1=101
2=108
3=108
4=111
5=0
6=0
7=0
8=92
9=0
10=68
You have an uninitialized array:
char string1 [100];
that has indeterminate values. You could initialize the array like
char string1 [100] = { 0 };
or
char string1 [100] = "";
In this call
scanf("%[^*]s", string1);
you need to remove the trailing character s, because %[] and %s are distinct format specifiers. There is no %[]s format specifier. It should look like this:
scanf("%[^*]", string1);
The array contains a string terminated by the zero character '\0'.
So to output the string you should write for example
for ( int i = 0; string1[i] != '\0'; ++i) {
printf( "%c", string1[i] ); // or putchar( string1[i] );
putchar( '\n' );
or like
for ( int i = 0; string1[i] != '\0'; ++i) {
printf("\n %d=%c",i,string1[i]);
putchar( '\n' );
or just
puts( string1 );
As for your statement
printf("\n %d=%d",i,string1[i]);
then it outputs each character (including non-initialized characters) as integers due to using the conversion specifier d instead of c. That is the function outputs internal ASCII representations of characters.
I know that a string after the \0 character if not filled completly
until the last cell will fill all the remaining empty ones with \0
No, that's not true.
It couldn't be true: there is no length to a string. No where neither the compiler nor any function can even know what is the size of the string. Only you do. So, no, string don't autofill with '\0'
Keep in minds that there aren't any string types in C. Just pointer to chars (sometimes those pointers are constant pointers to an array, but still, they are just pointers. We know where they start, but there is no way (other than deciding it and being consistent while coding) to know where they stop.
Sure, most of the time, there is an obvious answer, that make obvious for any reader of the code what is the size of the allocated memory.
For example, when you code
char string1[20];
sprintf(string1, "hello");
it is quite obvious for a reader of that code that the allocated memory is 20 bytes. So you may think that the compiler should know, when sprinting in it of sscaning to it, that it should fill the unused part of the 20 bytes with 0. But, first of all, the compiler is not there anymore when you will sscanf or sprintf. That occurs at runtime, and compiler is at compilation time. At run time, there is not trace of that 20.
Plus, it can be more complicated than that
void fillString(char *p){
sprintf(p, "hello");
}
int main(){
char string1[20];
string1[0]='O';
string1[1]='t';
fillString(&(string1[2]));
}
How in this case does sprintf is supposed to know that it must fill 18 bytes with the string then '\0'?
And that is for normal usage. I haven't started yet with convoluted but legal usages. Such as using char buffer[1000]; as an array of 50 length-20 strings (buffer, buffer+20, buffer+40, ...) or things like
union {
char str[40];
struct {
char substr1[20];
char substr2[20];
} s;
}
So, no, strings are not filled up with '\0'. That is not the case. It is not the habit in C to have implicit thing happening under the hood. And that could not be the case, even if we wanted to.
Your "star-terminated string" behaves exactly as a "null-terminated string" does. Sometimes the rest of the allocated memory is full of 0, sometimes it is not. The scanf won't touch anything else that what is strictly needed. The rest of the allocated memory remains untouched. If that memory happened to be full of '\0' before the call to scanf, then it remains so. Otherwise not. Which leads me to my last remark: you seem to believe that it is scanf that fills the memory with non-null chars. It is not. Those chars were already there before. If you had the feeling that some other methods fill the rest of memory with '\0', that was just an impression (a natural one, since most of the time, newly allocated memory are 0. Not because a rule says so. But because that is the most frequent byte to be found in random area of memory. That is why uninitialized variables bugs are so painful: they occur only from times to times, because very often uninitialized variables are 0, just by chance, but still they are)
The easiest way to create a zeroed array is to use calloc.
Try replacing
char string1 [100];
with
char *string1=calloc(1,100);

Problem reading two strings with getchar() and then printing those strings in C

This is my code for two functions in C:
// Begin
void readTrain(Train_t *train){
printf("Name des Zugs:");
char name[STR];
getlinee(name, STR);
strcpy(train->name, name);
printf("Name des Drivers:");
char namedriver[STR];
getlinee(namedriver, STR);
strcpy(train->driver, namedriver);
}
void getlinee(char *str, long num){
char c;
int i = 0;
while(((c=getchar())!='\n') && (i<num)){
*str = c;
str++;
i++;
}
printf("i is %d\n", i);
*str = '\0';
fflush(stdin);
}
// End
So, with void getlinee(char *str, long num) function I want to get user input to first string char name[STR] and to second char namedriver[STR]. Maximal string size is STR (30 charachters) and if I have at the input more than 30 characters for first string ("Name des Zuges"), which will be stored in name[STR], after that I input second string, which will be stored in namedriver, and then printing FIRST string, I do not get the string from the user input (first 30 characters from input), but also the second string "attached" to this, I simply do not know why...otherwise it works good, if the limit of 30 characters is respected for the first string.
Here my output, when the input is larger than 30 characters for first string, problem is in the row 5 "Zugname", why I also have second string when I m printing just first one...:
Name des Zugs:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
i is 30
Name des Drivers:xxxxxxxx
i is 8
Zugname: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaxxxxxxxx
Drivername: xxxxxxxx
I think your issue is that your train->name is not properly terminated with '\0', as a consequence when you call printf("%s", train->name) the function keeps reading memory until it finds '\0'. In your case I guess your structure looks like:
struct Train_t {
//...
char name[STR];
char driver[STR];
//...
};
In getlinee() function, you write '\0' after the last character. In particular, if the input is more than 30 characters long, you copy the first 30 characters, then add '\0' at the 31-th character (name[30]). This is a first buffer overflow.
So where is this '\0' actually written? well, at name[30], even though your not supposed to write there. Then, if you have the structure above when you do strcpy(train->name, name); you will actually copy a 31-bytes long string: 30 chars into train->name, and the '\0' will overflow into train->driver[0]. This is the second buffer overflow.
After this, you override the train->driver buffer so the '\0' disappears and your data in memory basically looks like:
train->name = "aaa...aaa" // no '\0' at the end so printf won't stop reading here
train->driver = "xxx\0" // but there
You have an off-by-one error on your array sizes -- you have arrays of STR chars, and you read up to STR characters into them, but then you store a NUL terminator, requiring (up to) STR + 1 bytes total. So whenever you have a max size input, you run off the end of your array(s) and get undefined behavior.
Pass STR - 1 as the second argument to getlinee for the easiest fix.
Key issues
Size test in wrong order and off-by-one. ((c=getchar())!='\n') && (i<num) --> (i+1<num) && ((c=getchar())!='\n'). Else no room for the null character. Bad form to consume an excess character here.
getlinee() should be declared before first use. Tip: Enable all compiler warnings to save time.
Other
Use int c; not char c; to well distinguish the typical 257 different possible results from getchar().
fflush(stdin); is undefined behavior. Better code would consume excess characters in a line with other code.
void getlinee(char *str, long num) better with size_t num. size_t is the right size type for array sizing and indexing.
int i should be the same type as num.
Better code would also test for EOF.
while((i<num) && ((c=getchar())!='\n') && (c != EOF)){
A better design would return something from getlinee() to indicate success and identify troubles like end-of-file with nothing read, input error, too long a line and parameter trouble like str == NULL, num <= 0.
I believe you have a struct similar to this:
typedef struct train_s
{
//...
char name[STR];
char driver[STR];
//...
} Train_t;
When you attempt to write a '\0' to a string that is longer than STR (30 in this case), you actually write a '\0' to name[STR], which you don't have, since the last element of name with length STR has an index of STR-1 (29 in this case), so you are trying to write a '\0' outside your array.
And, since two strings in this struct are stored one after another, you are writing a '\0' to driver[0], which you immediately overwrite, hence when printing out name, printf doesn't find a '\0' until it reaches the end of driver, so it prints both.
Fixing this should be easy.
Just change:
while(((c=getchar())!='\n') && (i<num))
to:
while(((c=getchar())!='\n') && (i<num - 1))
Or, as I would do it, add 1 to array size:
char name[STR + 1];
char driver[STR + 1];

String concatenation in C?

I am trying to understand string's behavior in C and it is bothering me since my following two code snippets result into different output:
(For the sake of this question, Let us assume user enters 12)
int main(void)
{
char L_Red[2];
char temp[] = "I";
printf("Enter pin connected to red: ");
scanf("%s", L_Red);
strcat(temp,L_Red);
printf("%s \n", temp);
return 0;
}
this yields: 12 as output (and not I12) Why ?
int main(void)
{
char L_Red[2];
printf("Enter pin connected to red: ");
scanf("%s", L_Red);
char temp[] = "I";
strcat(temp,L_Red);
printf("%s \n", temp);
return 0;
}
This yields: I12I (and not, I12) Why ?
I have read about string in C and as per my understanding, neither am I allocating temp any fixed size and changing it later to get these vague outputs nor am I using strings like the way they are not supposed to. Is there any other concept at play here ?
The array temp is an array of two characters (the 'I' and the string terminator '\0'). That's it. Attempting to append more characters to that array will write out of bounds and lead to undefined behavior.
You need to make sure that the destination array temp have enough space to fit its original content plus the string you want to append (plus the terminator).
Also, if you want to input more than one character for the "string" L_Red you need to increase its size as well.
I also recommend you use a limit in the format specifier so you can't write out of bounds:
char L_Red[3]; // Space for two characters, plus terminator
scanf("%2s", L_Red); // Read at most two characters of input
You are getting strange answers because your destination string (ie the first argument to strcat) is not long enough to handle both strings plus a null termination character. Also the length of L_Red is too short as it does not have enough space for the null termination character either.

While entering a string in an single element of array in c why does the following code gives the shown output?

#include <stdio.h>
int main(int argc, char *argv[])
{
int i, n, m;
scanf("%d %d", &n, &m);
char s[m][n];
for (i = 0; i < m; i++) {
printf("the string --\n");
scanf("%s", s[i]);
}
for (i = 0; i < m; i++) {
printf("the strings are %s \n",s[i]);
printf("\n");
}
return 0;
}
The output is:
2 2
the string --
10
the string --
11
the strings are 1011
the strings are 11
Why is the first string 1011 instead of 10?
In C, strings are represented as a sequence of char values, terminated by a null character (0 or '\0'). This means that to store a two-character string, you need space for three characters: the two characters of string content, plus the null terminator character.
Here, you've only allocated enough space for two characters in each string, but you need space for three.
So, it reads the first string into the array s[0], but the null terminator doesn't fit, and so it overflows into the second array s[1]. Now your array of arrays s looks like this: {{'1', '0'}, {'\0', ... }}.
Then, when it reads the second string into the array s[1], it overwrites the overflowed null terminator from before. And the null terminator for the second string doesn't fit into its own array, so it overflows again into the rest of the stack. The program might crash here, or corrupt other data, because you're overflowing past the end of the array.
So now your array of arrays s ends up looking like this: {{'1', '0'}, {'1', '1'}}, followed by a '\0' somewhere after the end of the array.
When printf goes to read your first string, it prints characters until it finds a null terminator. But it doesn't find one in the first string, so it keeps going, and hits the second string. It doesn't find one there either, and continues past the end of the array. In your case, luckily a null terminator was right there, but for all we know there could be something else.
To fix this, you need to allocate an extra character per string on line 9, for the null terminator:
char s[m][n+1];
There's another problem here, however. What if your input gives you the wrong length? For example, what if your input says 2 3, i.e. that the following strings will have a length of 3, but gives you the string foobar, which is 6 characters? Your code right now would overflow the buffer when it read that string, because it doesn't ensure it's the right length.
One way to avoid this would be to use gets_s instead of sscanf() for reading the strings on line 13:
gets_s(s[i], n+1);
This will read at most n characters, so avoid crashing your program or creating a security issue. However, gets_s is a C11 function, so you may not be able to use it.
You must set column size to 3 if you insert 2 characters per string, 4 for 3 characters per string and so on.
This because string in C have a termination character ('\0') in the last position.
#include <stdio.h>
int main(int argc, char *argv[])
{
int i, n, m;
scanf("%d %d", &n, &m);
char s[m][n+1];
for (i = 0; i < m; i++) {
printf("the string --\n");
scanf("%s", s[i]);
}
for (i = 0; i < m; i++) {
printf("the strings are %s \n",s[i]);
printf("\n");
}
return 0;
}
%s expects null terminated strings as an argument. When first string is read by scanf, there is not enough space for the null terminator within the allocated memory of first string. It will goes to the space next to the allocated space. Writing to unallocated space invokes undefined behavior.
While printing the strings with %s specifier, printf write the string character by character till it finds a null terminator '\0'. Here it may be the case that both the strings 10 and 11 are stored one after another in memory, so printf writes the first string till it read the null character of second string.
Input n as 3 and you will get the desire results.
In my opninion using scanf to read strings is just pure evil.
That said the array s[m][n] is just s[m*n] of course.
That said that evil thing scanf is going to load on *s[0] 10\n\0 and on *s[1*n] or *s[2] 11 and *s will be 1011\n\0
And this is a monument to bad C coding. I guess it's just an example but if I was asked this question i would say: "Come on, get me real things"

Determining the length of an array for memory efficiency

Write a function in C language that:
Takes as its only parameter a sentence stored in a string (e.g., "This is a short sentence.").
Returns a string consisting of the number of characters in each word (including punctuation), with spaces separating the numbers. (e.g., "4 2 1 5 9").
I wrote the following program:
int main()
{
char* output;
char *input = "My name is Pranay Godha";
output = numChar(input);
printf("output : %s",output);
getch();
return 0;
}
char* numChar(char* str)
{
int len = strlen(str);
char* output = (char*)malloc(sizeof(char)*len);
char* out = output;
int count = 0;
while(*str != '\0')
{
if(*str != ' ' )
{
count++;
}
else
{
*output = count+'0';
output++;
*output = ' ';
output++;
count = 0;
}
str++;
}
*output = count+'0';
output++;
*output = '\0';
return out;
}
I was just wondering that I am allocating len amount of memory for output string which I feel is more than I should have allocated hence there is some wasting of memory. Can you please tell me what can I do to make it more memory efficient?
I see lots of little bugs. If I were your instructor, I'd grade your solution at "C-". Here's some hints on how to turn it into "A+".
char* output = (char*)malloc(sizeof(char)*len);
Two main issues with the above line. For starters, you are forgetting to "free" the memory you allocate. But that's easily forgiven.
Actual real bug. If your string was only 1 character long (e.g. "x"), you would only allocate one byte. But you would likely need to copy two bytes into the string buffer. a '1' followed by a null terminating '\0'. The last byte gets copied into invalid memory. :(
Another bug:
*output = count+'0';
What happens when "count" is larger than 9? If "count" was 10, then *output gets assigned a colon, not "10".
Start by writing a function that just counts the number of words in a string. Assign the result of this function to a variable call num_of_words.
Since you could very well have words longer than 9 characters, so some words will have two or more digits for output. And you need to account for the "space" between each number. And don't forget the trailing "null" byte.
If you think about the case in which a 1-byte unsigned integer can have at most 3 chars in a string representation ('0'..'255') not including the null char or negative numbers, then sizeof(int)*3 is a reasonable estimate of the maximum string length for an integer representation (not including a null char). As such, the amount of memory you need to alloc is:
num_of_words = countWords(str);
num_of_spaces = (num_of_words > 0) ? (num_of_words - 1) : 0;
output = malloc(num_of_spaces + sizeof(int)*3*num_of_words + 1); // +1 for null char
So that's a pretty decent memory allocation estimate, but it will definitely allocate enough memory in all scenarios.
I think you have a few other bugs in your program. For starters, if there are multiple spaces between each word e.g.
"my gosh"
I would expect your program to print "2 4". But your code prints something else. Likely other bugs exist if there are leading or trailing spaces in your string. And the memory allocation estimate doesn't account for the extra garbage chars you are inserting in those cases.
Update:
Given that you have persevered and attempted to make a better solution in your answer below, I'm going to give you a hint. I have written a function that PRINTs the length of all words in a string. It doesn't actually allocate a string. It just prints it - as if someone had called "printf" on the string that your function is to return. Your job is to extrapolate how this function works - and then modify it to return a new string (that contains the integer lengths of all the words) instead of just having it print. I would suggest you modify the main loop in this function to keep a running total of the word count. Then allocate a buffer of size = (word_count * 4 *sizeof(int) + 1). Then loop through the input string again to append the length of each word into the buffer you allocated. Good luck.
void PrintLengthOfWordsInString(const char* str)
{
if ((str == NULL) || (*str == '\0'))
{
return;
}
while (*str)
{
int count = 0;
// consume leading white space
while ((*str) && (*str == ' '))
{
str++;
}
// count the number of consecutive non-space chars
while ((*str) && (*str != ' '))
{
count++;
str++;
}
if (count > 0)
{
printf("%d ", count);
}
}
printf("\n");
}
The answer is: it depends. There are trade-offs.
Yes, it's possible to write some extra code that, before performing this action, counts the number of words in the original string and then allocates the new string based on the number of words rather than the number of characters.
But is it worth it? The extra code would make your program longer. That is, you would have more binary code, taking up more memory, which may be more than you gain. In addition, it will take more time to run.
By the way, you have a memory leak in your program, which is more of a problem.
As long as none of the words in the sentence are longer than 9 characters, the length of your output array needs only to be the number of words in the sentence, multiplied by 2 (to account for the spaces), plus an extra one for the null terminator.
So for the string
My name is Pranay Godha
...you need only an array of length 11.
If any of the words are ten characters or more, you'll need to calculate how many extra char your array will need by determining the length of the numeric required. (e.g. a word of length 10 characters clearly requires two char to store the number 10.)
The real question is, is all of this worth it? Unless you're specifically required (homework?) to use the minimal space required in your output array, I'd be minded to allocate a suitably large array and perform some bounds checking when writing to it.

Resources