-1

I'm a beginner to C, trying to write a program to calculate a square number. At my college we're not allowed to use printf() or scanf() which makes things a bit complicated.

This led me to using arguments to get input. I'm getting a segmentation fault (core dumped) when I try to compile. I think this comes from using argv and indexes but I'm not sure how to fix it.

Do you have some insight that might help? It would be much appreciated!

#include <unistd.h>

int main(int argc, char *argv[]) {
    int number;
    int square;

    while(argc != 0) {
        number = argv[1][square];
        square = number * number;

        write(1, &square, 1);
        square++;
    }

    write(1, "\n", 1);
    return 0;
}
Christie Marx
  • 39
  • 1
  • 4
  • 4
    `square` is uninitialized... – Eugene Sh. May 21 '20 at 14:41
  • 2
    number = argv[1][square]; this line leads to real disaster! argv is an array of strings square is not initialized! – Adam May 21 '20 at 14:43
  • 1
    you need to use int atoi(const char *string). atoi converts the string number you get via cli to int number. i will post an answer. – Adam May 21 '20 at 14:46
  • 1
    Do you want to square the argument (as a number ) or square each ASCII character present in the argument ? – Parsa Mousavi May 21 '20 at 15:21
  • 2
    Its kind of bad form to drop a question then not hang around to respond to questions or other comments. :) – ryyker May 21 '20 at 15:46
  • 1
    Christie Marx, what are the smallest and largest values you are expected to square? – chux - Reinstate Monica May 21 '20 at 16:06
  • 1
    `write(1, &square, 1);` This code writes one byte of the system's internal representation of `square` to standard output. There's no reason to think that humans will find this meaningful both because it's only a single byte and because it's part of the system's internal representation. You need to output a representation that humans are likely to understand, such as digits in base 10. – David Schwartz May 21 '20 at 16:32
  • @ryyker sorry about that I wasn't aware this is bad form. Thanks for letting me know :) – Christie Marx May 22 '20 at 08:05
  • @chux-ReinstateMonica thanks for your question! The smallest value would be 1 and the largest is 500 – Christie Marx May 22 '20 at 08:07
  • 1
    @ParsaMousavi Thanks! I want to square the argument as a number – Christie Marx May 22 '20 at 08:08

4 Answers4

2

The segmentation fault, if not caused by something else first will be caused here:

number = argv[1][square];//seg fault possible when square becomes 
                         //larger than strlen(argv[1]) + 1

Attempting to access memory that the program process does not own, as above, will invoke undefined behavior.

Also, at the time this expression is executed:

square = number * number;

it is unknown what value was contained in square.

These two values should be initialized:

int number = 0;
int square = 0;//this specifically will cause problems later if not initialized

Also, at the time this is called:

    number = argv[1][square];

argv[1] is a string, and needs to be converted to an integer before using.

number = atoi(argv[1]);

Next, the statement:

number = argv[1][square]; //seg fault possible as noted above. 

will blow up when the value of square becomes larger than then string length of argv[1].

If your intent is squaring the value contained in argv[1] in its entirety as a single numeric value, it must first be converted from a string array, to an integer value, then you can easily get the square as you do in your code:

   int number = atoi(argv[1]);
   square = number * number;

If, as it appears in your code, you are interested in squaring each of the component integers making up the string, then given the input of "1234, to convert each of the digits inargv1from ASCII value to its numeric value. (i.e.val = argv1[x] - '0'==>x`), then square it, then move the the next character in the array and so on.... look at the other part of this answer below.

The following is an adaptation of your original to do this...

int main(int argc, char *argv[]) {
    int number = 0;
    int square = 0;

    if(argc != 2) 
    {
          printf("%s\n","Usage prog.exe <nnn>, where nnn is an integer value.\nProgram will exit.");
    }
    else
    {
        char *ptr = argv[1];    

        while(*ptr != '\0') 
        {
            number = *ptr - '0';
            square = number * number;
            printf("%d\n", square);//write() not available on my system, replace as necessary
            square++;
            ptr++;
        }
    }

    return 0;
}

So, for example given prog.exe "1234", the output is:

enter image description here

