質問

I'm getting a seg. fault when I try to subtract 32 from a char type (trying to convert to lowercase without tolower() in C. I have done the prerequisite searching for relevant Q/A threads with no luck. I also tried 'a' - 'A' for the conversion value, '32', casting it as (char*) and anything else I could think of. For an example:

char* s1 = "Bob";

if (*s1 >= 97 && *s1 <= 122)
     *s1 -= 32;
}

Any advice?

Edit:

After following the help below, I still get the error. (For this example, I am only trying to change the first letter of the name to lowercase.) Here is what I am trying:

 char* s1 = "Bob";
 printf("\n %s before", s1);

 // below I call my string length function to get actual size instead of 100
 char* temp = malloc(100);   
 temp = s1;

 if (*temp >= 'A' && *temp <= 'Z'){
    *temp -= 32;
 }

 printf("\n%s after", temp);
 free(temp);

Also, why do I need to allocate memory for a string that already is in memory?

役に立ちましたか?

解決 2

There are a number of problems with your code.

char* s1 = "Bob";

A string literal creates a read-only array of char; this array is static meaning that it exists for the entire lifetime of your program. For historical reasons, it's not const, so the compiler won't necessarily warn you if you attempt to modify it, but you should carefully avoid doing so.

s1 points to the first character of that array. You may not modify *s1. For safety, you should declare the pointer as const:

const char *s1 = "Bob";

If you want a modifiable character array, you can create it like this:

char s1[] = "Bob";

Now let's look at the remaining code:

if (*s1 >= 97 && *s1 <= 122)
     *s1 -= 32;
}

97 and 122 are the numeric ASCII codes for 'a' and 'z'. 32 is the difference between a lower case letter and the corresponding upper case letter -- again, in ASCII.

The C language doesn't guarantee that characters are represented in ASCII, or in any of the character sets that are compatible with it. On an IBM mainframe, for example, characters are represented in EBCDIC, in which the codes for the letters are not contiguous (there are gaps), and the difference between corresponding lower case and upper case letters is 64, not 32.

EBCDIC systems are rare these days, but even so, portable code tends to be clearer than non-portable code, even aside from any practical issues of whether the code will work on all systems.

As I'm sure you know, the best way to do this is to use the tolower function:

*s1 = tolower((unsigned char)*s1);

Note the cast to unsigned char. The to*() and is*() functions declared in <ctype.h> are oddly behaved, for historical reasons. They don't work on char arguments; rather, they work on int arguments that are within the range of unsigned char. (They also accept EOF, which is typically -1). If plain char is signed, then passing a char value that happens to be negative causes undefined behavior. Yes, it's annoying.

But you say you don't want to use tolower. (Which is fine; learning to do things like this yourself is a good exercise.)

If you're willing to assume that upper case letters are contiguous, and that lower case letters are contiguous, then you can do something like this:

if (*s1 >= 'a' && *s1 <= 'z') {
    *s1 -= 'a' - 'A';
}

That's still not portable to non-ASCII systems, but it's a lot easier to read if you don't happen to have the ASCII table memorized.

It also makes it a little more obvious that you've got the logic backwards. You say you want to convert to lower case, but your code converts from lower case to upper case.

Or you can use a lookup table that maps lower case letters to upper case letters:

char to_lower[CHAR_MAX] = { 0 }; /* sets all elements to 0 */
to_lower['A'] = 'a';
to_lower['B'] = 'b';
/* ... */
to_lower['Z'] = 'z';

Or, if your compiler supports compound literals:

const char to_lower[CHAR_MAX] = {
    ['A'] = 'a',
    ['B'] = 'b',
    /* ... */
};

I'll leave it to you to fill in the rest write the code to use it.

And now you can see why the tolower and toupper functions exist -- so you don't have to deal with all this stuff (apart from the odd unsigned char casts you'll need).

UPDATE :

In response to the new parts of your question:

char* temp = malloc(100);   
temp = s1;

That assignment temp = s1; doesn't copy the allocated string; it just copies the pointer. temp points to 100 bytes of allocated space, but then you make temp point to the (read-only) string literal, and you've lost any references to the allocated space, creating a memory leak.

You can't assign strings or arrays in C. To copy a string, use the strcpy() function:

char *temp = malloc(100);
if (temp == NULL) {     /* Don't assume the allocation was successful! */
    fprintf(stderr, "malloc failed\n");
    exit(EXIT_FAILURE);
}
strcpy(temp, s1);

Also, why do I need to allocate memory for a string that already is in memory?

It's in memory, but it's memory that you're not allowed to modify. If you want to modify it, you need to copy it to a modifiable location. Or, as I suggested above, you can put it in read/write memory in the first place:

char s[] = "Bob";

That initialization copies the string into the array s.

他のヒント

You can't alter literal strings like that - they are (usually) in read-only memory. You need to make a writeable copy of the string literal:

char* name = "Bob";
char* s1 = strdup(name);

...

free(s1);  // And you also need this to avoid a memory leak!

Initialize char and use malloc to allocat memory to store all string and than use for loop and convert whole string in lower case.

You need to

  1. Allocate buffer
  2. Copy string "Bob" to your buffer
  3. Loop through the string, editing it as you go.

This fails because string literals are usually stored in read-only memory.

The easiest fix is to use the literal to initialize an array, the array will be modifiable (unless explicitly made const so don't do that):

char s1[] = "Bob";

Also, it's very bad form to hardcode ASCII, use the islower() and tolower() functions from <ctype.h> to make this code proper.

char *s1 = "Bob";

is creating a pointer to a string constant. That means the string "Bob" will be somewhere in the read-only part of the memory and you just have a pointer to it. You can use the string as read-only. You cannot make changes to it.
Example:

s1[0] = 'b';

Is asking for trouble.

To make changes to s1 you should allocate memory for it

s1 = malloc(10); //or what you want

Now changes to s1 can be done easily.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top