C - Comparing two characters - c

I am having trouble comparing two characters. I've written a very basic C problem to try out command line arguments.
Here is my code so far:
#include <stdio.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
char ch;
char *type = "";
char *gender = "";
int baby = 0;
int count = 0;
/* Options:
* -t = type of pet
* -g = gender
* -b = baby or adult
*/
while ((ch = getopt(argc, argv, "t:g:b")) != EOF)
switch (ch) {
case 't':
type = optarg;
break;
case 'g':
gender = optarg;
break;
case 'b':
baby = 1;
break;
default:
fprintf(stderr, "Invalid option.\n");
return 1;
}
argc -= optind;
argv += optind;
printf("You have chosen a %s.\n", type);
if (gender == 'f')
puts("It's a girl");
if (gender == 'b')
puts("It's a boy.");
// The main command line arguments should be about the traits of the pet
printf("%s", "Traits: ");
for (count = 0; count < argc; count++)
printf("%s ", argv[count]);
return 0;
}
So if I type this into the terminal:
$ ./pet_shop -t dog -g f cute small
I get this as output:
You have chosen a dog:
Traits: cute small
The output it missing information about the gender, it should be a girl since I entered f. But I tried checking by printf("%i", gender) which gave the value 0. Is g == 'f' the incorrect way of comparing two characters?

