CS50 Caesar (usage of asterisk) [duplicate] - c

This question already has an answer here:
Segmentation fault: boolean expression
(1 answer)
Closed 2 years ago.
I have a question regarding how to use asterisk in my code.
Initially I run !isdigit(argv[1]) [without *] it showed segmentation fault. After referring to an online solution, I realise we should put * in !isdigit(*argv[1]). Can someone explain the function of * in the code?
Below is my code:
#include <stdio.h>
#include <cs50.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
int main (int argc, string argv[])
{
if (argc !=2 || !isdigit(argv[1]))
{
printf("Usage: ./caesar key\n");
return 1;
}
int key = atoi(argv[1]);
string p = get_string("plaintext: ");
printf ("ciphertext: ");
for ( int i=0, x=strlen(p); i<x; i++)
{
if (p[i]>='a' && p[i]<='z')
{
p[i] = (p[i] -'a'+key ) %26 +'a';
}
if (p[i]>='A' && p[i]<='Z')
{
p[i] = (p[i] -'A'+key ) %26 +'A';
}
}
printf("%s\n",p);
}

It seems that in this parameter declaration
string argv[]
the name string is an alias for the type char *.
So the parameter declaration may be rewritten like
char * argv[]
that is it is an array of pointers of the type char *. The compiler implicitly adjusts this declaration to char **argv.
So the expression argv[1] gives the second element of the passed to the function main array with the index 1. It has the type char *. The function isdigit expects an object of the type char converted to the type unsigned char. So to get an object of the type char (the first character of the pointed string) you have to dereference the pointer argv[1] having the type char * like *argv[1] or argv[1][0].
It would be more correct to write it at least as
!isdigit( ( unsigned char )*argv[1] )
But in general this check is not enough. You should check that the whole string pointed to by the pointer argv[1] represents a number.

Related

Error: char converts between pointers to integer types with different sign

Question: Write a program anagram that sorts the letters in a word, which is useful when searching for anagrams. anagram takes a single argument, which is a string containing only lower-case letters, sorts the letters alphabetically, and then prints the sorted letters. You may use any sorting algorithm you are familiar with, but you must write the sort function yourself. You may not use any sort function provided by a library.
Usage
$ ./anagram hello
ehllo
$ ./anagram positivity
iiiopsttvy
$ ./anagram abcdef
abcdef
The below code is what I have done so far but I get the error
passing char * to parameter of unsigned char converts between pointers to integer types with different sign
#include <stdio.h>
#include <string.h>
void anagram(unsigned char input[])
{
int count[256] = { 0 };
int i;
for (i = 0; input[i] != '\0'; i++)
{
count[input[i]]++;
}
for (i = 0; i < 256; i++)
{
while (count[i] > 0)
{
printf("%c", i);
count[i]--;
}
}
}
int main(int argc, char* argv[])
{
if(argc > 1)
{
anagram(argv[1]);
}
return 0;
}
The short answer to your question is you are getting the pointer sign mismatch warning because you are attempting to pass argv[1] (type char *) to anagram which you have declared with a parameter of unsigned char * (though you use input[], the practical effect is that input decays to a pointer when used as a parameter)
The simple solution while preserving the unsigned char* type for anagram is to cast argv[1] to (unsigned char *) when passed as a parameter to anagram, e.g.
anagram((unsigned char *)argv[1]);
The more practical question is "Do you really need the unsigned char* type to begin with?" While you can escape and pass non-ASCII values as the argument to your program -- is that something you expect and want to protect against by using the unsigned char* (which is 100% fine to do).
Generally, you would simply declare anagram (char *input) (which is your second alternative to eliminate the signed mismatch on the pointers) and limit your loop to looping over the valid range for ASCII characters (see ASCIItable.com)
Either way is fine so long as you handle the pointer signedness consistently. Putting that altogether and removing the magic numbers from your code, you could do something similar to the following:
#include <stdio.h>
#include <string.h>
#define CHRSET 256 /* if you need a constant, define one */
void anagram (unsigned char *input)
{
int i, count[CHRSET] = { 0 };
for (i = 0; input[i] != '\0'; i++)
count[input[i]]++;
for (i = 0; i < CHRSET; i++)
while (count[i] > 0) {
printf("%c", i);
count[i]--;
}
putchar ('\n'); /* provide a POSIX compliant newline before termination */
}
int main(int argc, char *argv[]) {
if (argc < 2) {
fprintf (stderr, "error: insufficient input\n"
"usage: %s <string>\n", argv[0]);
return 1;
}
anagram ((unsigned char *)argv[1]);
return 0;
}
(note: you can also move the output of the final newline to main, but unless you plan on smushing multiple sorted strings together by making repeated calls to anagram, then it is better left after the output of each string)
Look things over and let me know if you have further questions.

