Confused with the char array when scanf - c

I am confused with one tiny program.
#include <stdio.h>
#define LEN 10
int main()
{
char str1[LEN] = "\0";
char str2[LEN] = "\0";
scanf("%s", str1);
scanf("%s", str2);
printf("%s\n", str1);
printf("%s\n", str2);
return 0;
}
If my input are:
mangobatao
mangobatao123456
Why should the output be:
123456
mangobatao123456
And not:
mangobatao
mangobatao123456
How has the char array has been allocated in the memory?

Well, a 10 character char array won't fit "mangobatao", since it has 10 characters - there's no room for the null terminator. That means you've caused undefined behaviour, so anything could happen.
In this case, it looks like your compiler has laid out str2 before str1 in memory, so when you call scanf to fill str2, the longer string overwrites the beginning of str1. That's why you see the end of what you think should be in str2 when trying to print str1. Your example will work fine if you use a length of 100.

I think your compiler has allocated space for str2[10] just 10 characters before the str1 pointer.
Now, when you scanf a string of length 16 at str2, the string terminator '\0' is appended at str2 + 17th position, which is infact str1 + 7.
Now when you call printf at str1, the characters read are actually str2 + 11, str2 + 12,..., str2 + 16 until the null terminator is encountered at str2 + 17 (or str1 + 7).
The printf at str2 must be obvious.

Related

Weird characters when concatenating strings in c [duplicate]

This question already has answers here:
What is the value of an uninitialized string in c? [duplicate]
(6 answers)
Closed 1 year ago.
I'm trying to concatenate multiple strings to create a longer phrase but I get weird, seemingly random, characters in front of the phrase when I try to print it. Here is the code:
char* str2 = argv[2];
int len2 = strlen(str2);
char* str3 = argv[3];
int len3 = strlen(str3);
printf("child PID(%d) receives Y = '%s' and Z= '%s' from the pipe\n",getpid(), str2, str3 );
//get the length of arguments 2 and 3 and create an array that is 1 larger than that (for the space between the two phrases)
//then concatenate them
int catLen = len2+len3;
char conc[catLen + 1];
strcat(conc, str2);
strcat(conc," ");
strcat(conc, str3);
printf("child PID(%d) concatenates Y and Z to generate Y' = '%s'\n",getpid(),conc);
int len1;
//get the length of the first command-line argument in the pipe
read(port[0],&len1,sizeof(len1));
//get the first command-line argument from the pipe
char str1[len1];
read(port[0], &str1,len1);
printf("child PID(%d) reads X from the pipe = '%s'\n",getpid(),str1);
//problems start when concatenating strings here
int totalLen = len1+catLen+1;
char phrase[totalLen];
strcat(phrase, str1);
printf("%s\n",phrase);
strcat(phrase," ");
printf("%s\n",phrase);
strcat(phrase,conc);
printf("child PID(%d) concatenates X and Y' to generate Z' = '%s'\n",getpid(),phrase);
I only encounter this issue when I try and concatenate the second set of strings, and I don't understand why. What is different between how I did it earlier and at the bottom the program?
First of all,
as Ptit Xav points out, you need one additional byte of space for conc. You need len1 bytes for the first string, plus 1 for the space, plus len2 for the second string, and then one more for the terminating null character. So if catLen = len1 + len2, then you should declare char cond[catLen + 2]; (or change the calculation of catLen in some equivalent way).
Next, when you declare a local array like char conc[catLen + 2]; without initializing it, it contains garbage. If you then attempt to strcat onto it, you concatenate onto garbage. If the garbage happens not to contain any null bytes, then strcat will overrun the array searching for a null byte, causing undefined behavior (which is very bad).
So either make your first use a strcpy instead of strcat:
char conc[catLen + 2];
strcpy(conc, str2); // note strcpy here
strcat(conc," ");
strcat(conc, str3);
Or else set the first character of your array to be a null character before starting, so that it becomes an empty string:
char conc[catLen + 2];
conc[0] = '\0'; // conc now contains an empty string
strcat(conc, str2);
strcat(conc," ");
strcat(conc, str3);
You can also initialize the array:
char conc[catLen + 2] = "";
strcat(conc, str2);
strcat(conc," ");
strcat(conc, str3);
However this will cause the program to fill the entire array conc with null bytes, where you really only needed one, so it is inefficient.

Reading multiple strings using sscanf() based on a delimiter