gender is a char*, i.e. a pointer to a string's first charadcter. When you compare that to a single char, both the char and the pointer are converted to integers and an integer comparison is done.
To compare strings, use strcmp from <string.h>:
if (strcmp(gender, "f") == 0)
// it's a girl
Note the double quote (") which signifies a string, rather than a single character.

The problem is that you're comparing a string (or rather, a char*) to a char. This comparison (i.e. if(gender == 'f')) will compare the raw pointer value to the character instead of comparing the contents of the string to the character. Instead, you need to dereference the pointer and then compare that value, or index into the string, i.e. if(gender[0] == 'f').
Of course, it would also be a good idea to check that the string actually contains something before attempting that in order to avoid a segfault.

You have:
char *gender = "";
So gender is a string, not a character. To compare strings, use strcmp.

You first declared gender as a string:
char *gender = "";
Then you later treat is as a single character:
if(gender == 'f')
[...]
if(gender == 'b')
You need to clarify in your own mind what gender is, before you try and code it.
Pick one definition, and stick with it.

Related

warning: incompatible pointer to integer conversion

I am attempting to change the last character in a word to make it the plural version of that word. For example bus to buses or penny to pennies. I get a warning when I try to set the last character equal to the plural ending. Please let me know what I need to change in order to fix this problem. Thank you.
#include <stdio.h>
#include <string.h>
int main(void) {
char word[50];
char *last_char;
printf("Enter a word: ");
scanf("%[^\t\n]", word);
last_char = &word[(strlen(word)-1)];
if(*last_char == 'y')
{
*last_char = "ies";
printf("\nthis does not work %s", word);
}
else if(*last_char == 's')
{
*last_char = "es";
printf("\nthis is working %s", word);
}
else if(*last_char == 'h')
{
*last_char = 'es';
printf("\nPlural: %s", word);
}
else
{
last_char = &word[(strlen(word))];
*last_char = 's';
printf("\nPlural: %s", word);
}
return 0;
}
Just look at what you said yourself:
I get a warning when I try to set the last character equal to the plural ending.
You've got a memory slot for one character at that position and you're trying to squeeze many at that one slot. Isn't going to work.
"ies"; is not a single character, it is a string - an array of characters that decays to a pointer to the first character. A single char is of integer type; you cannot convert a pointer to character (well you can, but it does not make sense).
In this case the simple solution would be to strcpy the ending to the subarray starting from last_character, i.e.:
strcpy(last_char, "ies");
As for
last_char = &word[(strlen(word))];
*last_char = 's';
that is not correct because the string would not be null-terminated!
remember that last_char was already pointing to the last character, and the null terminator after that is at address last_char + 1; you can replace these two lines with
strcpy(last_char + 1, "s");

Getopt not grabbing and storing my input in C

This is my first post on this site so please forgive any formatting errors.
I am coding a small program to get simple information from a file like amount of words, sorting it etc. However I started simply by using getopt since I want to use the command line to parse my commands. This is what I have so far.
#include <stdio.h>
#include <stdlib.h>
int value = 0;
int main(int argc, char **argv) {
extern char *optarg;
extern int optind;
int c, err = 0;
int cflag = 0, sflag = 0, fflag = 0;
char *substring;
// usage from instructions in case of error
static char usage[] = "usage: mywords [-cs] [-f substring] filename";
// only f flag requires additional input
while ((c = getopt(argc, argv, "csf:")) != -1)
switch (c) {
case 'c':
cflag = 1;
break;
case 's':
sflag = 1;
break;
case 'f':
fflag = 1;
printf("Before assigning to substring\n");
substring = optarg;
printf("After\n");
break;
case '?':
err = 1;
break;
}
if (fflag = 1) {
printf("%c ", &substring);
}
}
I will then input something like this to the command line after I run my makefile (which runs successfully with no errors):
mywords -f test
and ideally the output would be
test
The printf statements I have in the fflag are simple test prints to see if I was even reaching there. Thank you for your help in advance.
EDIT
It was fixed. David Collins had the right answer. Thank you so much everyone
I have noticed three issues in you code. (There may be other small issues but addressing the following should get you up and running I think.)
Testing for equality
You should use if (fflag == 1) instead of if (fflag = 1).
Pointer de-referencing
If you want to retrieve the value of the character pointed to by substring, you would use *substring instead of &substring.
But you probably don't want to do this. See below.
printf() format specifiers
When printing a string / character array, use the %s specifier instead of %c. (%c is for single characters). So you should have
if (fflag == 1) {
printf("%s ", substring);
}
Or - more simply - just
if (fflag) {
puts(substring)
}
since any non-zero value evaluates to true in C (and you initialize fflag to zero at the start of your program).

Iterate through argv[]

I'm new to C and I completed a small exercise that iterates through the letters in an argument passed to it and identifies the vowels. The initial code only worked for one argument (argv[1]). I want to expand it to be able to iterate through all arguments in argv[] and repeat the same process of identifying vowels.
The code:
#include <stdio.h>
int main(int argc, char *argv[])
{
if (argc < 2) {
printf("ERROR: You need at least one argument.\n");
return 1;
}
if (argc == 2) {
int i = 0;
for (i = 0; argv[1][i] != '\0'; i++) {
char letter = argv[1][i];
if (letter == 'A' || letter == 'a') {
printf("%d: 'A'\n", i);
//so on
}
}
} else {
int i = 0;
int t = 2;
for (t = 2; argv[t] != '\0'; t++) {
for (i = 0; argv[t][i] != '\0'; i++) {
char letter = argv[t][i];
if //check for vowel
}
}
return 0;
}
}
I read this answer and it seems the best solution is to use pointers, a concept I'm still a bit shaky with. I was hoping someone could use the context of this question to help me understand pointers better (by explaining how using pointers in this instance solves the problem at hand). Many thanks in advance.
I was hoping someone could use the context of this question to help me understand pointers better....
In context of your program:
int main(int argc, char *argv[])
First, understand what is argc and argv here.
argc(argument count): is the number of arguments passed into the program from the command line, including the name of the program.
argv(argument vector): An array of character pointers pointing to the string arguments passed.
A couple of points about argv:
The string pointed to by argv[0] represents the program name.
argv[argc] is a null pointer.
For better understanding, let's consider an example:
Say you are passing some command line arguments to a program -
# test have a nice day
test is the name of the executable file and have, a, nice and day are arguments passed to it and in this case, the argument count (argc) will be 5.
The in-memory view of the argument vector (argv) will be something like this:
argv --
+----+ +-+-+-+-+--+ |
argv[0]| |--->|t|e|s|t|\0| |
| | +-+-+-+-+--+ |
+----+ +-+-+-+-+--+ |
argv[1]| |--->|h|a|v|e|\0| |
| | +-+-+-+-+--+ |
+----+ +-+--+ |
argv[2]| |--->|a|\0| > Null terminated char array (string)
| | +-+--+ |
+----+ +-+-+-+-+--+ |
argv[3]| |--->|n|i|c|e|\0| |
| | +-+-+-+-+--+ |
+----+ +-+-+-+--+ |
argv[4]| |--->|d|a|y|\0| |
| | +-+-+-+--+ |
+----+ --
argv[5]|NULL|
| |
+----+
A point to note about string (null-terminated character array) that it decays into pointer which is assigned to the type char*.
Since argv (argument vector) is an array of pointers pointing to string arguments passed. So,
argv+0 --> will give address of first element of array.
argv+1 --> will give address of second element of array.
...
...
and so on.
We can also get the address of the first element of the array like this - &argv[0].
That means:
argv+0 and &argv[0] are same.
Similarly,
argv+1 and &argv[1] are same.
argv+2 and &argv[2] are same.
...
...
and so on.
When you dereference them, you will get the string they are pointing to:
*(argv+0) --> "test"
*(argv+1) --> "have"
....
....
and so on.
Similarly,
*(&argv[0]) --> "test"
*(&argv[0]) can also written as argv[0].
which means:
*(argv+0) can also written as argv[0].
So,
*(argv+0) and argv[0] are same
*(argv+1) and argv[1] are same
...
...
and so on.
When printing them:
printf ("%s", argv[0]); //---> print "test"
printf ("%s", *(argv+0)); //---> print "test"
printf ("%s", argv[3]); //---> print "nice"
printf ("%s", *(argv+3)); //---> print "nice"
And since the last element of argument vector is NULL, when we access - argv[argc] we get NULL.
To access characters of a string:
argv[1] is a string --> "have"
argv[1][0] represents first character of string --> 'h'
As we have already seen:
argv[1] is same as *(argv+1)
So,
argv[1][0] is same as *(*(argv+1)+0)
To access the second character of string "have", you can use:
argv[1][1] --> 'a'
or,
*(*(argv+1)+1) --> 'a'
I hope this will help you out in understanding pointers better in context of your question.
To identify the vowels in arguments passed to program, you can do:
#include <stdio.h>
int main(int argc, char *argv[])
{
if (argc < 2) {
printf("ERROR: You need at least one argument.\n");
return -1;
}
for (char **pargv = argv+1; *pargv != argv[argc]; pargv++) {
/* Explaination:
* Initialization -
* char **pargv = argv+1; --> pargv pointer pointing second element of argv
* The first element of argument vector is program name
* Condition -
* *pargv != argv[argc]; --> *pargv iterate to argv array
* argv[argc] represents NULL
* So, the condition is *pargv != NULL
* This condition (*pargv != argv[argc]) is for your understanding
* If using only *pragv is also okay
* Loop iterator increment -
* pargv++
*/
printf ("Vowels in string \"%s\" : ", *pargv);
for (char *ptr = *pargv; *ptr != '\0'; ptr++) {
if (*ptr == 'a' || *ptr == 'e' || *ptr == 'i' || *ptr == 'o' || *ptr == 'u'
|| *ptr == 'A' || *ptr == 'E' || *ptr == 'I' || *ptr == 'O' || *ptr == 'U') {
printf ("%c ", *ptr);
}
}
printf ("\n");
}
return 0;
}
Output:
#./a.out have a nice day
Vowels in string "have" : a e
Vowels in string "a" : a
Vowels in string "nice" : i e
Vowels in string "day" : a
You can use nested for loops to loop through all arguments. argc will tell you number of arguments, while argv contains the array of arrays. I also used strlen() function from strings library. It will tell you how long a string is. This way you can check for any number of arguments. Your if statement can also be changed to just 2, either argc is less than 2 or more than.
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[]) {
if (argc < 2) {
printf("ERROR: You need at least one argument.\n");
return 1;
} else {
int i, x;
int ch = 0;
for (i=1; i<argc; i++) {
for (x = 0; x < strlen(argv[i]); x++) {
ch = argv[i][x];
if (ch == 'A' || ch == 'a' || ch == 'e')
printf('Vowel\n');
}
}
}
}
Python Equivalent for the nested loop
for i in range (0, argc):
for x in range(0, len(argv[i])):
ch = argv[i][x];
if ch in ('A', 'a', 'e'):
print('Vowel')
While you can use multiple conditional expressions to test if the current character is a vowel, it is often beneficial to create a constant string containing all possible members of a set to test against (vowels here) and loop over your string of vowels to determine if the current character is a match.
Instead of looping, you can simply use your constant string as the string to test against in a call to strchr to determine if the current character is a member of the set.
The following simply uses a loop and a pointer to iterate over each character in each argument, and in like manner to iterate over each character in our constant string char *vowels = "aeiouAEIOU"; to determine if the current character is a vowel (handling both lower and uppercase forms).
#include <stdio.h>
int main (int argc, char *argv[]) {
int i, nvowels = 0;
char *vowels = "aeiouAEIOU";
if (argc < 2) {
fprintf (stderr, "ERROR: You need at least one argument.\n");
return 1;
}
for (i = 1; i < argc; i++) { /* loop over each argument */
char *p = argv[i]; /* pointer to 1st char in argv[i] */
int vowelsinarg = 0; /* vowels per argument (optional) */
while (*p) { /* loop over each char in arg */
char *v = vowels; /* pointer to 1st char in vowels */
while (*v) { /* loop over each char in vowels */
if (*v == *p) { /* if char is vowel */
vowelsinarg++; /* increment number */
break; /* bail */
}
v++; /* increment pointer to vowels */
}
p++; /* increment pointer to arg */
}
printf ("argv[%2d] : %-16s (%d vowels)\n", i, argv[i], vowelsinarg);
nvowels += vowelsinarg; /* update total number of vowels */
}
printf ("\n Total: %d vowles\n", nvowels);
return 0;
}
Example Use/Output
$ ./bin/argvowelcnt My dog has FLEAS.
argv[ 1] : My (0 vowels)
argv[ 2] : dog (1 vowels)
argv[ 3] : has (1 vowels)
argv[ 4] : FLEAS. (2 vowels)
Total: 4 vowles
If you decided to use the strchar function from string.h to check if the current char was in your set of vowels, your inner loop over each character would be reduced to:
while (*p) { /* loop over each char in arg */
if (strchr (vowels, *p)) /* check if char is in vowels */
vowelsinarg++; /* increment number */
p++; /* increment pointer to arg */
}
Look things over and let me know if you have further questions.
If anyone else is coming to this from Learn C The Hard Way the answers above using pointers are getting a bit far ahead of us as we won't be covering pointers until chapter 15. However you can do this piece of extra credit with what we've learned so far. I won't spoil the fun but you can use a for loop for the arguments with a nested for loop for the letters in those words; getting a letter from each word then is as simple as argv[j][i] where j is the jth arg passed and i the ith letter in j. No having to import header files or use pointers. I have to say I did come back to H.S's answer when I did come to pointers in chapter 15.

