I'm having trouble converting argv to upper in C

358 views Asked by At

I'm trying to convert a fixed size argv to upper and to lower but It either gives me segmentation fault or it just stores gibberish on the var. I've tried this:

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

int main(int argc, string argv[])
{
    string lower = "abcdefghijklmnopqrstuvwxyz";
    string upper = "abcdefghijklmnopqrstuvwxyz";

    for (int i = 0, n = 26; i < n; i++)
    {
        lower[i] = tolower(argv[1][i]);
        upper[i] = toupper(argv[1][i]);
    }
}

or:

string lower = "";
string upper = "";

//check if repeated chars or numbers
for (int i = 0, n = 26; i < n; i++)
{
    printf("%c\n",(char) argv[1][i]);
    printf("%c\n",(char) argv[1][i]);
    printf("%c\n",tolower(argv[1][i]));
    printf("%c\n",toupper(argv[1][i]));
    lower += tolower(argv[1][i]);
    upper += toupper(argv[1][i]);
}

The strangest part is that the printf's give the right values but they arent getting stored in lower or upper. What n00b thing am I doing here?

2

There are 2 answers

1
Eric Postpischil On BEST ANSWER

You cannot use += in C to append to a string.

<cs50.h> defines string to be char *. Then string lower = ""; defines lower to be a char * and sets it to point to the first character of "". "" represents a null string, which is a string whose first and only character is the null (zero value) character, indicating the end of the string.

When applied a pointer, += n attempts to advance the pointer by n elements of the type it points to. For example, if a pointer p is pointing to element 3 of an array, p += 4 will make it point to element 7.

Since lower points to an element of an array with only one element, the null character, lower += tolower(argv[1][i]) attempts to make it point outside the array. The behavior is not defined by the C standard. (Except it could be in a C implementation in which tolower applied to that character yields a character with value 1. This would modify lower to point just beyond the last element in the array, which is a special position that is allowed for pointer arithmetic.)

With string lower = "abcdefghijklmnopqrstuvwxyz";, you have more room to do the array arithmetic; some additions to lower will work. However, again, tolower(argv[1][i]) is likely too big to remain inside the array.

Further, even if the pointer arithmetic works, lower += … only changes the pointer. It does not change the characters it points to.

To make your code work, you need to allocate enough memory to hold the resulting string, including the null character, and you must set lower to point to that memory, and similarly for upper. The number of characters you need is strlen(argv[1][i]) + 1. If you have learned how to use malloc, you can use that. If you have a fixed assignment that guarantees that argv[1] will be exactly 26 characters, you can define lower and provide memory for it by declaring it with char lower[27];, and similarly for upper.

Then, you must copy characters into that memory, which you can do with lower[i] = tolower(argv[1][i]);. When you are done copying characters into the array, you should write a null character at the end of it, to form a complete C string. (If you are not going to print the array as a string or otherwise use it as a string, you can omit this.)

0
Thomas On

If you invoke man toupper you'll learn that the function does not take a string but rather a character. So, one way to achieve what you're after is to loop over every character in your command line argument, call toupper on it, and store the result back.

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

void main(int argc, char *argv[])
{
  printf("before: %s\n", argv[1]);

  for (unsigned int i = 0, n = strlen(argv[1]); i < n; i++) {
    argv[1][i] = (char) toupper(argv[1][i]);
  }

  printf("after:  %s\n", argv[1]);  
}