I have string having multiple words separated with commas like
char str[]="K&R,c89,c99,c11";
I am trying to read the first 2 words into a separate character arrays using sscanf().
sscanf(str, "%[^,] s%[^,]s", str1, str2);
I intended sscanf() to scan through str till reaching a ,, store it to str1, continue scanning till another , and store into str2.
But value is being stored only into str1 while str2 seem to be having garbage.
I tried removing the space between the %[^,]ss if that was of any significance but it made no difference on the output.
What am I doing wrong? Or is this not possible for multiple words?
I've heard of doing something like this with strtok() but I was wondering if sscanf() could be used for this.
Duh.. It took me a while to see it. Get rid of the s in your format string. The character class [...] takes the place of s and by putting s in there, you are forcing sscanf to look for a literal s in str, e.g.
#include <stdio.h>
#define MAX 8
int main (void) {
char str[]="K&R,c89,c99,c11";
char str1[MAX] = "";
char str2[MAX] = "";
if (sscanf(str, "%[^,],%[^,]", str1, str2) == 2)
printf ("str1 : %s\nstr2 : %s\n", str1, str2);
return 0;
}
Example Use/Output
$ ./bin/sscanfcomma
str1 : K&R
str2 : c89
Also, consider protecting your arrays from overflow with, e.g.
if (sscanf(str, "%7[^,],%7[^,]", str1, str2) == 2)

Accessing a specific character in a char pointers string in C

I'm using scanf to read a string and put it into a char*.
scanf("%s", &str);
printf("%c",str);
It works fine but I don't know how to access a specific character in that string as I would with a char[]. I have been trying to increment the address:
str += 1;
I have been messing around a bit but all I get is the first character or segfaults or bus errors. Is it possible to work with that string without copying into a char[]?
1)
It works fine but I don't know how to access a specific character in that string as I would with a char[]
Just dereference the pointer-array with [] and access the character at position "i"
str[i]
2)
I have been messing around a bit but all I get is the first character or segfaults or bus errors
Yes - the arrayname itself is a pointer to the first character of the string
If str is declared as char * then this statement
scanf("%s", &str);
is incorrect. There has to be
scanf("%s", str);
And the next statement
printf("%c",str);
is also incorrect. There has to be
printf("%c", *str);
If you want to output some other character in the character array using pointer char * then you can write either
printf( "%c", *( str + n ) );
or
printf( "%c", str[n] );
or even as
printf( "%c", n[str] );
where n some integer value that less then strlen( str )
Take into account that str shall point to some allocated memory. Otherwise the program will have undefined behaviour.

Size definition of strcat() function

The question is why should I define size of string (string[] should be string[some-number])
When the program is as following it gives me Abort trap: 6:
#include <stdio.h>
#include <string.h>
int main(void)
{
char buffer1[] = "computer";
char string[]="program";
strcat( buffer1, string );
printf( "buffer1 = %s\n", buffer1 );
}
This is the program from http://www.tutorialspoint.com/cprogramming/c_data_types.htm it works fine:
#include <stdio.h>
#include <string.h>
int main ()
{
char str1[12] = "Hello";
char str2[12] = "World";
char str3[12];
int len ;
/* copy str1 into str3 */
strcpy(str3, str1);
printf("strcpy( str3, str1) : %s\n", str3 );
/* concatenates str1 and str2 */
strcat( str1, str2);
printf("strcat( str1, str2): %s\n", str1 );
/* total lenghth of str1 after concatenation */
len = strlen(str1);
printf("strlen(str1) : %d\n", len );
return 0;
}
What is the mistake? Even if I define all of the sizes of strings in my program, my code still gives Abort trap:6?
From the man page of strcat:
DESCRIPTION
The strcat() function appends the src string to the dest string, overwriting the termiā€
nating null byte ('\0') at the end of dest, and then adds a terminating null byte. The
strings may not overlap, and the dest string must have enough space for the result. If
dest is not large enough, program behavior is unpredictable; buffer overruns are a
favorite avenue for attacking secure programs.
When you declare your string, the compiler allocate the size of your initial string to be 9 (resp. 8) for the buffer1 (resp. string) (includin '\0').
Thus, strcat will result in 9 - 1 + 8 (i.e. 16 bytes) but only 9 are available.
Your strcat is buffer overflowing buffer1 which can hold only strlen("computer")+1 bytes. ommitting array size does not mean "dynamic" array! When you specify the size of the array, you are reserving as many bytes as you want: again you need to avoid bufferoverflow of course.
So,
strcpy(str3, str1);
and
strcat( str1, str2);
are ok since str3 size is enough for str1, and str1 is enough for strlen(str1) + strlen(str2) + 1, i.e. exactly 11: 5 (hello) + 5 (world) + 1 (terminator). The magic number 12 was choosen with a reason, big enough to hold both strings and a terminator.
About C strings
C-strings are array of chars where the last is "null", '\0', i.e. they are array of chars where the last one is 0. This terminator is needed so that string related functions can understand where the string ends.
If it happens that a null byte is found in the middle of a string, from the point of view of C string functions, the string will end at that point. E.g.
char buffer1[] = "computer\0program";
// array: { 'c', 'o', ... '\0', 'p', 'r', 'o', .., 'm', '\0' }
// ...
printf("%s\n", buffer1);
will print computer only. But at this point the buffer will be big enough to hold computer and program, a terminator (and another extra byte), since the compiler computed the size of the char array considering the literal sequence of characters which syntactically ends at the second ".
But for all C-string functions, the string contained in buffer1 is computer. Note also that sizeof buffer1 will give the correct size of the buffer, i.e. 17, opposed to the result of strlen(buffer1) which is just 8.
The first parameter of strcat is used to store the result, so it must have enough space for the concatenated string.
In your code:
char buffer1[] = "computer";
is equivalent to:
char buffer1[9] = "computer";
defines a char array with just enough space for the string "computer", but not enough space for the result.
char buffer1[] = "computer";
Creates a buffer big enough to hold 9 characters (strlen("Hello" + 1 byte for \0)). If you write anymore data to it what you end up with is Undefined behavior (UB). This is what happens when you do a strcat.
UB means the program might crash or show literally any behavior. You are rather lucky that a program with UB crashes because it does not need to, but if it does atleast there is a indication of something wrong in it. Most of the times programs with UB will continue running correctly and crash when you least expect or want them to.