C Using isdigit to check if optarg is a digit

If optarg (the argument after the flag -s, from Getop library) isn't a digit, I want an error message to be printed and the program terminated, and if it is a digit, size needs to be set to optarg. The problem I have is while a command like the -s r will hit the error message, -s 2 will also, meaning it's interpreting the 2 as a string.
Debugging with for -s 2
#1 printf("%d",atoi(optarg));
#2 printf("%d",isdigit(atoi(optarg)));
I get the int value 2 for line #1,
and an int value of 0 for line #2.
So I was wondering why isdigit(atoi(optarg))) gives me a 0, when atoi(optarg) gives me an int. Is there a better way to check if optarg is an int?
int main (int argc, char *argv[]){
int size;
char option;
size = 0;
const char *optstring;
optstring = "rs:pih";
while ((option = getopt(argc, argv, optstring)) != EOF) {
switch (option) {
case 'r':
type_set = 1;
break;
**case 's':
capacity_set = 1;
if(isdigit(atoi(optarg))==0){
fprintf(stderr,"Argument after -s needs to be an int\n");
return 0;
}**
else{
size = atoi(optarg);
}
break;
default{
return 0;
}
isdigit takes a character and tells you if it is a digit. atoi takes a string (char *) and returns the number that the string represents. So when you call isdigit(atoi(... you're taking a number and treating it as a character. Since the charater codes for digits are 48..57, any number other than one of those will return false.
You probably want isdigit(*optarg) -- this will tell you if the first character of the argument (string) is a digit character. Of course, this only looks at the first character, so you might want isdigit(optarg[0]) && optarg[1] == 0 instead.
If you want to accept a number rather than a digit (and only a number), strtol works much better than atoi as it allows you to check for failures. Something like:
char *end;
errno = 0;
size = strtol(optarg, &end, 10);
while (isspace(*end)) ++end;
if (errno || *end) {
// an error occurred on conversion, or there is extra cruft
// after a number in the argument.
fprintf(stderr,"Argument after -s needs to be an int\n");
return 0; }
Chris Dodd has a great answer. Another, possibly simpler method is as follows ( assuming you just want to know if optarg is a SINGLE digit:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Assuming ASCII. "string" must be one character long and it
// must be a digit
int isSingleDigit( char *str )
{
return( (strlen(str) == 1) && ( *str >= '0' && *str <= '9' ) );
}
int main (void)
{
char *str = "a";
char *str1 = "9";
char *str2 = "10";
char *str3 = "1a";
printf( "%s %d\n", str, isSingleDigit(str));
printf( "%s %d\n", str1, isSingleDigit(str1));
printf( "%s %d\n", str2, isSingleDigit(str2));
printf( "%s %d\n", str3, isSingleDigit(str3));
return 0;
}
Output:
a 0
9 1
10 0
1a 0

how to compare character against set of given characters in C?

I'd like to be able to compare a character on stdin with a characters of my specification. The purpose of this is to filter out every other input as wrong, while maintaining only the specified single chars as commands. Like on stdin "nn" or "qddaw" -> wrong go again but "n" make something useful.
Here is what I have in mind "code-wise":
if (input does not contain 'c' or 's' or 'q' or 'n') {
printf("some kind of error");
}
Well I tried to create an array with specified characters like array[] = {'a', 'b', 'c'} so I could be able to compare it with a string on the stdin with function strncmp.. like
char c[256];
scanf("%s", c)
if (strncmp(array, c, 1) != 0) printf("error");
but it doesn't seem to work. Any suggestions?
Edit1: Here is actual piece of code:
char c[256];
char* s = "nsrld";
char* quiter = "q";
do
{
printf(">");
scanf("%s", c);
if (only when there is no 'n' or 's' or other char from char* s on input)
{
errorHandle(ERROR_WRONG_CMD);
}
scanf("%*[^\n]"); scanf("%*c");
} while (strcmp(c,quiter) != 0);
as you can see I handled the 'q' thing quite well, but multiple chars are pain in the ass. Thanks for any advice.
Edit 2: or in other words I need a function which will compare input with a set of given characters and only if there is one OR another (like 'q' or 's' the function will pass (but not if there are characters together like 'qs')
I didn't make myself clear enough. What I need is input "type what ever you want" like "wwqwqe" and do the error unless the input is just 'c' or just 's' (and a few more).
char usersInput[200] = ""; /* A buffer to hold the input values */
char *result = gets(usersInput); /* Fill the buffer from stdin */
if (result != NULL) /* If we got something */
{
if (strlen(usersInput) == 1) /* the input must be exactly 1 character */
{
char ch = usersInput[0];
if (strchr(ch, "csqn") == NULL) /* It must be a valid values */
{
printf("Evil Bad Character <%c>\n", ch);
}
else
{
/* Do stuff with the known valid input value ch */
}
}
else
{
puts("The input value must be exactly 1 character\n");
puts("and must be 'c', 's', 'q' or 'n'");
}
}
else
{
puts("EOF or ERROR while reading stdin\n");
}
This should do the job.
One warning. gets is not smart enough to know that usersInput is 200 characters long.
It will gleefully let you type in 201 characters or more, which overwrites other characters in memory. That sort of thing can lead to hard-to-find bugs.
int ch = getchar();
if (ch != EOF)
{
if (strchr("csqn", ch) == NULL)
{
printf("Evil Bad Character <%c> in Hex %02X\n", ch, ch);
}
else
{
/* Do stuff with ch */
}
}
else
{
printf("EOF on input\n");
}
char c = getchar();
switch (c) {
case 'c':
case 's':
case 'q':
case 'n':
do_something();
break;
default:
print_error();
};
The above code should work. I don't know why your if statement wasn't working. Generally a switch works well in this type of scenario too.
Your first solution should work. If that's the exact same code you posted - then your problem might because the printf needs a newline at the end to flush to console.
I have thought the string as sets... So if the intersection of them is the void set then we will fail -> printf("Error")... otherwise the output is none...
#include <stdio.h>
#include <string.h>
int intersection(char* source, char* search)
{
int i,j;
for(i = 0; i < strlen(search); i++)
if(strchr(source,search[i]))j++;
if(j != strlen(search))return 0;
else return 1;
}
int main()
{
char *letters = "eo";
char *p = "hello";
int e = intersection(p,letters);
if(e==1)puts("Non Void");
else puts("Void");
}
While it looks as if you've got a solution, it might be worth mentioning that what you're asking for doesn't sound as if it's all that far away from the standard 'getopt' functionality... See http://www.gnu.org/software/libc/manual/html_node/Getopt.html for example.
This worked for me:
char c[256];
char* s = "nqsrld";
char* quiter = "q";
do
{
printf(">");
scanf("%s", c);
if ((strpbrk(s, c) == 0) || (strlen(c) >= 2))
{
errorHandle(ERROR_WRONG_CMD);
}
scanf("%*[^\n]"); scanf("%*c");
} while (strcmp(c,quiter) != 0);
Thanks to everyone for their help.
Write a function
int IsGood(int c)
{
if(c=='a'||c=='b'||c=='c')
return 1;
else
return 0;
}

Resources