Using OR in statements with arrays, (C) - arrays

I've been trying to make a username and password interface and I was wondering if it was possible to have an or statement within strcmp and if I could also use all values of the array within 1 string, Thanks!
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <string.h>
int main(int argc, char *argv[]) {
int u, p;
char Iuser[50],Ipass[50];
char user[3][50] = { "user1", "user2", "user3" };
char pass[3][50] = { "pass1", "pass2", "pass3" };
printf("\n Enter your username:");
gets(Iuser);
u = strcmp(user[0|1|2], Iuser);
if (u == 0) {
printf("\n Enter your password");
scanf("%s", &Ipass);
} else {
printf("\n Invalid Username, Try Again !");
}
}

No, you can't do like that in C.
I'm stealing Aconcagua's comment about what it actually does:
user[0|1|2] first calculates 0|1|2, then accesses the array. Result of bitwise OR-ing 0, 1 and 2 is 3, though, which already is out of bounds of your user array, thus undefined behaviour
So, instead of
u=strcmp(user[0|1|2],Iuser);
if(u==0) {
You should do:
#include <stdbool.h>
bool u = strcmp(user[0], Iuser) == 0 ||
strcmp(user[1], Iuser) == 0 ||
strcmp(user[2], Iuser) == 0;
if(u) {
If the array of users is long or the number of users is not known at compile-time:
bool u = false;
for(int i = 0; i < number_of_users; ++i) {
if(strcmp(user[i], Iuser) == 0) {
u = true;
break;
}
}
if(u) {
Note: Don't use bitwise OR, |, for these comparisons. Using the logical OR, ||, enables short-circuit evaluation so that it stops testing as soon as one condition is true, just like the loop above which breaks out as soon as one condition has been found true.

strcmp cannot be used this way: user[0|1|2] evaluates to user[3], which accesses an element of the array beyond the end of the array: strcmp() will have undefined behavior when it reads from this place.
The C library does not have a generic function to locate a string in an array, so you should write:
u = strcmp(user[0], Iuser) && strcmp(user[1], Iuser) && strcmp(user[2], Iuser);
Which is quite verbose and specific.
Note that you should always ask for a password to avoid giving information about user names to an intruder, so the code should be modified as:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[]) {
char Iuser[50];
char Ipass[50];
char user[3][50] = { "user1", "user2", "user3" };
char pass[3][50] = { "pass1", "pass2", "pass3" };
int nusers = sizeof(user) / sizeof(user[0]); // number of users
int u;
for (;;) {
printf("\n Enter your username:");
if (scanf("%49s", Iuser) != 1)
return 1;
printf("\n Enter your password");
if (scanf("%49s", Ipass) != 1)
return 1;
for (u = 0; u < nusers; u++) {
if (strcmp(user[u], Iuser) == 0 && strcmp(pass[u], Ipass) == 0)
break;
}
if (u < nusers)
break;
printf("\n Invalid Username and/or password, Try Again !");
}
// user has been authenticated.
// ...
return 0;
}
Note also that password should be read without echoing the characters to the terminal, which is tricky but can be achieved on unix systems via getpass:
#include <pwd.h>
#include <unistd.h>
char *getpass(const char *prompt);
Passwords should not be stored in clear text as you do, nor as encrypted text because they would be too easy to find. Computing a cryptographic hash is recommended, in addition to more advanced techniques.

You should do
u=strcmp(user[0],Iuser)!=0 && strcmp(user[1], Iuser)!=0 && strcmp(user[2],Iuser)!=0;
if(u==0) {
//User exists so ask password
because strcmp accepts only two strings to compare. If you do a OR as u said it would be something strange like bitwise operation inside char arrays, I doubt it would ever compile and we don't want to do that.
Have a good day.

Related

C project for fun

I am new to coding (I just started this year), so please forgive me for any dumb mistakes. However, my goal is to make some straightforward code to have users enter their username and password. I would like the username to have 8 characters,1 uppercase, 1 lowercase, 1 digit, and 1 symbol. I thought my code was good but I keep running into an issue where I keep getting an error that says, "array must be initialized with a brace enclosed initializer". If you can find where I can improve, and what the issue might be it would be greatly appreciated.
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <ctype.h>
char username[20];
char password[20];
bool verify_password(char *password)
{
int length = strlen(password);
if (length < 8) return false;
bool has_upper = false;
bool has_lower = false;
bool has_digit = false;
bool has_symbol = false;
for (int i = 0; i < length; i++)
{
if (isupper(password[i])) has_upper = true;
if (islower(password[i])) has_lower = true;
if (isdigit(password[i])) has_digit = true;
if (ispunct(password[i])) has_symbol = true;
}
if (!has_upper) return false;
if (!has_lower) return false;
if (!has_digit) return false;
if (!has_symbol) return false;
return true;
}
int main()
{
printf("please enter your new username:\n");
scanf("%s", username);
printf("please enter your password(must be 8 characters,1 uppercase, 1 lowercase, 1 digit, and 1 symbol):\n");
scanf("%s", password);
char password[strlen(password)] = password;
bool result = verify_password(password);
if (result)
{
printf("password has been verified\n");
}
else
printf("missing element described");
printf("welcome to your account %s!, your password is %s", username, password);
return 0;
}
The compiler is saying that arrays need to be initialized with = { ... };
char password[strlen(password)] is an array, specifically a variable-length array (VLA). However, these arrays are special since they cannot get initialized at all. You have to set their values at run-time
Furthermore, it has to be char password[strlen(password)+1] so there's room for the null terminator.
Furthermore, you need to assign a value to a string using strcpy, not with the = operator.
Furthermore, you cannot have several variables with the same name. Or well you can, but in case of char password[strlen(password)] = password;, the password refers to the local variable, not the global one. It's a bad idea to use global variables in general and this would be one of many reasons why.
Overall you cannot do C programming by trial & error. There's no "take a chance and type something that looks ok", you need to actually know what every single thing you write does and that it is valid C.

C: variables retain the value from previous operation instead of resetting

I am fairly new to C and have been trying my hand with some arduino projects on Proteus. I recently tried implementing a keypad and LCD interface with Peter Fleury's libraries, so far the characters I input are displayed fine, but I run into a problem when trying to print to the serial port. It's like the value of the keys keeps on being concatenated with every iteration so the ouput has extra characters like this:
The value before the comma is from the 'key' variable, the value after it the 'buf' variable:
151
(The 5 I input in the second iteration was added to the 1 from the first iteration and then put into the variable I print)
I figure it may be due to my lack/incorrect use of pointers, heres is my code:
#include <avr/io.h>
#include <util/delay.h>
#include <stdlib.h>
#include <stdio.h>
#include "lcd.h"
#include "mat_kbrd.h"
#include "funciones.h"
#include "menu.h"
char buf[256];
char* coma = ",";
int main(void)
{
pin_init();
serial_begin();
lcd_init(LCD_DISP_ON);
kbrd_init();
bienvenida();
while (1) {
int i = 0;
char key = 0;
//char *peso;
//int pesoSize = 1;
char peso[100];
//peso = calloc(pesoSize,sizeof(char));
int salida = 0;
lcd_clrscr();
desechos();
key = kbrd_read();
if (key != 0) {
lcd_gotoxy(0,3);
lcd_putc(key);
_delay_ms(2000);
lcd_clrscr();
cantidad();
while (salida != 1) {
char keypeso = 0;
keypeso = kbrd_read();
//pesoSize = i;
//peso = realloc(peso,pesoSize*sizeof(char));
if (keypeso != 0) {
if (keypeso == '+') {
salida = 1;
keypeso = *("");
lcd_clrscr();
calcularTotal(key,peso);
_delay_ms(2000);
} else {
lcd_gotoxy(i,1);
lcd_putc(keypeso);
snprintf(peso, sizeof peso, "%s%s",peso, &keypeso);
//strcat(peso,&keypeso);
i++;
_delay_ms(2000);
}
}
}
snprintf(buf, sizeof buf, "%s%s%s", &key,coma,peso);
serial_println_str(buf);
}
}
}
&key and &keypeso point to a single char, but you are using the %s format specifier, so trying to read a string into a single char. Use %c rather then %s for single characters, and pass the char not the address-of-char..

crypt function in C breaking password string in for loop

I am new to C, and I have been going through the CS50 course to learn some basics. I have been trying to solve the challenge which requires you to make a simple password cracker, but I ran into a problem which prevents me from writing a function program: every time I call the crypt function in my for loop, it somehow breaks my password string that I am iterating through.
I have tried making a copy of the password string, and passing that as an argument to crypt; I have also tried moving the crypt call into a separate function and calling that from the loop (as well as the combination of the two)
#define _XOPEN_SOURCE
#include <unistd.h>
#include <cs50.h>
#include <stdio.h>
#include <string.h>
string buildLetterDictionary();
int main(int argc, string argv[])
{
if (argc == 2)
{
printf("Two arguments, starting test...\n");
char password[2];
string letters = buildLetterDictionary();
for(int i = 0; i < 5; i++)
{
password[0] = letters[i];
password[1] = '\0';
printf("Password: %s\n", password);
string hashed = crypt(password, "50");
printf("\n%i\nOriginal: %s\nHashed: %s\n", i, password, hashed);
}
return 0;
}
else
{
printf("Usage: ./crack hash");
return 1;
}
}
string buildLetterDictionary()
{
char letters[27];
for(int i = 65; i < 91; i++)
{
letters[i-65] = i;
}
letters[26] = '\0';
string letter = letters;
return letter;
}
if I comment out the lines:
string hashed = crypt(password, "50");
printf("\n%i\nOriginal: %s\nHashed: %s\n", i, password, hashed);
The code works as expected, and produces the output:
A
B
C
D
E
But if I leave those lines in, the password is printed out as 'A' with the hash "50pe4e2XTIS/g" the first time, but every subsequent time is printed out as "" with the hash "50sXZPq5euCxs"
Please let me know what the underlying problem is, so that I may work towards resolving it! Thanks for any help in advance!
I am guessing here that cs50.h contains some definitions like a type alias from char * to string that the professor is giving you for simplicity.
If that is true, then buildLetterDictionary() cannot work, because you are doing:
char letters[27];
...
char * letter = letters;
return letter;
This means you are returning the address of a local variable, which will be destroyed as soon as you leave the function.

Making y[i] a modifiable variable in C

I am building a program that randomly generates a password using the ascii tabe of values and can only contain one of each char. it generates a password that is 8 char long.
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#define SIZE 10
char Charactor(char x);
void Check(char* y);
int main()
{
char string[SIZE];//defines varriables
char* point;
point=&string[SIZE];
srand(time(NULL));
for (int i=0;i<SIZE-1;i++)//empties out the string
{
string[i]='\0';
}
for (int i=0;i<SIZE-2;i++)//randomizes a char for each space in the string
{
string[i]=Charactor(*point);
}
Check(point);/checks the string for duplicated values
printf("%s\n",string);//prints string on screen
}
char Charactor(char x)
{
int rnd=0;
rnd=rand()%2;//chooses char or number using ascii
if (rnd==0)
{
rnd=rand()%10+48;
}
else
{
rnd=rand()%26+65;
}
x=(char)rnd;
return x;
}
void Check(char* y)
{
int run=0;
for (int i=0; i<SIZE-2;i++)
{
for (int x=0; x<SIZE-2; x++)
{
if (y[i]==y[x] && run=0)
{
run++;
continue;
}
else
{
y[i]='\0';
y[i]=Charactor(*y);
}
}
}
return;
}
with those changes the code is running now I just have to figure out how to change the correct value so I dont have any duplication.
Fix:
char* point =&string[0]; //Make it to point to first element
Since your Charactor(*point); is really not doing anything based on *point and later you use Check(point); probably to start a scan from start of string.
And
if (y[i]==y[x] && run==0)
^^Use Equality check
You cannot modify a boolean outcome of y[i]==y[x] && run as zero.
Note :
However if (y[i]==y[x] && (run=0) ) wouldn't have thrown this error.
Your error seems to be that you are mistakenly setting run=0 in
if (y[i]==y[x] && run=0)
This is the part that most likely confuses your compiler. Doesn't have to do anything with Y.
Fix to:
if (y[i]==y[x] && run==0)

how to best achieve string to number mapping in a c program

I have a definite set of strings and its corresponding numbers:
kill -> 1
live -> 2
half_kill -> 3
dont_live -> 4
List is of 30 such strings and their number mapping.
If user enters "kill", I need to return 1 and if he enters "dont_live" I need to return 4.
How should I achieve this in c program? I am looking for an efficient solution because this operation needs to be done 100s of times.
should I put them in #define in my .h file?
Thanks in advance.
Sort your table, and use the standard library function bsearch to perform a binary search.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct entry {
char *str;
int n;
};
/* sorted according to str */
struct entry dict[] = {
"dont_live", 4,
"half_kill", 3,
"kill", 1,
"live", 2,
};
int compare(const void *s1, const void *s2)
{
const struct entry *e1 = s1;
const struct entry *e2 = s2;
return strcmp(e1->str, e2->str);
}
int
main (int argc, char *argv[])
{
struct entry *result, key = {argv[1]};
result = bsearch(&key, dict, sizeof(dict)/sizeof(dict[0]),
sizeof dict[0], compare);
if (result)
printf("%d\n", result->n);
return 0;
}
Here's what you get when you run the program.
$ ./a.out kill
1
$ ./a.out half_kill
3
$ ./a.out foo
<no output>
PS: I reused portions of sidyll's program. My answer should now be CC BY-SA compliant :p
A possible solution:
#include <stdio.h>
#include <string.h>
struct entry {
char *str;
int n;
};
struct entry dict[] = {
"kill", 1,
"live", 2,
"half_kill", 3,
"dont_live", 4,
0,0
};
int
number_for_key(char *key)
{
int i = 0;
char *name = dict[i].str;
while (name) {
if (strcmp(name, key) == 0)
return dict[i].n;
name = dict[++i].str;
}
return 0;
}
int
main (int argc, char *argv[])
{
printf("enter your keyword: ");
char s[100]; scanf("%s", s);
printf("the number is: %d\n", number_for_key(s));
return 0;
}
Here's one approach:
int get_index(char *s)
{
static const char mapping[] = "\1.kill\2.live\3.half_kill\4.dont_live";
char buf[sizeof mapping];
const char *p;
snprintf(buf, sizeof buf, ".%s", s);
p = strstr(mapping, buf);
return p ? p[-1] : 0;
}
The . mess is to work around kill being a substring of half_kill. Without that issue you could simply search for the string directly.
If it is a very short list of strings then a simple block of ifs will be more than sufficient
if (0 == strcmp(value, "kill")) {
return 1;
}
if (0 == strcmp(value, "live")) {
return 2;
}
...
If the number approach 10 I would begin to profile my application though and consider a map style structure.
if you have a fixed set of strimgs, you have two options: generate a perfect hashing function (check gperf or cmph) or create a trie so that you never have to check charcters more than once.
Compilers usually use perfect hashes to recognize a language keyword, in your case I would probably go with the trie, it should be the fastest way (but nothing beats direct measurement!)
Is it really a bottleneck? You should worry about efficiency only if the simple solution proves to be too slow.
Having said that, possible speed improvements are checking the lengths first:
If it's 4 characters then it could be "kill" or "live"
If it's 9 characters then it could be "half_kill" or "dont_live"
or checking the first character in a switch statement:
switch (string[0]) {
case 'k':
if (strcmp(string, "kill") == 0)
return 1;
return 0;
case 'l':
...
default:
return 0;
}
Use hashmap/ hashtable i think this would be the best solution.
Can you use an Enumunerator?
int main(void) {
enum outcome { kill=1, live, half_kill, dont_live };
printf("%i\n", kill); //1
printf("%i\n", dont_live); //4
printf("%i\n", half_kill); //3
printf("%i\n", live); //2
return 0;
}
Create a list of const values:
const int kill = 1;
const int live = 2;
const int half_kill = 3;
etc

Resources