Difference between strncpy and memcpy?

How can I access s[7] in s?
I didn't observe any difference between strncpy and memcpy. If I want to print the output s, along with s[7] (like qwertyA), what are the changes I have to made in the following code:
#include <stdio.h>
#include <stdlib.h>
int main() {
char s[10] = "qwerty", str[10], str1[10];
s[7] = 'A';
printf("%s\n", s);
strncpy(str, s, 8);
printf("%s\n", str);
memcpy(str1, s, 8);
printf("%s\n", str1);
return 0;
}
Output:
qwerty
qwerty
qwerty
Others have pointed out your null-termination problems. You need to understand null-termination before you understand the difference between memcpy and strncpy.
The main difference is that memcpy will copy all N characters you ask for, while strncpy will copy up to the first null terminator inclusive, or N characters, whichever is fewer.
In the event that it copies less than N characters, it will pad the rest out with null characters.
You are getting the output querty because the index 7 is incorrect (arrays are indexed from 0, not 1). There is a null-terminator at index 6 to signal the end of the string, and whatever comes after it will have no effect.
Two things you need to fix:
Change the 7 in s[7] to 6
Add a null-terminator at s[7]
The result will be:
char s[10] = "qwerty";
s[6] = 'A';
s[7] = 0;
Original not working and fixed working.
As for the question of strncpy versus memcpy, the difference is that strncpy adds a null-terminator for you. BUT, only if the source string has one before n. So strncpy is what you want to use here, but be very careful of the big BUT.
Strncpy will copy up to NULL even you specified the number of bytes to copy , but memcpy will copy up to specified number of bytes .
printf statement will print up to NULL , so you will try to print a single charater , it will show ,
printf("\t%c %c %c\t",s[7],str[7],str1[7]);
OUTPUT
7 7
To make "qwertyA" you need to set s[6] = 'A' and s[7]='\0'
Strings are indexed from 0, so s[0] == 'q', and they need to be null terminated.
When you have:
char s[10] = "qwerty";
this is what that array contains:
s[0] 'q'
s[1] 'w'
s[2] 'e'
s[3] 'r'
s[4] 't'
s[5] 'y'
s[6] 0
s[7] 0
s[8] 0
s[9] 0
If you want to add an 'A' to the end of your string, that's at index 6, since array indexes start at 0
s[6] = 'A';
Note that when you initialize an array this way, the remaining space is set to 0 (a nul terminator), While not needed in this case, generally be aware that you need to make your strings nul terminated.
e.g.
char s[10];
strcpy(s,"qwerty");
s[6] = 'A';
s[7] = 0;
In the above example "qwerty" , including its nul terminator is copied to s. s[6] overwrites that nul terminator. Since the rest of s is not initialized we need to add a nul terminator ourselves with s[7] = 0;
As explained by Philip Potter, the main difference is that memcpy will copy all N characters you ask for, while strncpy will copy up to the first null terminator inclusive, or N characters, whichever is fewer. In the event that it copies less than N characters, it will pad the rest out with null characters.
Execute the below code and check the difference, you might find it useful.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char s[10] = "qwer\0ty", str[10], str1[10];
s[7] = 'A';
printf("%s\n",s);
strncpy(str,s,8);
printf("%s\n",str);
memcpy(str1,s,8);
printf("%s\n",str1);
for(int i = 0; i<8; i++)
{
printf("%d=%c,",i,str[i]);
}
printf("\n");
for(int i = 0; i<8; i++)
{
printf("%d=%c,",i,str1[i]);
}
return 0;
}
Output:
qwer
qwer
qwer
0=q,1=w,2=e,3=r,4=,5=,6=,7=,
0=q,1=w,2=e,3=r,4=,5=t,6=y,7=A,

Resources