I see three issues here (may be more).
First, the assignment
const char *temp = argv;
assigns a pointer to an array of char
pointers to a const char
pointer (instead of assigning a char
pointer, what probably was the intention).
Second, the function input_number
is invoked
// Remember, now temp == argv
input_number(temp, argc);
but inside it there is this call
// Remember, now c == temp == argv
scanf("%c", *c);
which essentially overrides the value that the pointer that *c
holds points to. What follows, since now c == argv
, scanf
overrides the value that the pointer to the first element of the array of program arguments points to, that is, the first character of the name of its executable. Indeed, if we try to print the executable name before and after such assignment (example code)
#include <stdio.h>
void f (const char **c) {
printf("%s\n", *c);
scanf("%c", *c);
printf("%s\n", *c);
}
int main(int argc, char *argv[]) {
const char *c = argv;
f(c);
return 0;
}
we receive (the input for scanf
is a
)
./prog
a/prog
what probably was not the intention.
Now, the third issue is, the assignment in the second issue works. But as far as I know, modifying a const
value (**c
in this case) results in undefined behavior. That is, now it works, but in some other execution, compiler, computer etc. it may not. Compare:
Edit: another issue is of course what Jabberwocky and the compiler say: you try to call atoi
with a pointer to pointer to char
, while it expects a pointer to char
.
Edit: worth to note, in the condition
if (!(f >= '0' && f <= '9'))
the constants '0'
and '9'
are of type int
(see [1]), so the comparisons are fine. But f
is both declared as int
and is assigned the result of atoi
, therefore for me it is expected to be just an int
, therefore in my opinion it would be clearer to write the comparisons using only integer constants, that is
if (!(f >= 0 && f <= 9))
Edit: another issue is that the function input_file
is declared with a char
parameter, that is
void input_file(char c);
but then it's called with an int
parameter, that is
// Where c is declared as "int c;"
input_file(c);
It would be clearer in my opinion to keep the types the same: both char
s or both int
s.
An interesting thing would be to know why it works at all: how can you place an int
variable, where there is a char
variable expected? For one, C99 standard draft says that
[t]he type char, the signed and unsigned integer types, and the enumerated types are
collectively called integer types.
So, char
is an "integer type". Then, cppreference.com says that
[a] value of any integer type can be implicitly converted to any other integer type.
and later
if the target type can represent the value, the value is unchanged
But what is the value of c
here? It is declared as
int c;
So, first, cppreference.com says that
[t]here are four kinds of storage duration in C:
- automatic storage duration. The storage is allocated when the block in which the object was declared is entered and deallocated when it is exited by any means (goto, return, reaching the end).
and then it says that
[i]f an initializer is not provided:
- objects with automatic storage duration are initialized to indeterminate values (which may be trap representations)
So, c
in the moment of the conversion has an indeterminate value. What follows, it cannot be sure before actually executing the program whether "the target type can represent the value". What follows, we need to look for another rule about converting char
s to int
s: cppreference.com says that
- otherwise, if the target type is unsigned, the value 2b
, where b is the number of bits in the target type, is repeatedly subtracted or added to the source value until the result fits in the target type. In other words, unsigned integers implement modulo arithmetic.
- otherwise, if the target type is signed, the behavior is implementation-defined (which may include raising a signal)
And the C99 standard draft says that
[t]he implementation shall define char to have the same range,
representation, and behavior as either signed char or unsigned char.
Ultimately, not going into details, it is up to the particular implementation of C you use to decide what is going to happen when you try to call a function expecting a char
with an int
if the value is beyond range (and, again, we don't know whether it will be beyond range or not when the code will be executed).
Edit: another issue is, in the function input_file
there is this line
strcpy(Contact.name, &file);
What it does is it copies a single character into a char
array. An address of a single character is possibly not expected by strcpy
because man 3 strcpy
reads that
[t]he strcpy() function copies the string pointed to by src, including the terminating null byte ('\0'), to the buffer pointed to by dest.
Unfortunately, I see no clues neither in the man page nor on the internet how this function shall behave if there is no \0
. In this answer to another question, Sourav Ghosh says that it is undefined behavior. And if he is true, I suppose that this is not what you expected.