error: comparison between pointer and integer ('int' and 'string' (aka 'char *'))

I´m starting in C any help will be appreciated.
From a saved string in ´letters´, I want to compare, one by one, if those match the one delivered by the user on argv[]. Moreover, I have a problem on my if clause that I don´t understand.
crypt.c:17:18: error: comparison between pointer and integer ('int' and 'string' (aka 'char *')) [-Werror]
if (g[i] == argv[1])
~~~~ ^ ~~~~~~~
I don´t agree with the compiler this to be a pointer and an integer as I previously defined. g as a string and argv[] as a string. Can someone shed some light in what I am missing, please? I barely know about pointers, can you offer some theory as well? Thanks.
Here's my code:
#include <stdio.h>
#include <cs50.h>
#include <string.h>
int main(int argc, string argv[])
{
if (argc != 2)
{
printf("Wrong Input\n");
}
string g = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
for (int i = 0, n = strlen(g); i < n; i++)
{
if (g[i] == argv[1])
{
printf("True\n");
}
else
printf("False\n");
}
}
I'm going to assume string is typedef char *. (Strings in C are so weird I'd recommend not pretending they're anything but pointers and to not hide that with a string type.)
The trouble is argv is a char **, an array of strings. Whereas g is a char *, or a string. g[0] is the first character of the string, a char. argv[0] is the first string, a char *.
So g[i] == argv[1] is comparing a char with a char *. You probably mean g[i] == argv[1][i].
g is a string, but g[i] is not string anymore. (read: https://www.tutorialcup.com/cplusplus/strings.htm#CharacterArray)
Maybe you can check it as a char.
#include <stdio.h>
#include <cs50.h>
#include <string.h>
// maybe cs50.h have this line inside:
// typedef char* string;
int main(int argc, string argv[])
{
if ((argc != 2) || (strlen(argv[1])!=1))
{
printf("Wrong Input\n");
return 0;
}
// second argument, and first char
char arg = argv[1][0];
string g = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
for (int i = 0, n = strlen(g); i < n; i++)
{
if (g[i] == arg)
{
printf("True\n");
}
else
{
printf("False\n");
}
}
}

error comparision between pointer and integer

C Problem statement: Search an array of integers to find the first first negative integer, if one exists , return its position in the array.
I am aware that I can do this by using indexing, however, I am just wondering why is the program not going into the if condition? Even if I do the casting the code never goes inside if condition.
void find_negative(int argc, char *argv[])
{
int i = 0;
//ignore the first string of arguments because it will be "./problem1.3.c"
for(i =1; i<argc;i++)
{
if(*(argv+i)==2) <-------------------------this is where I get stuck (problem)
{
printf("found it at %d location.\n", i);
}
else
{
printf("All positive.\n");
}
}
}
int main(int argc , char *argv[])
{
find_negative(argc, argv);
return 0;
}
The type of (argv+i) is char**.
The type of *(argv+i) is char*.
In the line,
if(*(argv+i)==2)
you are trying to compare a char* with 2, whose type is int. That explains the compiler error messages.
Perhaps you want to extract an integer from the argument and compare it with 2. Then, you need to use:
if(atoi(*(argv+i))==2)
Some things to note:
*(argv + i) is exactly equivalent to argv[i]. There is no difference whatsoever in this particular code.
argv has type char ** (a pointer to a pointer to char), so *(argv + i) has type char * (a pointer to char). You are comparing this directly to the integer value 2, which is why the compiler is giving you grief, because it is rare to ever compare pointers to integers.
Comparing a string "2" to an integer value 2 won't work, they are different types entirely. If the program is receiving its input via command line arguments, you should parse the input into an actual binary integer.
As an example:
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char *argv[])
{
for (int i = 1; i < argc; i++)
{
long value = strtol(argv[i], NULL, 10);
if (value < 0)
{
printf("Found a negative integer at position %d\n", i);
return 0;
}
}
// if we get here then there were no negative integers in the input
puts("No negative integers in input");
return 1;
}
The function I used above is documented here.
The input, argv is an array of characters. You probably first need to parse it and convert it to an array of integers.

format ’%s’ expects argument of type ’char *’

