I am using Linux and there is a custom function of which returns an ASCII int of current key sort of like getch(). When trying to get used to it and how to store the password I came into an issue, my code is as follows:
int main() {
int c;
char pass[20] = "";
printf("Enter password: ");
while(c != (int)'\n') {
c = mygetch();
strcat(pass, (char)c);
printf("*");
}
printf("\nPass: %s\n", pass);
return 0;
}
Unfortunately I get the warning from GCC:
pass.c:26: warning: passing argument 2 of ‘strcat’ makes pointer from integer without a cast
/usr/include/string.h:136: note: expected ‘const char * __restrict__’ but argument is of type ‘char’
I tried using pointers instead of a char array for pass, but the second I type a letter it segfaults. The function works on its own but not in the loop, atleast not like getch() would on a Windows system.
What can you see is wrong with my example? I am enjoying learning this.
EDIT: Thanks to the answers I came up with the following silly code:
int c;
int i = 0;
char pass[PASS_SIZE] = "";
printf("Enter password: ");
while(c != LINEFEED && strlen(pass) != (PASS_SIZE - 1)) {
c = mygetch();
if(c == BACKSPACE) {
//ensure cannot backspace past prompt
if(i != 0) {
//simulate backspace by replacing with space
printf("\b \b");
//get rid of last character
pass[i-1] = 0; i--;
}
} else {
//passed a character
pass[i] = (char)c; i++;
printf("*");
}
}
pass[i] = '\0';
printf("\nPass: %s\n", pass);
The problem is that strcat expects a char * as its second argument (it concatenates two strings). You don't have two strings, you have one string and one char.
If you want to add c to the end of pass, just keep an int i that stores the current size of pass and then do something like
pass[i] = (char) c.
Make sure to null-terminate pass when you are done (by setting the last position to 0).
A single character is not the same as a string containing a single character.
In other words, 'a' and "a" are very different things.
A string, in C, is a null-terminated array of chars. Your "pass" is an array of 20 chars - a block of memory containing space for 20 chars.
The function mygetch() returns a char.
What you need to do is to insert c into one of the spaces.
Instead of "strcat(pass, c)", you want to do "pass[i] = c", where i starts at zero, and increments by one for every time you call mygetch().
Then you need to do a pass[i] = '\0', when the loop is done, with i equal to the number of times you called mygetch(), to add the null terminator.
You're other problem is that you haven't set a value for c, the first time you check to see if it's '\n'. You want to call mygetch() before you do the comparison:
int i = 0;
for (;;)
{
c = mygetch();
if (c == '\n')
break;
c = mygetch();
pass[i++] = c;
}
pass[i] = '\0';
Over and above the correctly diagnosed issue with strcat() taking two strings -- why did you ignore the compiler warnings, or if there were no warnings, why don't you have warnings turned on? As I was saying, over and above that problem, you also need to consider what happens if you get EOF, and you also need to worry about the initial value of 'c' (which could accidentally be '\n' though it probably isn't).
That leads to code like this:
int c;
char pass[20] = "";
char *end = pass + sizeof(pass) - 1;
char *dst = pass;
while ((c = getchar()) != EOF && c != '\n' && dst < end)
*dst++ = c;
*dst = '\0'; // Ensure null termination
I switched from 'mygetch()' to 'getchar()' - primarily because what I say applies to that and might not apply to your 'mygetch()' function; we don't have a specification of what that function does on EOF.
Alternatively, if you must use strcat(), you still need to keep a track on the length of the string, but you can do:
char c[2] = "";
char pass[20] = "";
char *end = pass + sizeof(pass) - 1;
char *dst = pass;
while (c[0] != '\n' && dst < end)
{
c[0] = mygetch();
strcat(dst, c);
dst++;
}
Not as elegant as all that - using strcat() in context is overkill. You could, I suppose, do simple counting and repeatedly use strcat(pass, c), but that has quadratic behaviour as strcat() has to skip of 0, 1, 2, 3, ... characters on the subsequent iterations. By contrast, the solution where dst points to the NUL at the end of the string means that strcat() doesn't have to skip anything. With a fixed size addition of 1 character, though, you're probably better off with the first loop.
Related
I am making a simple program to read from a file character by character, puts them into tmp and then puts tmp in input[i]. However, the program saves a character in tmp and then saves the next character in input[i]. How do I make it not skip that first character?
I've tried to read into input[i] right away but then I wasn't able to check for EOF flag.
FILE * file = fopen("input.txt", "r");
char tmp;
char input[5];
tmp= getc(file);
input[0]= tmp;
int i=0;
while((tmp != ' ') && (tmp != '\n') && (tmp != EOF)){
tmp= getc(file);
input[i]=tmp;
length++;
i++;
}
printf("%s",input);
It's supposed to print "ADD $02", but instead it prints "DD 02".
You are doing things in the wrong order in your code: The way your code is structures, reading and storing the first char is moved out of the loop. In the loop, that char is then overwritten. In that case start with i = 1.
Perhaps you want to read the first character anyway, but I guess you want to read everything up to the first space, which might be the first character. Then do this:
#include <stdio.h>
int main(void)
{
char input[80];
int i = 0;
int c = getchar();
while (c != ' ' && c != '\n' && c != EOF) {
if (i + 1 < sizeof(input)) { // store char if the is room
input[i++] = c;
}
c = getchar();
}
input[i] = '\0'; // null-terminate input
puts(input);
return 0;
}
Things to note:
The first character is read before the loop. the loop condition and the code that stores the char then use that char. Just before the end of the loop body, the next char is read, which will then be processed in the next iteration.
You don't enforce that the char buffer input cannot be overwritten. This is dangerous, especially since your buffer is tiny.
When you construct strings char by char, you should null-terminate it by placing an explicit '\0' at the end. You have to make sure that there is space for that terminator. Nearly all system functions like puts or printf("%s", ...) expect the string to be null-terminated.
Make the result of getchar an int, so that you can distinguish between all valid character codes and the special value EOF.
The code above is useful if the first and subsequent calls to get the next item are different, for example when tokenizing a string with strtok. Here, you can also choose another approach:
while (1) { // "infinite loop"
int c = getchar(); // read a char first thing in a loop
if (c == ' ' || c == '\n' || c == EOF) break;
// explicit break when done
if (i + 1 < sizeof(input)) {
input[i++] = c;
}
}
This approach has the logic of processing the chars in the loop body only, but you must wrap it in an infinite loop and then use the explicit break.
Why can't I print the alphabet using this code?
void ft_putchar(char c)
{
write(1, &c, 1);
}
int print_alf(char *str)
{
int i;
i = 0;
while (str[i])
{
if (i >= 'A' && i <= 'Z')
ft_putchar(str[i]);
else
ft_putchar('\n');
i++;
}
return (str);
}
int main ()
{
char a[26];
printf("%s", print_alf(a));
return (0);
}
I get this warning
format ' %s ' expects type 'char*' but argument 2 has type 'int'
How do I print the alphabet using a string, and write function?
Your entire print_alf function looks suspicious.
You are returning str which is of type char *. Therefore the return type of print_alf should to be char * instead of int.
Your while (str[i]) loop makes no sense at all since you are passing uninitialized memory to it. So your code will very likely corrupt the memory since the while loop will continue to run until a '\0' is found within the memory which does not need to be the case within the boundaries of the passed memory (a).
You are not adding a zero termination character ('\0') at the end of the string. This will result in printf("%s", print_alf(a)); printing as many characters beginning at the address of a until a '\0' is found within the memory.
Here is a suggestion how to fix all that problems:
char *print_alf(char *str, size_t len)
{
char letter;
if ((str) && (len >= 27)) // is str a valid pointer and length is big enough?
{
for (letter = 'A'; letter <= 'Z'; letter++) // iterate all characters of the alphabet
{
*str = letter;
str++;
}
*str = '\0'; // add zero termination!!!
}
else
{
str = NULL; // indicate an error!
}
return (str);
}
int main()
{
char a[26 + 1]; // ensure '\0' fits into buffer!
printf("%s", print_alf(a, sizeof(a)));
return (0);
}
Make up your mind whether print_alf should return a string which you then print with printf or whether print_alf should be a void function that does the printing, which you should then just call without printf. At the moment, your code tries to be a mixture of both.
The easiest way is to just print the alphabet:
void print_alf(void)
{
int c;
for (c = 'A'; c <= 'Z'; c++) putchar(c);
}
Call this function like so:
print_alf(); // print whole alphabet to terminal
A more complicated variant is to fill a string with the alphabet and then print that string. That's what you tried to achieve, I think. In that case, you must pass a sufficiently big buffer to the function and return it. Note that if you want to use the string functions and features of the standard lib (of which printf("%s", ...) is one) you must null-terminate your string.
char *fill_alf(chat *str)
{
int i;
for (i = 0; i < 26; i++) str[] = 'A' + i;
str[26] = '\0';
return str;
}
It is okay to return the buffer that was passed into the function, but beware of cases where you return local character buffers, which will lead to undefined behaviour.
You can call it as you intended in your original code, but note that you must make your buffer at least 27 characters big to hold the 26 letters and the null terminator:
char a[27];
printf("%s\n", fill_alf(a));
Alternatively, you could do the filling and printing in twp separate steps:
char a[27];
fill_alf(a); // ignore return value, because it's 'a'
printf("%s\n", a); // print filled buffer
If you just want to print the alphabet, the print_alf variant is much simpler and straightforward. If you want to operate further on the alphabet, eg do a shuffle, consider using fill_alf.
Your print_alf(char *str) function actually returns an integer which causes the error (it is defined to return int). When you specify %s to printf it expects characters, not numbers.
You can fix this by changing the return type of your function to char and if everything else works in your code you'll be good to go.
I'm working on a program for school right now in c and I'm having trouble reading text from a file. I've only ever worked in Java before so I'm not completely familiar with c yet and this has got me thoroughly stumped even though I'm sure it's pretty simple.
Here's an example of how the text can be formatted in the file we have to read:
boo22$Book5555bOoKiNg#bOo#TeX123tEXT(JOHN)
I have to take in each word and store it in a data structure, and a word is only alpha characters, so no numbers or special characters. I already have the data structure working properly so I just need to get each word into a char array and then add it to my structure. It has to keep reading each char until it gets to a non-alpha char value. I've tried looking into the different ways to scan in from a file and I'm not sure what would be best for my scenario.
Here's the code I have right now for my input:
char str[MAX_WORD_SIZE];
char c;
int index = 0;
while (fscanf(dictionaryInputFile, "%c", c) != EOF) //while not at end of file
{
if (isalpha(c)) //if current character is a letter
{
tolower(c); //ignores case in word
str[index] = c; //add char to string
index++;
}
else if (str[0] != '\0') //If a word
{
str[index] = '\0'; //Make sure no left over characters in String
dictionaryRoot = insertNode(str, dictionaryRoot); //insert word to dictionary
index = 0; //reset index
str[index] = '\0'; //Set first character to null since word has been added
}
}
My thinking was that if it doesn't hit that first if statement then I have to check if str is a word or not, that's why it checks if the 0 index of str is null or not. I'm guessing the else if statement I have is not right though, but I can't figure out a way to end the current word I'm building and then reset str to null when it's added to my data structure. Right now when I run this I get a segmentation fault if I pass the txt file as an argument.
I'd just like to know if I'm on the right track and if not maybe some help on how I should be reading this data.
This is my first time posting here so I hope I included everything you'll need to help me, if not just let me know and I'd be happy to add more information.
Biggest problem: Incorrect use of fscanf(). #BLUEPIXY
// while (fscanf(dictionaryInputFile, "%c", c) != EOF)
while (fscanf(dictionaryInputFile, "%c", &c) != EOF)
No protection against overflow.
// str[index] = c; //add char to string
if (index >= MAX_WORD_SIZE - 1) Handle_TooManySomehow();
Not sure why testing against '\0' when '\0' is also a non-alpha.
Pedantically, isalpha() is problematic when a signed char is passed. Better to pass the unsigned char value: is...((unsigned char) c)), when code knows it is not EOF. Alternatively, save the input using int ch = fgetc(stream) and use is...(ch)).
Minor: Better to use size_t for array indexes than int, but be careful as size_t is unsigned. size_t is important should the array become large, unlike in this case.
Also, when EOF received, any data in str is ignored, even if it contained a word. #BLUEPIXY.
For the most part, OP is on the right track.
Follows is a sample non-tested approach to illustrate not overflowing the buffer.
Test for full buffer, then read in a char if needed. If a non-alpha found, add to dictionary if a non-zero length work was accumulated.
char str[MAX_WORD_SIZE];
int ch;
size_t index = 0;
for (;;) {
if ((index >= sizeof str - 1) ||
((ch = fgetc(dictionaryInputFile)) == EOF) ||
(!isalpha(ch))) {
if (index > 0) {
str[index] = '\0';
dictionaryRoot = insertNode(str, dictionaryRoot);
index = 0;
}
if (ch == EOF) break;
}
else {
str[index++] = tolower(ch);
}
}
What I need to do is when given a text or string like
\0abc\n\0Def\n\0Heel\n\0Jijer\n\tlkjer
I need to sort this string using qsort and based on the rot encoding comparison.
int my_rot_conv(int c) {
if ('a' <= tolower(c) && tolower(c) <= 'z')
return tolower(c)+13 <= 'z' ? c+13 : c-13;
return c;
}
int my_rot_comparison(const void *a, const void *b) {
char* ia = (char*) a;
char* ib = (char*) b;
int i=0;
ia++, ib++;
while (i<strlen(ia)) {
if (ia[i] == '\0' || ia[i] == '\n' || ia[i] == '\t' || ib[i] == '\0' || ib[i] == '\n' || ib[i] == '\t') {
i++;
}
if (my_rot_conv(ia[i]) > my_rot_conv(ib[i])) {
return 1;
} else if (my_rot_conv(ia[i]) < my_rot_conv(ib[i]))
return -1;
}
return 0;
}
I get to the point that I compare two string that starts with \0, getting the -1 in the following example.
printf("%d \n", my_rot_comparison("\0Abbsdf\n", "\0Csdf\n"));
But this wouldn't work for a string with qsort because ia++, ib++; does work only for one word comparison.
char *my_arr;
my_arr = malloc(sizeof(\0abc\n\0Def\n\0Heel\n\0Jijer\n\tlkjer));
strcpy(my_arr, \0abc\n\0Def\n\0Heel\n\0Jijer\n\tlkjer);
qsort(my_arr, sizeof(my_arr), sizeof(char), my_rot_comparison);
and the array should be sorted like \0Def\n\0Heel\n\0Jijer\n\0\n\tlkjer
My question is how do I define the comparison function that works for the string that includes \0 and \t and \n characters?
strlen simply cannot operate properly on a string which embeds \0 bytes, since by definition of the function strlen considers the end of the string to be the first encountered \0 byte at or after the beginning of the string.
The rest of the standard C string functions are defined in the same way.
This means that you have to use a different set of functions to manipulate string(-like) data that can include \0 bytes. You will perhaps have to write these functions yourself.
Note that you will probably have to define a structure which has a length member in it, since you won't be able to rely on a particular sentinel byte (such as \0) to mark the end of the string. For example:
typedef struct {
unsigned int length;
char bytes[];
}
MyString;
If there is some other byte (other than \0) which is forbidden in your input strings, then (per commenter #Sinn) you can swap it and \0, and then use normal C string functions. However, it is not clear whether this would work for you.
assuming you use an extra \0 at the end to terminate
int strlenzz(char*s)
{
int length =0;
while(!(*s==0 && *(s+1) == 0))
{
s++;
length++;
}
return length+1
}
Personally I'd prefer something like danfuzz's suggestion, but for the sake of listing an alternative...
You could use an escaping convention, writing functions to:
"escape" / encode, expanding embedded (but not the terminating) '\0'/NUL to say '\' and '0' (adopting the convention used when writing C source code string literals), and
another to unescape.
That way you can still pass them around as C strings, your qsort/rot comparison code above will work as is, but you should be very conscious that strlen(escaped_value) will return the number of bytes in the escaped representation, which won't equal the number of bytes in the unescaped value when that value embeds NULs.
For example, something like:
void unescape(char* p)
{
char* escaped_p = p;
for ( ; *escaped_p; ++escaped_p)
{
if (*escaped_p == '\\')
if (*++escaped_p == '0')
{
*p++ = '\0';
continue;
}
*p++ = *escaped_p;
}
*escaped_p = '\0'; // terminate
}
Escaping is trickier, as you need some way to ensure you have enough memory in the buffer, or to malloc a new buffer - either of the logical size of the unescaped_value * 2 + 1 length as an easy-to-calculate worst-case size, or by counting the NULs needing escaping and sizing tightly to logical-size + #NULs + 1....
I'm attempting to write a simple shell like interface, that takes in a users input (by char) and stores it via a pointer to a pointer* (exactly how argv works). Here's my code:
char input[100];
char **argvInput;
char ch;
int charLoop = 0;
int wordCount = 0;
argvInput = malloc(25 * sizeof(char *));
while((ch = getc(stdin))) {
if ((ch == ' ' || ch == '\n') && charLoop != 0) {
input[charLoop] = '\0';
argvInput[wordCount] = malloc((charLoop + 1) * sizeof(char));
argvInput[wordCount] = input;
charLoop = 0;
wordCount++;
if (ch == '\n') {
break;
}
} else if (ch != ' ' && ch != '\n') {
input[charLoop] = ch;
charLoop++;
} else {
break;
}
}
If I loop through argvInput via:
int i = 0;
for (i = 0; i < wordCount; i++)
printf("Word %i: %s\n", i, argvInput[i]);
All of the values of argvInput[i] are whatever the last input assignment was. So if I type:
"happy days are coming soon", the output of the loop is:
Word 0: soon
Word 1: soon
Word 2: soon
Word 3: soon
Word 4: soon
I'm at a loss. Clearly each loop is overwriting the previous value, but I'm staring at the screen, unable to figure out why...
This line is your bane:
argvInput[wordCount] = input;
Doesn't matter that you allocate new space, if you're going to replace the pointer to it with another one (i.e. input).
Rather, use strncpy to extract parts of the input into argvInput[wordCount].
argvInput[wordCount] = input; is only making the pointer of argvInput[wordCount] point to the memory of input instead of copy the content of input into the new allocated memory. You should use memcpy or strcpy to correct your program.
After the pointer assignment the memory status looks like the image below. The memory allocated by malloc((charLoop + 1) * sizeof(char));, which are the grey ones in the graph, could not be accessed by your program anymore and this will lead to some memory leak issue. Please take care of that.
I suggest printing your argvInput pointers with %p, instead of %s, to identify this problem: printf("Word %i: %p\n", i, (void *) argvInput[i]);
What do you notice about the values it prints? How does this differ from the behaviour of argv? Try printing the pointers of argv: for (size_t x = 0; x < argc; x++) { printf("Word %zu: %p\n", x, (void *) argv[x]); }
Now that you've observed the problem, explaining it might become easier.
This code allocates memory, and stores a pointer to that memory in argvInput[wordCount]: argvInput[wordCount] = malloc((charLoop + 1) * sizeof(char)); (by the way, sizeof char is always 1 in C, so you're multiplying by 1 unnecessarily).
This code replaces that pointer to allocated memory with a pointer to input: argvInput[wordCount] = input; ... Hence, all of your items contain a pointer to the same array: input, and your allocated memory leaks because you lose reference to it. Clearly, this is the problematic line; It doesn't do what you initially thought it does.
It has been suggested that you replace your malloc call with a strdup call, and remove the problematic line. I don't like this suggestion, because strdup isn't in the C standard, and so it isn't required to exist.
strncpy will work, but it's unnecessarily complex. strcpy is guaranteed to work just as well because the destination array is allocated to be large enough to store the string. Hence, I recommend replacing the problematic line with strcpy(argvInput[wordCount], input);.
Another option that hasn't been explained in detail is strtok. It seems this is best left unexplored for now, because it would require too much modification to your code.
I have a bone to pick with this code: char ch; ch = getc(stdin); is wrong. getc returns an int for a reason: Any successful character read will be returned in the form of an unsigned char value, which can't possibly be negative. If getc encounters EOF or an error, it'll return a negative value. Once you assign the return value to ch, how do you differentiate between an error and a success?
Have you given any thought as to what happens if the first character is ' '? Currently, your code would break out of the loop. This seems like a bug, if your code is to mimic common argv parsing behaviours. Adapting this code to solve your problem might be a good idea:
for (int c = getc(stdin); c >= 0; c = getc(stdin)) {
if (c == '\n') {
/* Terminate your argv array and break out of the loop */
}
else if (c != ' ') {
/* Copy c into input */
}
else if (charLoop != 0) {
/* Allocate argvInput[wordCount] and copy input into it,
* reset charLoop and increment wordCount */
}
}