Novice programmer learning C, I'm encountering this 'segmentation fault (core dumped)' error while trying to run a for-loop with strcmp. I have seen questions on similar issues with strcmp, but they don't seem to address my problem. Here's the program I've written.
#include<stdio.h>
#include<string.h>
int main() {
char ftpstring[15];
printf("\nEnter valid ftp command > ");
fgets(ftpstring,15,stdin);
const char* ftp[] = { "ascii", "recv", "send", "rmdir", "mkdir" , "pwd", "ls", "cd", "status", "quit" };
for ( int i = 0; i <= 10; i++ ) {
int comparison;
comparison = strcmp(ftpstring, ftp[i]);
if (comparison == 0 ) {
printf("%s is a valid ftp command.", ftpstring);
break;
}
if(i == 10) {
printf("%s is NOT a valid ftp command.", ftpstring);
}
}
}
As you can see, this program tries to read user input to determine if it matches one of the predefined valid ftp commands, then return whether or not it does.
for ( int i = 0; i <= 10; i++ ) should be for ( int i = 0; i < 10; i++ )
The ftp array contains 10 strings, so the loop should be from 0 to 9 including.
More general solution could be
for ( int i = 0; i < sizeof(ftp)/sizeof(ftp[0]); i++ )
But it is better to define a macro
#define FTP_NUM_OF_COMMANDS 10
and define the ftp array as following:
const char* ftp[FTP_NUM_OF_COMMANDS] = { "ascii", "recv", "send", "rmdir", "mkdir" , "pwd", "ls", "cd", "status", "quit" };
In this case the compiler will also verify that you don't initialize it (by mistake) with more than 10 values. The for loop will look like this:
for ( int i = 0; i < FTP_NUM_OF_COMMANDS; i++ )
Also note that the following code should be moved outside the for loop
if(i == FTP_NUM_OF_COMMANDS) {
printf("%s is NOT a valid ftp command.", ftpstring);
}
i==FTP_NUM_OF_COMMANDS will never occur inside the loop itself, if that condition is true, the for loop should break. Make sure that you define i outside the for loop scope so it would be available after the for loop breaks.
You are making a comparison past the end of the array: the for loop should stop at 9, while yours goes past the end of the array.
Using 10 as a "magic number" is not a good choice, either: it is much better to have the compiler compute the size for you. Finally, it is better to use index after the loop to decide if the command has been found or not:
int index = -1;
for ( int i = 0 ; i != sizeof(ftp) / sizeof(*ftp) ; i++ ) {
if (!strcmp(ftpstring, ftp[i])) {
index = i;
break;
}
}
if (index == -1) {
printf("%s is NOT a valid ftp command.", ftpstring);
}
Related
This function is basically just supposed to compare 2 strings and return their ASCII difference if they are different. It works perfectly fine when I compile it with the GCC compiler, but when I run it through the online compiler that is used to upload our classes homework, I get this error message:
Error near line 98: Reading an uninitialized value from address 10290
Line 98 is marked in the below code. I am not quite sure what the problem is and how I'm supposed to fix it. Does anyone have an idea?
int stringCompare(char * pStr1, char * pStr2) {
int n = 100;
int difference;
for (int i = 0; i < n; i++) {
difference = pStr1[i] - pStr2[i]; // line 98
if (difference != 0) {
return difference;
}
}
return difference;
}
Your code can skip over EOLN, if string equals, and try to compare memory after end of lines. To fix this, you need instantly return, if both string equals, and you see EOLN char '\0' in both strings at position i. Try my fix:
int stringCompare(char * pStr1, char * pStr2) {
int n = 100;
int difference;
for (int i = 0; i < n; i++) {
difference = pStr1[i] - pStr2[i];
if (difference != 0 || pStr1[i] == '\0') {
return difference;
}
}
return difference;
}
The problem in your code is that you fail to check the real length of the strings before indexing them. You are iterating with i from 0 to 99, but you do not check for the NUL terminator (\0) that marks the end of a string and therefore your for loop goes beyond the end of the string resulting in undefined behavior accessing memory that is not supposed to (which is what the error is telling you).
The correct way to iterate over a string, is not to loop a fixed amount of cycles: you should start from index 0 and check each character of the string in the loop condition. When you find \0, you stop. See also How to iterate over a string in C?.
Here's a correct version of your code:
int stringCompare(char *pStr1, char *pStr2) {
size_t i;
for (i = 0; pStr1[i] != '\0' && pStr2[i] != '\0'; i++) {
if (pStr1[i] != pStr2[i])
break;
}
return pStr1[i] - pStr2[i];
}
You could even write this more concisely with a simple while loop:
int stringCompare(char *pStr1, char *pStr2) {
while (*pStr1 && *pStr1 == *pStr2) {
pStr1++;
pStr2++;
}
return *pStr1 - *pStr2;
}
Of course, both the above functions expect two valid pointers to be passed as arguments. If you also want to allow invalid pointers you should check them before starting the loop (though it does not seem like you want to do that from your code).
I am having issue with lower casing my words that are being used as inputs. So my program takes in words and sorts them alphabetically and removes duplicates. But I'd like to change words upper case and lower them to equal to lower case words.
example: Apple changes to apple
my input:
./a.out Orange apple banana Apple banana
my output:
Apple
Orange
apple
banana
Here is what I am trying to achieve
output:
apple
banana
orange
Here is my code
#include <stdio.h>
#include <string.h>
int main(int argc, char* argv[]) {
int i, j, k, size;
size = argc -1;
char *key;
char* a[argc-1];
for (i = 2; i < argc; i++) {
key = argv[i];
j = i-1;
while (j >= 1 && strcmp(argv[j], key) > 0) {
argv[j+1] = argv[j];
j--;
}
argv[j+1] = key;
}
if (argc > 1){
for (i = 1; i < argc;){
puts(argv[i]);
while (argv[++i] != NULL && strcmp(argv[i - 1], argv[i] ) == 0)
continue;
}
}
return 0;
}
You have a list of words and you want to output them sorted, and only the unique ones. And you want to do it in a case insensitive fashion.
Get all the strings to the same case.
Sort the list of strings.
Don't output repeats.
C has no built in function to lower case a string, but it does have ones to lower case characters: tolower. So we write a function to lower case a whole string by iterating through it and lower casing each character.
void str_lower(char *str) {
for( ; str[0] != NULL; str++ ) {
str[0] = (char)to_lower(str[0]);
}
}
Then we need to sort. That's handled by the built in qsort function. To use it, you need to write a function that compares two strings and returns just like strcmp. In fact, your comparison function will just be a wrapper around strcmp to make qsort happy.
int compare_strings( const void *_a, const void *_b ) {
/* The arguments come in as void pointers to the strings
and must be cast. Best to do it early. */
const char **a = (const char **)_a;
const char **b = (const char **)_b;
/* Then because they're pointers to strings, they must
be dereferenced before being used as strings. */
return strcmp(*a, *b);
}
In order to handle any data type, the comparison function takes void pointers. They need to be cast back into char pointers. And it's not passed the string (char *) it's passed a pointer to the string (char **), again so it can handle any data type. So a and b need to be dereferenced. That's why strcmp(*a, *b).
Calling qsort means telling it the array you want to sort, the number of items, how big each element is, and the comparison function.
qsort( strings, (size_t)num_strings, sizeof(char*), compare_strings );
Get used to this sort of thing, you'll be using it a lot. It's how you work with generic lists in C.
The final piece is to output only unique strings. Since you have them sorted, you can simply check if the previous string is the same as the current string. The previous string is strings[i-1] BUT be sure not to try to check strings[-1]. There's two ways to handle that. First is to only do the comparison if i < 1.
for( int i = 0; i < num_strings; i++ ) {
if( i < 1 || strcmp( strings[i], strings[i-1] ) != 0 ) {
puts(strings[i]);
}
}
Another way is to always output the first string and then start the loop from the second.
puts( strings[0] );
for( int i = 1; i < num_strings; i++ ) {
if( strcmp( strings[i], strings[i-1] ) != 0 ) {
puts(strings[i]);
}
}
This means some repeated code, but it simplifies the loop logic. This trade-off is worth it, complicated loops mean bugs. I botched the check on the first loop myself by writing if( i > 0 && strcmp ... )`.
You'll notice I'm not working with argv... except I am. strings and num_strings are just a bit of bookkeeping so I didn't always have to remember to start with argv[1] or use argv+1 if I wanted to pass around the array of strings.
char **strings = argv + 1;
int num_strings = argc-1;
This avoids a whole host of off-by-one errors and reduces complexity.
I think you can put the pieces together from there.
There are a set of standard functions for checking and changing the type of characters in ctype.h. The one you are interested in is tolower(). You can #include<ctype.h> and then add a snippet like the following to pre-process your argv before doing the sorting:
for(i = 1; i < argc; i++) {
argv[i][0] = tolower(argv[i][0]);
}
That will only operate on the first character of each word. If you need to normalize the entire word:
for(i = 1; i < argc; i++) {
for(j = 0; argv[i][j]; j++) {
argv[i][j] = tolower(argv[i][j]);
}
}
Silly me, I was able to figure it out after looking at my code realizing that i can do key[0] = tolower(key[0]); which i did before having a pointer point at it.
for (i = 2; i < argc; i++) {
key = argv[i];
key[0] = tolower(key[0]);
j = i-1;
while (j >= 1 && strcmp(argv[j], key) > 0) {
argv[j+1] = argv[j];
j--;
}
argv[j+1] = key;
}
Which lower cases the first letter. And if i wanted to lower case all the letters, i would've have used a for loop. Thank you everyone for your contribution. :)
I want to remove any spaces from the user input and give the result back on the screen. So far, the following is my working solution. I haven't noticed any errors yet. Since I'm pretty new to C and programming in general, my question is: Is there something I can do better? Anything to optimize or something? I appreciate any tips from you guys since you are probably a lot more experienced than I am. So, here's my code:
#include <stdio.h>
#include <string.h>
#define PUFFERGROESSE 100
#define ERROR 1
#define OK 0
int main(){
char stringPuffer[PUFFERGROESSE];
printf("Please enter some words:"); fflush(stdout);
if(fgets(stringPuffer, PUFFERGROESSE, stdin) == NULL){
printf("Unable to read.\n");
return ERROR;
} else {
char endString[PUFFERGROESSE];
for (int i = 0, j = 0; i < PUFFERGROESSE; i++, j++) {
if (stringPuffer[i] != ' ' ) {
endString[j] = stringPuffer[i];
} else {
j--;
}
}
printf("Without spaces your input looks like that: %s", endString);
}
}
In your code, the for loop condition is i < PUFFERGROESSE. Inside this loop, you access stringPuffer using the loop index.
Now, stringPuffer being an uninitialized automatic local variable and with a sufficiently small input, a strict check like i < PUFFERGROESSE will cause access to uninitialized memory of stringPuffer, creating undefined behavior.
You can make use of strlen() after taking the user input.
Another note, int main() is better as int main(void), at least.
NITPICK: why's the OK defined, if not used?
Several suggestions:
Initialize endString to all zeros; that way you won't have to worry about string termination issues later on:char endString[PUFFERGROESSE] = {0};
Instead of looping while i is less than PUFFERGROESSE, loop until you see the end of the string:for( int i = 0, j = 0; stringPuffer[i] != 0; i++ )
Also, only increment j when you write the non-space character, rather than incrementing it unconditionally and then having to decrement it when you see a space:if ( !isspace( stringPuffer[i] ) )
endString[j++] = stringPuffer[i];
So basically, that code reduces to:
char endString[PUFFERGROESSE] = {0};
for (int i = 0, j = 0; stringPuffer[i] != 0; i++) {
if ( !isspace( stringPuffer[i] ) ) {
endString[j++] = stringPuffer[i];
}
}
So, I have this code:
void stringCounter( char cp[], int counter[] ) {
char c;
for ( int i = 0; strlen(cp); i++ ) {
c = cp[i];
// Checking to see if the variable c is alphanumeric...
if ( isalnum(c) ) {
if ( c >= 'A' && c <= 'Z' ) {
counter[c - 'A']++;
} else {
counter[26]++;
}
}
}
return;
}
And I have a bad access error on line 5 (of my listed code). I really don't know how to fix this error! I have tried making things pointers, tried not making things pointers, but really can't get it. Usually, I can debug my programs fairly well, but I am really having trouble with this one.
Any help is appreciated.
Your condition should be i < strlen(cp), not just strlen(cp).
You are simply incrementing i without any boundary checking
for ( int i = 0; i< strlen(cp); i++ ) {
^^ fix this
Simple Question. Imagine this in ANSI-C:
int i;
for(i=0 ; i<5 ; i++){
//Something...
}
printf("i is %d\n", i);
Will this output "i is 5" ?
Is i preserved or is the value of i undefined after the loop?
Yes. If i is declared outside of the for loop it remains in scope after the loop exits. It retains whatever value it had at the point the loop exited.
If you declatred I in the loop:
for (int i = 0 ; i < 5 ; i++)
{
}
Then i is undefined after the loop exit.
Variable i is defined outside of the scope of the loop (which is great, or you wouldn't be able to print it in that case).
And it is post-icnremented for every-turn of the loop, for which the end condition is "stop when i is bigger or equal to 5".
So it really makes perfect sense for i to be equal to 5 at this point.
A block scope is not exactly the same as a function scope in C. The variable i doesn't "get back" magically to its previous value when you step out of the loop's scope.
i's value will be 5 after your loop. Unless you did something like
i = 50000;
inside of it.
It's also generally recommended against using "i" after you exit the loop in most coding standards I have ever read. In particular do NOT do:
for(i = 0; i < num_elements; i++)
{
if(element[i].id == id)
{
/* Do something to element here. */
break;
}
}
if(i == num_elements)
{
fprintf(stderr, "Failed to find element %d.", id);
succeeded == false;
}
While this will work it is poor coding. It is less readable and maintainable than the alternatives. E.g.
succeeded = false;
for(i = 0; i < num_elements; i++)
{
if(element[i].id == id)
{
/* Do something to element here. */
succeeded = true;
break;
}
}
if(false == succeeded)
{
fprintf(stderr, "Failed to find element %d.", id);
}
Yes, variables are valid only inside the block in which they are declared.
Here's an example:
#include <stdio.h>
void main(int argc, char *argv[])
{
if(argc == 2) {
int x;
x = 7;
}
x = 1;
}
That's the compiler:
gcc ex.c
ex.c: In function ‘main’:
ex.c:10: error: ‘x’ undeclared (first use in this function)
ex.c:10: error: (Each undeclared identifier is reported only once
ex.c:10: error: for each function it appears in.)