ryyker
  • 22,849
  • 3
  • 43
  • 87
  • 2
    At this line the previous value of `square` is not important. It is important in the line before. – Eugene Sh. May 21 '20 at 14:43
  • 1
    @EugeneSh. - Yes, I was getting to that, i.e. explaining that `argv[1]` is not a number yet. Still editing. Thanks. – ryyker May 21 '20 at 14:46
  • 1
    The last printf is not necessary . It just prints the incremented square. Also you shouldn't use printf as stated in the OP's question. Please use write instead. – Parsa Mousavi May 21 '20 at 15:26
  • 1
    Yes, I had already made that adjustment to my code, but had not edited in at the time of your comment, (as indicated by the image.) Thanks. – ryyker May 21 '20 at 15:29
  • 1
    @chux-ReinstateMonica - Hmmm. A couple of questions, why start off with `int a = 1`, and not `int a = 0`?. But as well, I am not sure why after setting `chr *ptr = argv[1]`, that it would not be completely valid to increment through the string until `*ptr == NULL`. – ryyker May 21 '20 at 16:01
  • 2
    @ryyker The open file descriptor is actually 1 ( which means "STDOUT") .And actually causes the write to print out into the console. Default file descriptors is STDIN , STDOUT , STDERR . ( 0 , 1 , 2 respectively) – Parsa Mousavi May 21 '20 at 16:14
  • 1
    @chux-ReinstateMonica - I did neglect to put a test in to verify `argc == 2` before asserting that `argv[1]` even existed, so I will do that, but other than that I am afraid I am not following what you are saying. I am setting *ptr to point to `argv[1]`, not `argv[0]`. And it sounds like you are asserting that there is no guarantee that even if `argc == 2`, that `argv[1]` exits? – ryyker May 21 '20 at 16:15
  • 1
    @ParsaMousavi - Thanks for that clarification! – ryyker May 21 '20 at 16:16
  • 1
    @chux-ReinstateMonica - I think I just saw what you were saying, I am intending to walk through the individual characters of the first command line argument ( `argv[1]` ) to square each of the individual ASCII values, corrected for its intended integer value. (i.e. for "1234", the first conversion will be `'1' - '0' ==> 1, ... '4' - '0' ==> 4` That is I was not trying to index through the arguments, I was intending to index through the individual `char` of the string. – ryyker May 21 '20 at 16:22
  • 1
    @chux-ReinstateMonica - LOL, an artifact of an earlier iteration of my attempt to understand OP intent. (that resulted in blowing up `number = argv[1][square];`) Thanks. Edited out. – ryyker May 21 '20 at 16:29
  • 1
    @chux-ReinstateMonica - `write()` is not available in my system, so replaced with `printf()`, OP will have to replace it, as I noted in my answer. – ryyker May 21 '20 at 16:45
  • 1
    @chux-ReinstateMonica - got you. I will comment the replacement. Thanks. – ryyker May 21 '20 at 16:47
  • 1
    @chux-ReinstateMonica - do you suppose we can clean up our conversation here.? – ryyker May 21 '20 at 17:33
  • 1
    @chux-ReinstateMonica - Yes, you are right, will change it to an appropriate value. Thanks again! (I've made that mistake too often.) – ryyker May 21 '20 at 17:42
1

The completed code should look like this :

#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

int main(int argc, char **argv) {
    int number;
    int square;

    if(argc == 2) {
        number = atoi(argv[1]);
        square = number * number;
        char sq_buffer[12];
        sprintf(sq_buffer , "%d" , square);
        strcat(sq_buffer , "\n");
        write(1, (const void *) sq_buffer, strlen(sq_buffer)); 
    }else{
        char message[100];
        strcpy(message , "Please specify exactly one argument \n");
        write(1 , (const void *)message , strlen(message));
    }
   return 0;
}
Parsa Mousavi
  • 1,052
  • 1
  • 13
  • 31
  • @ryyker I think there's a problem with the OP's question . If the intention was to square each ASCII value , then at the first glance the while condition is wrong because it causes an infinite loop , and the second problem is with the "argv[1][square]" .It would cross the boundaries of argv. – Parsa Mousavi May 21 '20 at 15:19
  • @ryyker If that was the intention , then yes your answer is right. – Parsa Mousavi May 21 '20 at 15:28
  • @chux-ReinstateMonica OK. Just forgot the newline.Thanks. – Parsa Mousavi May 21 '20 at 16:36
1

When you say that you are not allowed to use 'printf', does that also include 'fprintf' and 'sprintf'? If so, then I would solve this problem like this:

void  main( int argc, char **argv )
{
    int     i;
    int     j;
    int     square;
    int     value;
    char    buffer[256];

    for (i = 1; i < argc; i++) {

        /* Get the next integer from the args to the program */
        value = atoi(argv[i]);

        /* Calculate the square of that value */
        square = value * value;

        /* Now convert it to ASCII */
        itoa(square, buffer, 10);

        /* Write out the data */
        write(1, buffer, strlen(buffer));

        /* You could also use putchar() for it, like this: */
        j = 0;
        while (buffer[j] != '\0')
            putchar(buffer[j++]);
    }
}

If he doesn't want you using itoa(), then you could write your own int to ASCII conversion routine like this:

char  *IntToASCII( int  x )
{
    static char    buffer[256];
    char           *ptr;
    int            neg;
    char           *digits = "0123456789";
    unsigned long  tempX;

    neg = (x < 0);
    tempX = x;
    if (neg) {
        tempX *= -1;
    }

    ptr = buffer + sizeof(buffer) - 1;
    *ptr = '\0';
    do {
        ptr--;
        *ptr = digits[tempX % 10];
        tempX /= 10;
    } while (tempX > 0);

    if (neg)
        *ptr = '-';

    return (ptr);
}

Or you can make 'buffer' an input parameter and remove the static declaration for it.

  • 1
    Your answer has several minor errors please consider rectify them. First "*ptr = '\0 " needs the closing single quote . Second you should add type specifier ( for your purpose "int" ) before " j = 0" . – Parsa Mousavi May 21 '20 at 16:35
  • 1
    Thanks @ParsaMousavi... Typos that my old eyes didn't catch... Big monitor, but even with that the text seems small these days... :( The code was so simple that I didn't bother putting it into a file and compiling it... Ooops... – Grumpy OldMan May 21 '20 at 16:40
  • 1
    Note that `IntToASCII(INT_MIN)` leads to undefined behavior in `x *= -1;` and `digits[x % 10];`. `int signed;` is a dodgy use of a keyword. – chux - Reinstate Monica May 21 '20 at 16:41
  • 1
    @GrumpyOldMan OK. But there is also another minor problem which is you shouldn't use "signed" which is a standard keyword in C as a variable name. Wish you all the best :) – Parsa Mousavi May 21 '20 at 16:44
  • 1
    @ParsaMousavi -- Thanks again... Changed it to 'neg' instead... And this time, I actually ran it through gcc to ensure it would compile... Guess I got a bit of egg on my old face from that one... :) chux-ReinstateMonica -- Changed it to an unsigned long inside the routine which I think will keep it from running into that possible problem. – Grumpy OldMan May 21 '20 at 16:53
  • @chux-ReinstateMonica -- You caught the code while I was in the middle of updating it... Your caffeine level is probably a bit higher than mine is right now. :) – Grumpy OldMan May 21 '20 at 17:01
  • "Changed it to an unsigned long inside the routine which I think will keep it from running into that possible problem." --> it does avoid UB, but can generate the wrong answer - trouble being that `tempX *= -1;` is like `tempX *= ULONG_MAX;` Perhaps `if (x < 0) tempX = 0u - x; else tempX = x;` or the like. – chux - Reinstate Monica May 21 '20 at 17:05
  • @chux-ReinstateMonica -- I just realized something... For this particular problem as stated by the OP, there is no way that it would be called with a negative value since the number that is passed has been squared... I still should look into portion that you pointed out though. – Grumpy OldMan May 21 '20 at 17:21
  • "there is no way that it would be called with a negative value" --> Maybe a way. With `square = value * value;`, code does not prevent overflow - which for `int` math is _undefined behavior_ - so the product could be anything - if code continues. [This](https://stackoverflow.com/a/40027745/2410359) might be of interest. – chux - Reinstate Monica May 21 '20 at 17:25
0
#define STDOUT_FILENO    1
#define MAX_INT_DIGITS   (10+1)  /*+1 for string null terminating*/
int main(int argc, char *argv[]) 
{
  int number=0, square=0;
  char ret_num[MAX_INT_DIGITS];

  for (int i =1; i < argc; ++i) 
  {
    number = atoi(argv[i])
    square = number * number;
    itoa(square, ret_num, 10);
    write(STDOUT_FILENO, ret_num, strlen(ret_num));
    /*write new line*/
    write(STDOUT_FILENO, '\n', 1);
  }


return 0;
}
Adam
  • 2,820
  • 1
  • 13
  • 33
  • 1
    you can pass what ever you like to the write function! its and const void* can take a const pointers and non const pointers (by the way any kind of pointer cos its a generic pointer i.e. void*), the const in the write function just to tell the user that this function (write) doesnot change its value!! some promise to the caller. regarding how this will be shown on screen you are write. i need to convert it back to string! itoa function would do the work. thanks for the comment. i want to give her a blitz answer. this is a minor issue. i will fix it. if you like it push the vote up :-) – Adam May 21 '20 at 15:49
  • 1
    I didn't mean the problem was with "const " the actual problem is "void *" . I mean you're using int * instead of void * which results in undesired behavior at least in my compiler (GCC) . Which compiler do you use ? – Parsa Mousavi May 21 '20 at 15:54
  • 1
    again, this is have nothing to do with the compiler! void* is a generic pointer that can points to any address! int* or char* or STRUCT* all of them are addresses! void* can point at any one of them! – Adam May 21 '20 at 15:57
  • 1
    @chux-ReinstateMonica - you are right. i will fix it. my bad. thanks for correcting me. – Adam May 21 '20 at 15:59
  • 1
    @Adam Yes.You're right . That was actually the "sizeof " that caused that problem.And please add semicolon after "number = atoi(argv[i])" and also itoa is not a standard C stuff. You can use sprintf instead.And also please include "unistd.h" for the "write" function. – Parsa Mousavi May 21 '20 at 16:10
  • 1
    @chux-ReinstateMonica- we can use the strlen() for the string! – Adam May 21 '20 at 16:12
  • 1
    `write(STDOUT_FILENO, '\n', sizeof('\n'));` has 2 problems. 2nd arg should be a pointer. 3rd is 4. I suspect you wanted 1. – chux - Reinstate Monica May 21 '20 at 16:23
  • 1
    Yes. Posting a fix. Thanks – Adam May 21 '20 at 16:26
  • 1
    @ParsaMousavi [You shouldn't use " int* " as the second argument to "write" function.](https://stackoverflow.com/questions/61936999/c-program-to-calculate-numbers-squared-arguments-segmentation-fault/61937076?noredirect=1#comment109548691_61937265) is not so in C. Conversion from `int *` to `const void *` is well defined. Cast not needed. – chux - Reinstate Monica May 21 '20 at 16:37