I'm working on a problem about modifying strings with dynamic memory allocation. The applicable parts of my code are as follows:
./dma 5
#include <stdio.h>
#include <stdlib.h>
char* strcopy(char* destination, char* source);
char *strconcat(char* destination, char* source);
int main(int argc, char *argv[]) {
int cmd, a=1, b, length_of_str, n, n2;
char* pstring[atoi(argv[1])];
for (b=0; b<atoi(argv[1]); b++) {
printf("Enter the length of string %d: ", b+1);
scanf("%d", &length_of_str);
pstring[b]=(char *)malloc(length_of_str*sizeof(char));
printf("Please enter string %d: ", b+1);
scanf("%s", &pstring[b]);
}
while (a!=0) {
printf("Your strings are: \n");
for (b=0; b<atoi(argv[1]); b++) {
printf("String number %d - \"%s\"\n", b+1, &pstring[b]);
}
printf("Options:\n");
printf("1 - Find string length\n");
printf("2 - Compare strings\n");
printf("3 - Copy strings\n");
printf("4 - Concatenate strings\n");
printf("5 - Quit\n");
printf("Please enter your option: ");
scanf("%d", &cmd);
switch (cmd) {
case 3:
printf("Enter the number of the source string: ");
scanf("%d", &n);
printf("Enter the number of the destination string: ");
scanf("%d", &n2);
strcopy(pstring[n-1], pstring[n2-1]);
break;
case 4:
printf("Enter the number of the source string: ");
scanf("%d", &n);
printf("Enter the number of the destination string: ");
scanf("%d", &n2);
strconcat(pstring[n-1], pstring[n2-1]);
break;
case 5:
a=0;
break;
default:
printf("Invalid Option.\n");
break;
}
}
free(pstring);
return 0;
}
char* strcopy(char* destination, char* source) {
destination=(char *)realloc(*source, sizeof(char)*strlength(destination));
for (; *source!='\0'; source++) {
*destination=*source;
destination++;
}
*destination='\0';
return destination;
}
char* strconcat(char* destination, char* source) {
destination=(char *)realloc(*source, sizeof(char)*strlength(destination));
for (; *destination!='\0'; destination++) {
}
for (; *source!='\0'; source++) {
*destination=*source;
destination++;
}
*destination='\0';
return destination;
}
I need to incorporate realloc into my concatenation and copy functions (which should be fine since they worked in a separate problem). I've tried a number of ways and I've tried different syntax but I only seem to get segmentation faults or invalid pointers. How exactly am I supposed to incorporate realloc? The intended result should look like this:
Your strings are:
String number 1 – “first”
String number 2 – “second”
String number 3 – “third”
String number 4 – “fourth”
String number 5 – “fifth”
Options:
1 – Find string length
2 – Compare strings
3 – Copy strings
4 – Concatenate strings
5 – Quit
Please enter your option: 3
Enter the number of the source string: 2
Enter the number of the destination string: 5
Your strings are:
String number 1 – “first”
String number 2 – “second”
String number 3 – “third”
String number 4 – “fourth”
String number 5 – “second”
Options:
1 – Find string length
2 – Compare strings
3 – Copy strings
4 – Concatenate strings
5 – Quit
Please enter your option:
One major problem is how you read the string:
scanf("%s", &pstring[b])
Here pstring[b] is of type char *, and due to the malloc call it points to some memory (unless malloc fails, which you forget to check for).
But &pstring[b] is a pointer to the pointer, and is of type char **. This is hardly what you want, and will cause scanf to write to memory somewhere it wasn't supposed to write, and can even write out of bounds of allocated memory. This of course leads to undefined behavior.
Once you solve that, you need to remember that char strings in C are really called null-terminated byte strings. The null-terminator is what all standard string functions look for to find the end of the string. Of course, that means that a string for x characters needs space for x + 1 to fit the terminator. So if the user says he or she want a string of length 6 (for example) and then give foobar as input, that needs space for 7 characters with the terminator. This terminator problem (or rather missing to allocate space for it) you have in other places as well (like the strcopy function).
Your scanf call also doesn't hinder the user to input a string longer that the user said. If the user said that the string should be 3 characters, but then enter foobar, that will write out of bounds. Unfortunately there's no way to solve this with only scanf, as the field width modifier must be part of the format string. You could use the scanf_s function which solves this problem, but it's not mandated by the C specification, and needs you to define a specific macro for it to be available if the implementation have it (see e..g this scanf and family reference for details). Otherwise you need to programatically construct the format string to include a field width.
You also do free(pstring) which is invalid, since you didn't allocate pstring itself, it's an array. You do need to loop over all the strings in the array pstring and free them. Trying to pass a pointer not returned by malloc (et al) to free also leads to undefined behavior.
Lastly, in C you should not cast the result of malloc (and related functions).
Related
I created a struct datatype 'ans' that contains three string datatype member variables a[2],b[2],c[2]. Inside main, I created a struct variable 'p' to accept the three string inputs and then pass it to a function - void f1(ans *x) via call by reference to print the strings. Now in the function, instead of printing the three separate strings (*x).a,(*x).b,(*x).c, it is printing the whole string joined together. I am attaching the code and output for reference:
#include <stdio.h>
typedef struct
{
char a[2];
char b[2];
char c[2];
} ans;
void f1(ans *x) {
printf("The strings are :\n");
printf("%s\n",(*x).a);
printf("%s\n",(*x).b);
printf("%s\n",(*x).c);
}
int main() {
ans p;
printf("Enter for a:\n");
scanf("%s", p.a);
printf("Enter for b:\n");
scanf("%s", p.b);
printf("Enter for c:\n");
scanf("%s", p.c);
f1(&p);
return 0;
}
Sample output:
Enter for a:
ab
Enter for b:
cd
Enter for c:
ef
The strings are :
abcdef
cdef
ef
Can anyone explain why is this showing as output instead of the following:
The strings are:
ab
cd
ef
I can't figure out what's happening :(
In scanf("%s", p.a);, scanf reads characters and writes them to the memory pointed to by p.a. It also writes a terminating null character after them. Since the a member of the structure is declared as char a[2];, when scanf writes more than two characters, including the terminating null, the behavior is not defined by the C standard.
In printf("%s\n",(*x).a);, for %s, printf takes a pointer to a char and prints the characters it finds there until a terminating null character marks the end of the string. When there is no terminating null character the array that is (*x).a, printf overruns the array, and the behavior is not defined by the C standard.
To fix the problem, ensure there is enough space in the arrays for all the characters to be written into them, including the terminating null character, or ensure that no more characters are written in the arrays than will fit.
if you enter "ab", scanf will scan string as "ab\0" which is 3 characters, therefore your a[2] won't fit as same as other variabls.
To prevent scanf from overruns the array, a simple adaptation of your program, from the several that can be suggested:
#define MAXCH 3
typedef struct
{
char a[MAXCH];
char b[MAXCH];
char c[MAXCH];
} ans;
void f1(ans *x)
{
printf("The strings are :\n");
printf("%s\n",(*x).a);
printf("%s\n",(*x).b);
printf("%s\n",(*x).c);
}
int main()
{
ans p;
char in[128];
printf("Enter for a:\n");
scanf("%s",in);
snprintf(p.a,MAXCH,"%s",in);
printf("Enter for b:\n");
scanf("%s",in);
snprintf(p.b,MAXCH,"%s",in);
printf("Enter for c:\n");
scanf("%s",in);
snprintf(p.c,MAXCH,"%s",in);
f1(&p);
return 0;
}
Sample output:
Enter for a:
abcdefgh
Enter for b:
cdefghij
Enter for c:
efghijkl
The strings are :
ab
cd
ef
please take a look at the code below.
#include <stdio.h>
#include <conio.h>
struct str {
char st[1];
char rule[20];
} production_rules[30];
int main () {
int n;
printf("Enter number of productions: ");
scanf("%d", &n);
printf("Enter the productions\n");
for (int i = 0; i < n; i++) {
printf("Enter the non terminal: ");
scanf("%s", production_rules[i].st);
printf("Enter the RHS of the production Rule: ");
scanf("%s", production_rules[i].rule);
}
printf("the production rules are \n");
for (int i = 0; i < n; i++) {
printf("%s -> %s\n", production_rules[i].st, production_rules[i].rule);
}
return 0;
}
I am getting the following output
Enter number of productions: 1
Enter the productions
Enter the non terminal: A
Enter the RHS of the production Rule: abc
the production rules are
Aabc -> abc
Expected Output:
Enter number of productions: 1
Enter the productions
Enter the non terminal: A
Enter the RHS of the production Rule: abc
the production rules are
A -> abc
The problem is in the last line of the output. I don't understand why the char array is being concatenated. Can some one help me with this problem
There is no issue in your struct, but just the way you use printf, as you put %s to print the element "st", whilst you should use "%c" instead.
In fact, "st" is just a char[1], not a proper string, so it doesn't contain the string termination character '\0'.
As your struct is stored in memory as a buffer of consecutive char, the "%s" makes the "printf" stop when the termination string character is found, so at the end of the element "rule", and that's reason of your output.
So, just replace %s with %c when printf of st and it will work. Your code should appear like this:
for (int i = 0; i < n; i++) {
printf("%c -> %s\n", production_rules[i].st, production_rules[i].rule);
}
As mentioned in the comments, the problem was: not having a sufficient number of rooms in that character array. It is because the null-terminator character is always put at the end of the string, so it requires extra space.
In the current situation, your requirement is only a single character. So, you can change it into a char in the struct definition:
struct str {
char st;
char rule[20];
} production_rules[30];
And use it this way:
scanf(" %c", &production_rules[i].st);
Notice that an extra whitespace character is given here because it is necessary. Otherwise, it would simply ignore the input from being given.
Another method to solve this issue is to increase the length of the array by one. Suppose, you need 3 numbers as char[] then you need a length of 4 (extra one).
I want the output to print the data that we print. but it is not working as expected and the output is not displaying and it is exiting
#include <stdio.h>
int main() {
char name[20], department[3], section[1];
printf("enter the name of the student:");
scanf("%s", name);
printf("enter your department:");
scanf("%s", department);
printf("enter the section");
scanf("%s", section);
printf("Name:%s \n Department:%s \n Section: %s ", name, department, section);
return 0;
}
Your program has undefined behavior because the arrays are too short and scanf() stores the user input beyond the end of the arrays, especially the last one, section that can only contain an empty string which scanf() cannot read anyway.
Make the arrays larger and tell scanf() the maximum number of characters to store before the null terminator, ie: the size of the array minus 1.
Here is a modified version:
#include <stdio.h>
int main() {
char name[50], department[50], section[50];
printf("enter the name of the student:");
if (scanf("%49s", name) != 1)
return 1;
printf("enter your department:");
if (scanf("%49s", department) != 1)
return 1;
printf("enter the section");
if (scanf("%49s", section) != 1)
return 1;
printf("Name:%s\n Department:%s\n Section: %s\n", name, department, section);
return 0;
}
Note that using scanf with a %s conversion requires that each data item be a single word without embedded spaces. If you want name, department and section to accommodate spaces, which is more realistic for anyone besides Superman Krypton A, you would use %[\n] with an initial space to skip pending whitespace and newlines (or fgets() but in another chapter):
#include <stdio.h>
int main() {
char name[50], department[50], section[50];
printf("enter the name of the student:");
if (scanf(" %49[^\n]", name) != 1)
return 1;
printf("enter your department:");
if (scanf(" %49[^\n]", department) != 1)
return 1;
printf("enter the section");
if (scanf(" %49[^\n]", section) != 1)
return 1;
printf("Name:%s\n Department:%s\n Section: %s\n", name, department, section);
return 0;
}
scanf(" %49[^\n]", name) means skip any initial whitespace, including pending newlines from previous input, read and store and bytes read different from newline, up to a maximum of 49 bytes, append a null byte and return 1 for success, 0 for conversion failure or EOF is end of file is reached without storing any byte. For this particular call, conversion failure can happen if there is an encoding error in the currently selected locale.
The problem is that you have not accounted for the null character. It should work with the following.
char name[20] , department[4] , section[2];
The reason this happens is that C requires an extra character for the null character \0 which tells the program when the string ends.
first of all you should respect the size of string ,so you should either convert section to char or increase the size of that string because you have here the problem of '\0' character...so the rule is : the size of string is the size what you need + 1 for '\0' NULL character
and her is two program i tried to Modification you program for two scenarios :
#include <stdio.h>
int main(){
/// any size you like just respect the NULL character
char name[20],department[4],section[23];
printf("enter the name of the student:");
scanf("%s",name);
printf("enter your department:");
scanf("%s",department);
printf("enter the section");
scanf ("%s",section);
printf("Name:%s \n Department:%s \n Section:%s ", name,department,section);
return 0;
}
and case of char :
#include <stdio.h>
int main(){
char name[20],department[4];
char section;
printf("enter the name of the student:");
scanf("%s",name);
printf("enter your department:");
scanf("%s",department);
printf("enter the section");
///don't forget this space before %c it is important
scanf (" %c",§ion);
printf("Name:%s \n Department:%s \n Section:%c ", name,department,section);
return 0;
}
I watched for a long time and finally found the problem.
This is problem: char section[1];.
You declared the size is too short.
It looks like this after you declared it: section[0] = '\0';.
If you scanf a, the array data like section[0] = 'a';, and then it automatically add '\0' somewhere, so you got a memory leaking.
So replace char section[1]; to char section[2];.
I will not insist in the reasons of the other answers (that state other problems in your code than the one you are asking for) but I'll limit my answer to the reasons you don't get any output before the first prompt (there's no undefined behaviour before the third call of printf if you have input short enough strings to not overflow the arrays --- the last is impossible as long as you input one char, because to input one char you heed at least space for two)
I want the output to print the data that we print. but it is not working as expected and the output is not displaying and it is exiting
stdio works in linebuffer mode when output is directed to a terminal, which means that output is written to the terminal in the following cases:
The buffer is filled completely. This is not going to happen with a sort set of strings.
There is a \n in the output string (which there isn't, as you want the cursor to remain in the same line for input as the prompt string)
As there is no \n in your prompts, you need to make printf flush the buffer at each call (just before calling the input routines) You have two ways of doing this.
Calling explicitly the function fflush(3), as in the example below:
printf("enter the name of the student:");
fflush(stdout); /* <-- this forces flushing the buffer */
if (scanf(" %49[^\n]", name) != 1)
return 1;
configuring stdout so it doesn't use buffers at all, so every call to printf forces a write to the standard output.
setbuf(stdout, NULL); /* this disables buffering completely on stdout */
/* ... later, when you need to print something */
printf("enter the name of the student:"); /* data will be printed */
if (scanf(" %49[^\n]", name) != 1)
return 1;
But use this facilities only when it is necessary, as the throughput of the program is degraded if you disable the normal buffering of stdio.
The problem I have is the following: I want to read from keyboard 3 strings(s1,s2,s3).What is the best way to do this without any problem and then I want to print those 3 strings.I give the 3 strings but in the print section I only get s2 and s3,but s1 is blank!
Here's my code guys!:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define M 4
int main(){
char buffer[255];
char s1[M],s2[M],s3[M+1];
printf("Give first string: ");
scanf("%s",buffer);
while(strlen(buffer) > 4){
printf("lenght of string must be <= 4\n");
printf("Give first String again : ");
scanf("%s",buffer);
}
strncpy(s1,buffer,5);
printf("Give second string: ");
scanf("%s",buffer);
while(strlen(buffer) > 4){
printf("lenght of string must be <= 4\n");
printf("Give second String again : ");
scanf("%s",buffer);
}
strncpy(s2,buffer,5);
printf("Give third string: ");
scanf("%s",buffer);
while(strlen(buffer) > 5){
printf("lenght of string must be <= 5\n");
printf("Give third String again : ");
scanf("%s",buffer);
}
strncpy(s3,buffer,5);
printf("First String : %s\n",s1);
printf("Second String : %s\n",s2);
printf("Third String : %s\n",s3);
return 1;
}
strncpy() doesn't null terminate the strings and you need to take care of it else your code will lead to undefined behavior.
Also
M=4
and you are copying 5 character which will lead to undefined behavior.
It's because your strings are not terminated. You need to allow for an extra space in each buffer for a '\0' character at the end of the string. Make all your buffers bigger by 1 and ether set the to 0 to start with
memset(&buf1, 0, sizeof(buf1));
or explicitly set the last index of that buffer to '\0'
#define M 4
...
char s1[M],s2[M],s3[M+1];
...
strncpy(s1,buffer,5)
In the code above (extracted from your program) you copy 5 bytes (or less) in a buffer of size 4. When buffer contains a string of size 4 (strlen(buffer) == 4), strncpy(s1, buffer, 5) copies 5 characters to s1 and the fifth character is the NUL character (the string terminator).
Because s1 has room for only 4 characters, the terminator is written in a memory that does not belong to s1 but to a different variable.
It's recommended to use strncpy() and not strcpy() when you deal with user input to avoid buffer overflows. However, what you accomplished here is exactly a buffer overflow.
The solution is simple and it involves two easy steps:
declare s1 and s2 as char [5] too;
use M everywhere, avoid the hardcoded constants in the code; using constants (#defines) allows easier change of the value, if needed:
...
char s1[M+1],s2[M+1],s3[M+1];
printf("Give first string: ");
scanf("%s",buffer);
while(strlen(buffer) > M){
printf("length of string must be <= %d\n", M);
printf("Give first String again : ");
scanf("%s",buffer);
}
strncpy(s1,buffer,M+1);
...
(repeat the changes above to `s2` and `s3`)
In your code,
strncpy(s1,buffer,5);
creates memory overrun , as s1 is having only 4 bytes allocated. This, in turn invokes undefined behaviour.
Same for
strncpy(s2,buffer,5);
Solution:
Either use #define M 5
or, keep #define M 4, and define your arrays like
char s1[M+1],s2[M+1],s3[M+1];
so that, there is enough space to store the terminating null. (\0)
This is a program to concatenate strings using malloc
#include<stdio.h>
#include<conio.h>
#include<stdlib.h>
char *sconcat(char *ptr1,char *ptr2);
void main()
{
char string1[20],string2[20],*ptr;
clrscr();
printf("enter string 1: ");
gets(string1);
printf("enter string 2: ");
gets(string2);
ptr=sconcat(string1,string2);
printf("output string : %s",ptr);
getch();
}
char *sconcat(char *ptr1,char *ptr2)
{
int len1,len2,i,j;
char *ptr3;
len1=strlen(ptr1);
len2=strlen(ptr2);
ptr3=(char *)malloc((len1+len2+1)*sizeof(char));
for(i=0;ptr1[i]!='\0';i++)
ptr3[i]=ptr1[i];
j=i;i=0;
for(;ptr2[j]!='\0';j++,i++)
ptr3[j]=ptr2[i];
ptr3[j]='\0';
return(ptr3);
}
output:
enter string 1 : this program does
enter string 2 : not give output
output string : this program does
What correction is needed to concatenate strings. When I use char string1[20],string2[20],*ptr; after void main(),
output:
enter string 1 : is this
enter string 2 : correct ?
output string : correct? ?
The test in your second for loop is incorrect; it should be ptr2[i] != '\0', not ptr2[j] != '\0'.
Several remarks on the code:
Don't cast the return value of malloc.
sizeof(char) is 1 by definition, so multiplying by sizeof(char) only makes the code harder to read.
Declare the parameters of sconcat as const char *, since they're not modifying the strings they receive.
malloc can return NULL; you must handle that case in your program, for example by displaying an error message and exiting.
gets is unsafe and will crash your program if the user enters more characters than were allocated. Replace gets(string) with fgets(string, sizeof(string), stdin), and getting rid of the trailing newline.
clrscr() and getch(), as well as the infamous <conio.h> header, are not standard C and are non-portable; avoid them in simple programs like this one.
You can more simply use strcat
printf("enter string 1: ");
gets(string1);
printf("enter string 2: ");
gets(string2);
strcat(string1,string2);
It would, however, change string1 so you might want to use strcpy too (to copy string1 to another string and then return it).