I am attempting to write a program that will take two sets of strings N and Q. The goal of the program is to print out the number of times each string in Q occurs in N. However, I am struggling to manage strings and pointers in C and in particular I believe my issue stems from attempting to have an array of strings. I get a segmentation fault when the code below is executed. I have commented out my attempts at debugging using printf(). I believe the issue occurs when I try to assign S into the N_array.
int main() {
int N, Q;
char *N_array[1000], *Q_array[1000];
scanf("%d", &N);
for (int N_i = 0; N_i < N; N_i++) {
//printf("made it through A for loop %d times\n", N_i+1);
scanf("%s", N_array[N_i]);
}
scanf("%d", &Q);
//Does the array contain any information?
//for (int N_i = 0; N_i < N; N_i++) { printf("N_array[%d] == %d\n", N_i, N_array[N_i]);}
for (int Q_i = 0; Q_i < Q; Q_i++) {
//printf("Made it to B for loop\n");
int occurs = 0, result;
char s[21];
scanf("%s", &s[21]);
strcpy(Q_array[Q_i], s);
for (int N_i2 = 0; N_i2 < N; N_i2++) {
//printf("Made it to C for loop\n");
result = strcmp(Q_array[Q_i], N_array[N_i2]);
if (result == 0) occurs++;
}
printf("%d", occurs);
}
return 0;
}
One problem is here
for (int N_i = 0; N_i < N; N_i++) {
//printf("made it through A for loop %d times\n", N_i+1);
scanf("%s", N_array[N_i]);
}
N_Array contains 1000 pointers to char, but each of those pointers points to, well.. nowhere. It's an uninitialized pointer which points to a random memory location you don't own. This is undefined behavior. You have to allocate memory before scanf.
N_Array[N_i] = malloc(max_length_of_string + 1);
Another problem is this line
char s[21];
scanf("%s", &s[21]);
The second parameter of scanf should be just s, not &s[21], which is just outside your array.
And one line below you have the same problem as described in my first point
strcpy(Q_array[Q_i], s);
Q_array[Q_i] doesn't yet point to any memory which you're allowed to write to. You should allocate memory here as well.
scanf("%s", N_array[N_i]);
This should lead to SegFault as expected as N_array[N_i] doesn't contain a valid memory address.
What you need is to allocate memory using malloc, calloc to point N_array[N_i] to a valid memory location.
Correct code would be:
for (int N_i = 0; N_i < N; N_i++)
{
if(!(N_array[N_i] = malloc((MAX_STRING_LENGTH + 1) * sizeof(char))))
{
printf("malloc failed!\n);
exit(1);
}
scanf("%s", N_array[N_i]);
}
scanf("%s", &s[21]);
This is incorrect as %s expects char * but type of &s[21] is char **.
You need to use scanf("%20s", s); instead.
This way scanf only read at most 20 chars (as we have char s[21]. So leaving last byte for \0 character) and thus this will avoid accidental buffer overrun (where the length of string entered by user is greater than 20).
strcpy(Q_array[Q_i], s);
Again here Q_array[Q_i] points to invalid memory address as it contains garbage value. Use malloc, calloc etc. to allocate memory first.
Correct code could be:
if(!(Q_array[Q_i] = malloc((MAX_STRING_LENGTH + 1) * sizeof(char))))
{
printf("malloc failed!\n);
exit(1);
}
strcpy(Q_array[Q_i], s);
Related
#include <stdio.h>
#include <stdlib.h>
char *ptr;
int n;
int main()
{
ptr = (char *)calloc(n, sizeof(char));
// First ID
printf("Enter the length of your employ ID\n");
scanf("%d", &n);
for (int i = 0; i <= n; i++)
{
scanf("%c", &ptr[i]);
}
for (int i = 0; i <= n; i++)
{
printf("%c", ptr[i]);
}
// Second ID
printf("Enter the size of new ID\n");
scanf("%d", &n);
ptr = (char *)realloc(ptr, n * sizeof(char));
for (int i = 0; i <= n; i++)
{
scanf("%c", &ptr[i]);
}
for (int i = 0; i <= n; i++)
{
printf("%c", ptr[i]);
}
// Third ID
printf("Enter the size of new ID\n");
scanf("%d", &n);
ptr = (char *)realloc(ptr, n * sizeof(char));
for (int i =0; i <=n; i++)
{
scanf("%c", &ptr[i]);
}
for (int i = 0; i <= n; i++)
{
printf("%c", ptr[i]);
}
return 0;
}
I tried to Get Ids of three people but the program doesnt work and after taking the input once it just exits : ( . It works fine when I use realloc once but not twice can someone explain why ?
it takes the input and then exits
The statement:
int n;
declares n at file scope. Objects declared at file scope have static storage duration and are always initialised. In the absence of an explicit initialization, they are implicitly initialised to zero.
From the C standard C11 6.7.9/10:
"... If an object that has static or thread storage duration is not
initialized explicitly, then:
— if it has pointer type, it is initialized to a null pointer;
— if it has arithmetic type, it is initialized to (positive or
unsigned) zero;"
Accessing out of bounds memory:
Then:
ptr = (char *)calloc(n, sizeof(char));
allocates memory for 0 objects.
The calloc() function allocates memory for an array of nmemb elements
of size bytes each and returns a pointer to the allocated memory. The
memory is set to zero. If nmemb or size is 0, then calloc() returns
either NULL, or a unique pointer value that can later be successfully
passed to free().
On error, these functions return NULL. NULL may also be returned by a
successful call to malloc() with a size of zero, or by a successful
call to calloc() with nmemb or size equal to zero.
But you didn't check the return value of calloc.
Then, this statement:
scanf("%c", &ptr[i]);
tries to access memory that wasn't allocated, and thus invokes undefined Behaviour.
Off by one error:
You have allocated space for n elements, but the condition:
i <= n
tries to access n + 1 elements, which is memory out of bounds, memory you haven't allocated, memory that doesn't belong to you, and is thus undefined behaviour. (But that's irrelevant since you didn't allocate anything in the first place).
Regarding realloc:
The realloc() function returns a pointer to the newly allocated
memory, which is suitably aligned for any kind of variable and may be
different from ptr, or NULL if the request fails. If size was equal to
0, either NULL or a pointer suitable to be passed to free() is
returned. If realloc() fails the original block is left untouched; it
is not freed or moved.
Which means that if it fails and returns NULL, then ptr gets initialised with NULL and you lose all access to the original memory.
One solution is to use another pointer:
char *new = realloc(ptr, size);
if (!new) { /* if realloc failed */
/* deal with it however you wish */
}
/* If we got here, it means that we weren't bounded */
ptr = new;. /* Now ptr points to the new memory, if it didn't already */
new = 0; /* This avoids a dangling pointer */
/* some code relevant to ptr here */
free(ptr); /* For every allocation, there must be a call to free */
Side note: You shouldn't cast the result of malloc and family. It's redundant and may hide a bug. The void * returned by these functions is automatically promoted to the correct type.
Trailing newline:
scanf("%d", &n);
leaves a newline in the input buffer, which automatically gets read by subsequent calls to scanf, and you might never be prompted for input.
Instead of:
scanf("%c");
Use " %c" with a leading blank to skip optional white space:
scanf(" %c");
First of all, please note that you're using:
ptr = (char *)calloc(n, sizeof(char));
when n isn't explicitly initialized in file scope so it is automatically initialized to 0. So basically you overwrite a buffer of size 0 in:
for (int i = 0; i <= n; i++)
{
scanf("%c", &ptr[i]);
}
while exceeding the size allocated with i <= n which sould be i < n, that is probably why.
Second, you must free your memory allocation at the end using free and check allocation's success.
Third, when using strings, you might want to use scanf using %s, unless you have a specific reason.
A more clear and less error prone implementation may be:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int size, times, i;
char *ptr;
printf("Enter the number of IDs to enter\n");
scanf("%d", ×);
for (i = 0; i < times; ++i) {
printf("Enter the size of new ID\n");
scanf("%d", &size);
if (size < 0) {
printf("Entered value is negative\n");
return size; // or any other error
}
// note the '+ 1' so we have a NULL terminating string
ptr = (char *)calloc(size + 1, sizeof(char));
if (ptr == NULL) {
printf("Error allocating ptr\n");
return -1; // or any other error
}
printf("Enter your ID\n");
scanf("%s", ptr);
printf("Your ID is: %s\n", ptr);
free(ptr);
}
return 0;
}
Hopefully I answered everything and didn't miss a thing (:
In what way is the following code faulty or undefined behaviour?
I suggested this as a possibility to create an array of strings, if string number and size are unknown beforehand, and after a short discussion, it was suggested that I open a new question.
The following code produced the expected results when I compiled it with gcc, but that can happen in spite of undefined behaviour (it's undefined after all).
So, what is the mistake?
int n, i;
printf("How many strings? ");
scanf("%d", &n);
char *words[n];
for (i = 0; i < n; ++i) {
printf("Input %d. string: ", i + 1);
scanf("%s", &words[i]);
}
for (i = 0; i < n; ++i) {
printf("%s\n", &words[i]);
}
Edit:
I feel stupid now for missing this, I guess getting the correct answer back just made me miss my error. But that others may learn from my mistake:
I guess I got completely wrong what the & operator does. I thought it would give me where words points to, but of course it does the exact opposite. See the answers.
scanf("%s", &words[i]); and printf("%s\n", &words[i]); invokes *undefined behavior because data having wrong type are passed.
In both of scanf() and printf(), %s requires char* pointing at valid buffer but what is passed are char**.
Also don't forget to allocate buffer to store strings before reading.
Try this:
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int n, i;
printf("How many strings? ");
scanf("%d", &n);
char *words[n];
for (i = 0; i < n; ++i) {
printf("Input %d. string: ", i + 1);
words[i] = malloc(1024001); /* allocate enough buffer */
if (words[i] == NULL) {
perror("malloc");
return 1;
}
scanf("%1024000s", words[i]); /* limit length to read to avoid buffer overrun */
}
for (i = 0; i < n; ++i) {
printf("%s\n", words[i]);
}
for (i = 0; i < n; ++i) {
free(words[i]); /* clean what is allocated */
}
return 0;
}
char *words[n];
creates an array of uninitialized pointers
scanf("%s", foo);
writes values to the position foo is pointing to
it is not specified where the pointers of words are pointing to so they could point anywhere which could result in a segfault
next words is a char**
words[i] is a char *
&words[i] is a char **
%s expects a char* so it's again undefined behavior what happens
so you first have to initialize your words arrays using for example malloc and then write the values to words[i]
This:
char *word;
is a pointer, Before it is used as a container for say a string, it needs to point to memory sufficient for the string.
for example, this will work
word = malloc(80*sizeof(*word));
if(word)
{//success, continue
Similar to above, this:
char *word[n];
extension is an an array of n pointers. Before any of the pointers can be used as a container for say some strings, each needs to point to its own memory location. Assuming the value n has been scanned in, this will work:
for(int i=0;i<n;i++)
{
word[i] = malloc(80*sizeof(*word[i]));//80 for illustration
if(!word[i])
{//handle error...
Once the memory is allocated, the strings can be populated.
However, to ensure user input does not overflow the buffer defined by each instance of word, use a width specifier in the format string:
Change:
scanf("%s", &words[i]);
To:
scanf("%79s", words[i]);//note & is not needed as the symbol for a char array serves as the address
// ^^ ^
I allocated a 2D array of characters, and while reading strings with no whitespaces between, the code is working fine. When I read them with whitespaces, I'm facing a bug. How do I read all N number of Strings, each in a single line, each one containing whitespaces.
Example input:
Enter total number of Strings : 3
Enter all the 3 Strings :
John Doe
Jane Doe
Trad Braversy
My code:
// Code to enter the total number of Strings :
int N;
printf("\n\tEnter the total number of Strings : ");
scanf("%d", &N);
// Code for allocating initial memory to them :
char** strings = (char**)malloc(N * sizeof(char*));
for (int i = 0; i < N; ++i) {
strings[i] = (char*)malloc(1024 * sizeof(char));
}
// Code for entering all the N strings :
printf("\n\tEnter all the %d Strings :\n", N);
for (int i = 0; i < N; ++i) {
gets(strings[i]);
}
// Code to reallocate the memory according to the entered length :
for (int i = 0; i < N; ++i) {
strings[i] = (char*)realloc(strings[i], strlen(strings[i]) + 1);
}
A few observations:
It's safer to read a full line of text, then parse out the integer from that, rather than doing scanf() for a single integer. This is because the latter leaves the newline in the stream, which can confuse later reads.
There's no real point in using malloc() to do dynamic memory allocation for this, you can use a VLA:
char strings[N][1024];
Note that using a capital-only symbol for a runtime variable is stylistically strange in C.
Then, it's much better to use fgets(), it's safer and just better:
for (int i = 0; i < N; ++i)
{
if (fgets(strings[i], sizeof strings[i], stdin) == NULL)
{
fprintf(stderr, "**Read error on string %d\n", i);
exit(1);
}
}
And as always, be prepared that I/O can fail, and try to handle that.
I'm making a program where user enters grades (1 to 5) and then the grade gets added to array for later inspection. When user enters letter "s", the program closes. When ran my program crashes, why?
#include <stdio.h>
#include <stdlib.h>
int i;
int grade[50];
char *num[20];
int enter();
int enter()
{
for (i=0; i<10; i++) {
printf("\nEnter grade:\nPress [s] to close program\n");
scanf("%s",&num[i]);
if (strcmp(num[i],"s") == 0) {
break;
} else {
grade[i] = atoi(num[i]);
}
}
}
int main()
{
enter();
for (i=0; i<10; i++) {
printf("\n%d",grade[i]);
}
return 0;
}
remove ' * ' from num[20] declaration, as you are declaring 20 character string pointers, so reading and comparing values with num[i] will cause error.
Besides, you just nead a simple string to get the grade.
The reason why program crashed is that num is a pointer array, the element of num can not pointer to valid memory which used to store the string you inputed.
you can change char *num[10] to char num[10][12] and 'scanf("%s", &num[i])to scanf("%s", num[i]), and that everything will be OK.
Of course, you can use malloc to dynamic alloc memory for each element in num, like:
`for(i = 0; i < 10; i ++){
num[i] = (char*)malloc(sizeof(char) * 12);
}
`
Even thought, you must change scanf("%s", &num[i]) to scanf("%s", num[i]);
Finally, you can not forget to free the memory you just dynamic malloc.
So basically, right now, this function can only take 9 words with 10 characters each. How do i make it so that it can take an arbitrary amount of words and characters and sort them accordingly in alphabetical order?
int sortText(){
char name[10][9], tname[10][9], temp[10];
int i, j, n;
printf("Enter the amount of words you want to sort (max 9):");
scanf("%d", &n);
printf("Enter %d words: ",n);
for (i = 0; i < n; i++)
{
scanf("%s", name[i]);
strcpy(tname[i], name[i]);
}
for (i = 0; i < n - 1 ; i++){
for (j = i + 1; j < n; j++){
if (strcmp(name[i], name[j]) > 0){
strcpy(temp, name[i]);
strcpy(name[i], name[j]);
strcpy(name[j], temp);
}
}
}
printf("\n------------------------------------------\n");
printf("%-3s %4s %11s\n", "Input","|", "Output");
printf("------------------------------------------\n");
for (i = 0; i < n; i++)
{
printf("%s\t\t%s\n", tname[i], name[i]);
}
printf("------------------------------------------\n");
}
You have two problems, that each needs to be solved separately, but they can still be solved in a similar way, namely using dynamic memory allocations and more importantly reallocation.
There are two important aspects to remember here, and the first is that a string is an array of characters (with a special terminating character) and that you can have a pointer to an array located anywhere in memory.
If we start with the data-types and how you should store your strings, what you want is an array of arrays, much like you have right now, but allocated dynamically which means you want an array of pointers (to the strings), but since the array of string also needs to be dynamic you need a pointer to an array which contains pointers to other arrays, i.e. a pointer to a pointer to char: char **.
Now when we know what data-type to use, lets think about how to allocate it. To allocate space for a single string in your array, you allocate one char * using the malloc function:
char **strings = malloc(1 * sizeof(char *));
That was the simple part. Now before we start reading the actual string, lets think about how to add a new string to your collection: This is done by reallocating the array of strings you have, using the realloc function:
char **temp_strings = realloc(strings, current_count + 1 * sizeof(char *));
if (temp_string == NULL)
{
// Allocation failed, handle error appropriately
}
strings = temp_strings;
++current_count;
Here the variable current_count is the current length of the array of strings, it should be initially initialized to 1 (as we only have a single string in the array).
Now for the reading of the actual strings, and this is a little more complicated since we actually can't read whole strings (since we don't know how long each line is). Instead we read one character at a time, and end when we hit a newline. We also need to reallocate the string for each character.
Maybe something like this:
int ch;
char *s = NULL;
size_t current_length = 0; // Current length of string
while ((c = fgetc(stdin)) != EOF)
{
if (c == '\n')
break; // Newline, done with the current string
if (s == NULL)
{
s = malloc(2); // Allocate two character: One for c and one for the terminator
}
else
{
// The current length is not including the terminator
// that's why we add two characters
char *temp_s = realloc(s, current_length + 2);
if (temp_s == NULL)
{
// Handle error
}
s = temp_s;
}
s[current_length++] = c;
s[current_length] = '\0'; // Terminate as a string
}
if (s != NULL)
{
// "Add" the string to the array of strings
strings[current_count] = s;
}
For C only you should use char pointer located dynamic.
You can make list by implement a linked list. Then strcmp still work well.
See about linked list here:
http://www.cprogramming.com/tutorial/c/lesson15.html