1

I am trying to convert a command line argument to a variable (line 17, "key"). When I run the program without anything else but the name in the command line (e.g. $ ./caesar) I do not get any errors but the code does not progress past the lines shown below. But, when I run the program with any command line argument (e.g. $ ./caesar 2) the code gives an error that is also shown below.

I have to use argv for key as it is a requirement in this assignment (I am doing this for CS50 if that helps). I do not know how argv works very well and researching on it didn't help me. Due to this I didn't try much that is helpful in the way of debugging. Link to the cs50 library: https://cs50.readthedocs.io/library/c/

#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>

int main(int argc, string argv[]){
    if (argc != 2){ //limits args code will accept to one
        return 1;
    }
    if (isdigit(argv[1]) == true){ //checks if first arg is digit
        printf("Usage: ./caesar key\n");
        return 1;
    } else {
        printf("Success\n");
    }

    int key = atoi(argv[1]); // converting first arg to int

This is the error I am getting when I use any command line arg, along with how I am compiling the program.

$ make caesar
clang -fsanitize=signed-integer-overflow -fsanitize=undefined -ggdb3 -O0 -std=c11 -Wall -Werror -Wextra -Wno-sign-compare -Wno-unused-parameter -Wno-unused-variable -Wshadow    caesar.c  -lcrypt -lcs50 -lm -o caesar
$ ./caesar 2
UndefinedBehaviorSanitizer:DEADLYSIGNAL
==1070==ERROR: UndefinedBehaviorSanitizer: SEGV on unknown address 0x7f259d8849b2 (pc 0x0000004281c1 bp 0x7fff67ba6ad0 sp 0x7fff67ba68f0 T1070)
==1070==The signal is caused by a READ memory access.
    #0 0x4281c0 in main /root/sandbox/caesar.c:10:9
    #1 0x7f24cdfb6b96 in __libc_start_main /build/glibc-OTsEL5/glibc-2.27/csu/../csu/libc-start.c:310
    #2 0x402b89 in _start (/root/sandbox/caesar+0x402b89)

UndefinedBehaviorSanitizer can not provide additional info.
==1070==ABORTING
melpomene
  • 84,125
  • 8
  • 85
  • 148
  • 3
    `isdigit(argv[1])` is a type error. How are you compiling your code? – melpomene Jul 21 '19 at 21:11
  • Given the command line you show, it is very surprising that your code is compiling. It doesn't escape when I compile it — `error: incompatible pointer to integer conversion passing 'string' (aka 'char *') to parameter of type 'int' [-Werror,-Wint-conversion]` —— `if (isdigit(argv[1]) == true)` – Jonathan Leffler Jul 21 '19 at 21:20
  • Can you specify: what is `string` in your code?? Depending on what it is, you may be introducing undefined behaviour, simply by declaring `main()` with a `string[]` as an argument. Assuming it is simply `char *` (and not an array) your code would not even compile, let alone run and generate a runtime error. So it must be something else .... – Peter Jul 22 '19 at 01:33
  • @JonathanLeffler that might be possible because isdigit is an evil macro, with cast to int in it in Glibc, I must have seen it before already – Antti Haapala -- Слава Україні Jul 22 '19 at 02:57
  • @AnttiHaapala - oh, okay. `string` is `char *`. Which means the code as supplied has undefined behaviour. `isdigit()` accepts an argument of type `int`. `argv[1]` is of type `char *`. Instead of `isdigit(argv[1])` the usage should be `isdigit(argv[1][0])`. – Peter Jul 22 '19 at 04:59

1 Answers1

1

sscanf could be used to scan an integer from argv[1].
sscanf will return the number of items successfully scanned.

#include <stdio.h>

int main ( int argc, char *argv[]) {
    int key = 0;
    if (argc != 2){ //limits args code will accept to one
        printf("Usage: ./%s key\n", argv[0]);
        return 1;
    }
    if ( 1 != ( sscanf ( argv[1], "%d", &key))) { //try to scan an int
        printf("Usage: ./%s key\n", argv[0]);
        return 1;
    } else {
        printf("Success\n");
    }
    return 0;
}

As @NeilEdelman suggests the code can be condensed to

#include <stdio.h>

int main ( int argc, char *argv[]) {
    int key = 0;
    if ( argc != 2 || 1 != ( sscanf ( argv[1], "%d", &key))) { //try to scan an int
        printf("Usage: ./%s key\n", argv[0]);
        return 1;
    } else {
        printf("Success\n");
    }
    return 0;
}
xing
  • 2,125
  • 2
  • 14
  • 10
  • `sscanf` is meant to be used in exactly this way. I would further reduce the number of exit points by grouping, `if(argc != 2 || 1 != sscanf(...)) {}`. – Neil Jul 21 '19 at 21:51