For exercising my programming skills in C I'm trying to write the strncpy function by myself. Doing that I kept hitting errors, solving most of them eventually I'm stuck with no further inspiration to go on.
The error I receive is:
ex2-1.c:29:3: warning: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘int’ [-Wformat=]
printf("The copied string is: %s.\n", stringb);
The thing is that it's a very common error and that it's also already described on SO, only I can't seem to apply the tips other people have already pointed out. I get that I'm using a wrong type when printing the variable, and when I use the %d format it will return an integer which is probably the ASCII value of the first character, as it doesn't change when increasing the max number of bytes to copy.
Using GDB I've found out that the b variable when done iterating through the while loop holds the correct string, still I can't seem to print it.
I'm probably lacking a very fundamental part of knowledge about the C language and I apologise for asking this novice question (once again). Also I would appreciate it if you could give feedback or point out other flaws in my code.
#include <stdlib.h>
#include <stdio.h>
void strmycpy(char **a, char *b, int maxbytes) {
int i = 0;
char x = 0;
while(i!=maxbytes) {
x = a[0][i];
b[i] = x;
i++;
}
b[i] = 0;
}
int main (int argc, char **argv) {
int maxbytes = atoi(argv[2]);
//char stringa;
char stringb;
if (argc!=3 || maxbytes<1) {
printf("Usage: strmycpy <input string> <numberofbytes>. Maxbytes has to be more than or equal to 1 and keep in mind for the NULL byte (/0).\n");
exit(0);
} else {
strmycpy(&argv[1], &stringb, maxbytes);
printf("The copied string is: %s.\n", stringb);
}
return 0;
}
There is a slight difference between char and char*. The first is a single character whereas the later is a pointer to char (which can point to variable number of char objects).
The %s format specifier really expects a C-style string, which should not only be of type char* but is also expected to be null-terminated (see C string handling). If you want to print a single character, then use %c instead.
As for the program, assuming that what I think you want is what you want, try something like this:
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
static void strmycpy(char *dest, const char *src, size_t n) {
char c;
while (n-- > 0) {
c = *src++;
*dest++ = c;
if (c == '\0') {
while (n-- > 0)
*dest++ = '\0';
break;
}
}
}
int main(int argc, char *argv[]) {
size_t maxbytes;
char *stringb;
if (argc != 3 || !(maxbytes = atoll(argv[2]))) {
fprintf(
stderr,
"Usage: strmycpy <input string> <numberofbytes>.\n"
"Maxbytes has to be more than or equal to 1 and keep "
"in mind for the null byte (\\0).\n"
);
return EXIT_FAILURE;
}
assert(maxbytes > 0);
if (!(stringb = malloc(maxbytes))) {
fprintf(stderr, "Sorry, out of memory\n");
return EXIT_FAILURE;
}
strmycpy(stringb, argv[1], maxbytes);
printf("The copied string is: %.*s\n", (int)maxbytes, stringb);
free(stringb);
return EXIT_SUCCESS;
}
But frankly speaking, this is so fundamental that explaining might just result in writing a book on C. So you will be a lot better off if you just read one already written. For a list of good C books and resources, see The Definitive C Book Guide and List
Hope it helps. Good Luck!

How to reverse an array of strings and reverse each string in place using pointers?

I am learning C and I'm trying to reverse each string in an array in place with pointers. When i run the code below, I get the warnings that passing argument 1 and 2 of 'swapChars' makes pointer from integer without a cast. When I run it, I get a "bus error". Does anyone know why?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void swapChars(char *a, char *b) {
char temp = *a;
*a = *b;
*b = temp;
}
void reverse(int size, char* arr[]) {
for(int w = 0; w < size; w++) {
int length = strlen(arr[w]);
for(int i = 0; i < length/2; i++) {
swapChars(arr[w][i], arr[w][length-i]);
}
}
}
EDIT: thank you! I don't get any errors/warnings anymore, but do you know why it doesn't print anything when i run it with "hello world" ?
this is my main():
int main(int argc, char* argv[]) {
int numWords = argc-1;
char** words = argv+1;
reverse(numWords,words);
for (char** word = words; word < words+numWords; word++) {
printf("%s ",*word);
}
printf("\n");
return 0;
}
swapChars( &(arr[w][i]), &(arr[w][(length-1)-i]));
You need to pass in the address of ( or "pointer to" ) the array element, not the array element itself. Without this the compiler is warning you that it will try to convert the array element's value into a pointer - which it suspects is the wrong thing to do. So it warns you about this. And indeed - it is not what you intended.
#Pi is more right than I am. Fixed it. Add the -1 in the second argument because otherwise the null at the end of the string gets reversed also. That is why your string doesn't print. Probably.
When you are passing your arguments, you are dereferencing both values that you are passing in.
To correct this, the line should read
swapChars(&arr[w][i], &arr[w][length-1-i]);
This will pass in the addresses, rather than the values.

Resources