A question about pointer of array.

6

66650755

Now,I have this pointer of array:
char *Ptr[SIZE]={"HeArd","DiaMoNd","SprAde","AbC"};

I'd like to conver all the upper-case letters to lower-case ones.So I
writted the follwing program:

#include<stdio.h>
#include<ctype.h>
#define SIZE 4

void convertolower(char *Ptr[],int size);

int main()
{
char *Ptr[SIZE]={"HeArd","DiaMoNd","SprAde","AbC"};
int i;

convertolower(Ptr,SIZE);

for(i=0;i<=3;i++)
{
printf("%s\n",Ptr);
}

return 0;
}

void convertolower(char *Ptr[],int size)
{
int i;
int k;

for(i=0;i<=3;i++)
{

for(k=0;*(Ptr)!='\0';Ptr++,k++)
{
if( isupper( *Ptr ) )
{
*Ptr = tolower( *Ptr );
}
}
Ptr-=k;//let Ptr point back to the start of it's corresponding
string
}

}




When I debug it,and come to this centence:
*Ptr = tolower( *Ptr );

the compiler said:access violation.

How come? And is there any other means to conver the upper-case letter
to lower ones?
 
I

Ian Collins

void convertolower(char *Ptr[],int size)
{
int i;
int k;

for(i=0;i<=3;i++)

Where does 3 come from?
{

for(k=0;*(Ptr)!='\0';Ptr++,k++)


That looks horribly complicated.

How about a simplification:

void convertolower(char *Ptr[],int size)
{
for( int i=0; i < size; ++i )
{
for( char* c = Ptr; *c != NULL; ++c )
{
if( isupper( *c ) )
{
*c = tolower( *c );
}
}
}
}
When I debug it,and come to this centence:
*Ptr = tolower( *Ptr );

The code you posted looked OK, was it the same code you ran?
 
K

Keith Thompson

Now,I have this pointer of array:
char *Ptr[SIZE]={"HeArd","DiaMoNd","SprAde","AbC"};

I'd like to conver all the upper-case letters to lower-case ones.So I
writted the follwing program:

#include<stdio.h>
#include<ctype.h>
#define SIZE 4

void convertolower(char *Ptr[],int size);

int main()
{
char *Ptr[SIZE]={"HeArd","DiaMoNd","SprAde","AbC"}; [SNIP]

When I debug it,and come to this centence:
*Ptr = tolower( *Ptr );

the compiler said:access violation.

How come? And is there any other means to conver the upper-case letter
to lower ones?


Ptr is an array of pointers to char; each element points to (the first
character of) a string. You initialize each pointer with a string
literal, and then try to modify the contents of the string. Though
string literals aren't "const" (for historical reasons), attempting to
modify a string literal invokes undefined behavior.

You could declare it something like this:

char s0[] = "HeArd";
char s1[] = "DiaMoNd";
char s2[] = "SprAde";
char s3[] = "AbC";
char *Ptr[] = (s0, s1, s2, s3);

(Or perhaps there's a neater way to do this.)
 
B

Ben Bacarisse

Now,I have this pointer of array:
char *Ptr[SIZE]={"HeArd","DiaMoNd","SprAde","AbC"};

I'd like to conver all the upper-case letters to lower-case ones.So I
writted the follwing program:

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

Be generous with spaces.
#define SIZE 4

void convertolower(char *Ptr[],int size);

int main()

int main(void) is better.
{
char *Ptr[SIZE]={"HeArd","DiaMoNd","SprAde","AbC"};

The heart of your problem lies here. In C, string literals like these
can't be modified. One way to test your function would be to
initialise some automatic (local) arrays and point to those:

char str1[] = "HeArd";
char str2[] = "...";
...
char *Ptr[SIZE] = { str1, str2, ... };
int i;

convertolower(Ptr,SIZE);

for(i=0;i<=3;i++)

3? Why 3? Also, most people prefer the idiom:

for (i = 0; i < array_size; i++)

rather than using <= max_index.
{
printf("%s\n",Ptr);
}

return 0;
}

void convertolower(char *Ptr[],int size)
{
int i;
int k;

for(i=0;i<=3;i++)


3 again!
{

for(k=0;*(Ptr)!='\0';Ptr++,k++)


More spaces please!
{
if( isupper( *Ptr ) )


For technical reasons you should write:

if (isupper( (unsigned char)*Ptr ))

char can be a signed type, and passing a negative number to isupper is
incorrect (except that EOF is permitted).
{
*Ptr = tolower( *Ptr );
}


Ditto here. Also, there is no need to test for isupper because
tolower has to do that anyway -- tolower has no effect on
non-upper-case characters so you can just call it for all the
characters if you like. (Very old code used to have this test before
standard C implementations came along.)
}
Ptr-=k;//let Ptr point back to the start of it's corresponding
string
}

}

When I debug it,and come to this centence:
*Ptr = tolower( *Ptr );

the compiler said:access violation.


It is not likely to be the compiler, but I know what you mean. The
error is a run-time one. The compiler finished its job long ago.
How come? And is there any other means to conver the upper-case letter
to lower ones?

I would write a str_tolower function first. Then I'd call that in a
loop. Your function is overly specific for my taste.
 
B

Ben Bacarisse

Ptr is an array of pointers to char; each element points to (the first
character of) a string. You initialize each pointer with a string
literal, and then try to modify the contents of the string. Though
string literals aren't "const" (for historical reasons), attempting to
modify a string literal invokes undefined behavior.

You could declare it something like this:

char s0[] = "HeArd";
char s1[] = "DiaMoNd";
char s2[] = "SprAde";
char s3[] = "AbC";
char *Ptr[] = (s0, s1, s2, s3);

(Or perhaps there's a neater way to do this.)

Yes, I thought there must be too. In C99 (the OP uses some C99) you
can do this:

char *Ptr[] = {
(char []){"HeArd"},
(char []){"DiaMoNd"},
(char []){"SprAde"},
(char []){"AbC"}
};

which avoids the unnecessary names but forces C99. I don't claim it
is "obviously neater" but I have grown fond of compound literals if
only for writing unit tests -- you can build a wide range of test
cases using them.
 
I

Ian Collins

Anthony said:
This is how I'd do it (complete with all the OPs unused variables and such):

#include<stdio.h>
#include<ctype.h>
#define SIZE 4
#define MAXSTRLEN 8

void convertolower(unsigned char Ptr[SIZE][MAXSTRLEN], int size);
Why unsigned char?
void convertolower(unsigned char Ptr[SIZE][MAXSTRLEN], int size) {

int i;
int k;
unsigned char *p;

for(i=0; i<SIZE; i++){

p = &Ptr[0];


Why make a simple assignment so complicated?
 
I

Ian Collins

pete said:
That kind of reminds me of: if (a != 0) a = 0;
:)

for( int i=0; i < size; ++i )
{
for( char* c = Ptr; *c != NULL; ++c )
{
*c = tolower( *c );
}
}
 
I

Ian Collins

Anthony said:
Ian said:
Anthony said:
This is how I'd do it (complete with all the OPs unused variables
and such):

#include<stdio.h>
#include<ctype.h>
#define SIZE 4
#define MAXSTRLEN 8

void convertolower(unsigned char Ptr[SIZE][MAXSTRLEN], int size);
Why unsigned char?

Why signed?

Who said anything about signed? char, signed char and unsigned char are
distinct types.
 
I

Ian Collins

blargg said:
Ian Collins wrote:
[...]
for( int i=0; i < size; ++i )
{
for( char* c = Ptr; *c != NULL; ++c )
{

Why the comparison with NULL? Seems like it would only confuse a learner,
and not compile with some definitions of NULL.


Fair point, it should be '\0'.
 
K

Keith Thompson

Anthony Fremont said:
Ben said:
Keith Thompson said:
Ptr is an array of pointers to char; each element points to (the
first character of) a string. You initialize each pointer with a
string literal, and then try to modify the contents of the string.
Though string literals aren't "const" (for historical reasons),
attempting to modify a string literal invokes undefined behavior.

You could declare it something like this:

char s0[] = "HeArd";
char s1[] = "DiaMoNd";
char s2[] = "SprAde";
char s3[] = "AbC";
char *Ptr[] = (s0, s1, s2, s3);

(Or perhaps there's a neater way to do this.)

Yes, I thought there must be too. In C99 (the OP uses some C99) you
can do this:

char *Ptr[] = {
(char []){"HeArd"},
(char []){"DiaMoNd"},
(char []){"SprAde"},
(char []){"AbC"}
};

which avoids the unnecessary names but forces C99. I don't claim it
is "obviously neater" but I have grown fond of compound literals if
only for writing unit tests -- you can build a wide range of test
cases using them.

This is how I'd do it (complete with all the OPs unused variables and such):

#include<stdio.h>
#include<ctype.h>
#define SIZE 4
#define MAXSTRLEN 8

void convertolower(unsigned char Ptr[SIZE][MAXSTRLEN], int size);

int main(void) {

unsigned char Ptr[SIZE][MAXSTRLEN] = {"HeArd","DiaMoNd","SprAde","AbC"};
[snip]

The disadvantage is that it allocates the maximum size for each
string. (And plain char would make more sense here than unsigned
char.)
 
T

Tomás Ó hÉilidhe

I've read a few of the replies in this thread and I was surprised to
see that nobody cast the "char" to an "unsigned char" before they
called tolower.
 
B

Ben Bacarisse

Tomás Ó hÉilidhe said:
I've read a few of the replies in this thread and I was surprised to
see that nobody cast the "char" to an "unsigned char" before they
called tolower.

Four people replied with code that included such a call. Two did not
cast; one did not need to; and one wrote:

| For technical reasons you should write:
|
| if (isupper( (unsigned char)*Ptr ))
|
| char can be a signed type, and passing a negative number to isupper is
| incorrect (except that EOF is permitted).

That is very far from "nobody".
 
P

Phil Carmody

Ben Bacarisse said:
Keith Thompson said:
Ptr is an array of pointers to char; each element points to (the first
character of) a string. You initialize each pointer with a string
literal, and then try to modify the contents of the string. Though
string literals aren't "const" (for historical reasons), attempting to
modify a string literal invokes undefined behavior.

You could declare it something like this:

char s0[] = "HeArd";
char s1[] = "DiaMoNd";
char s2[] = "SprAde";
char s3[] = "AbC";
char *Ptr[] = (s0, s1, s2, s3);

(Or perhaps there's a neater way to do this.)

Yes, I thought there must be too. In C99 (the OP uses some C99) you
can do this:

char *Ptr[] = {
(char []){"HeArd"},
(char []){"DiaMoNd"},
(char []){"SprAde"},
(char []){"AbC"}
};

which avoids the unnecessary names but forces C99. I don't claim it
is "obviously neater" but I have grown fond of compound literals if
only for writing unit tests -- you can build a wide range of test
cases using them.

I wouldn't claim it's neat, but I certainly think it classifies
as *neater*, as it doesn't put anything unneccesary in any
namespace.

I'm converted!

Phil
 
P

Phil Carmody

Anthony Fremont said:
Ian said:
Anthony said:
Ian Collins wrote:
Anthony Fremont wrote:
This is how I'd do it (complete with all the OPs unused variables
and such):

#include<stdio.h>
#include<ctype.h>
#define SIZE 4
#define MAXSTRLEN 8

void convertolower(unsigned char Ptr[SIZE][MAXSTRLEN], int size);

Why unsigned char?

Why signed?

Who said anything about signed? char, signed char and unsigned char
are distinct types.

Left unspecified, char is either signed or unsigned in the OPs
implementation, there is no third possibility.

There's no other possibility for numerical behaviour, but that
doesn't mean that there's no other possibility for the C type
used. If you're unable to distinguish C types from numerical
behaviour, you're going to get confused very easily. Do you not
agree that a 32-bit int is a different type from a 32-bit long?

Phil
 
K

Keith Thompson

Anthony Fremont said:
Ian said:
Anthony said:
Ian Collins wrote:
Anthony Fremont wrote:
This is how I'd do it (complete with all the OPs unused variables
and such):

#include<stdio.h>
#include<ctype.h>
#define SIZE 4
#define MAXSTRLEN 8

void convertolower(unsigned char Ptr[SIZE][MAXSTRLEN], int size);

Why unsigned char?

Why signed?

Who said anything about signed? char, signed char and unsigned char
are distinct types.

Left unspecified, char is either signed or unsigned in the OPs
implementation, there is no third possibility. What is wrong with
specifying unsigned when it's text strings? Would it be better to cast to
unsigned for the tolower() call?

The type char is distinct from either signed char or unsigned char,
though it has the same representation as one of them. The
implementation chooses whichever representation is more convenient for
textual data. Since it's being used to store textual data here, plain
char is the appopriate type. And yes, it would be better to cast to
unsigned char for the tolower() call.

Plain char is appropriate for textual data. unsigned char is
appropriate for raw byte data, or when you need a very small unsigned
type. signed char is approprriate when you need a very small signed
type (which isn't very often).
 
J

James Kuyper

Anthony said:
Ian said:
Anthony said:
Ian Collins wrote:
Anthony Fremont wrote:
This is how I'd do it (complete with all the OPs unused variables
and such):

#include<stdio.h>
#include<ctype.h>
#define SIZE 4
#define MAXSTRLEN 8

void convertolower(unsigned char Ptr[SIZE][MAXSTRLEN], int size);

Why unsigned char?
Why signed?
Who said anything about signed? char, signed char and unsigned char
are distinct types.

Left unspecified, char is either signed or unsigned in the OPs
implementation, there is no third possibility. What is wrong with
specifying unsigned when it's text strings? Would it be better to cast to
unsigned for the tolower() call?

Most the C standard library's string handling routines have char*
interfaces; choosing unsigned char complicates the use of those functions.

Use char for text. Signed and unsigned char should be only used when
you're using them as small integers, rather than as text characters.
 
J

James Kuyper

Anthony said:
AIUI, char types are _either_ signed or unsigned depending upon the
implementation. I'm trying to perceive some advantage to leaving it
unspecified, but I can't.

The type "char" qualifies as neither signed nor unsigned, per the C
standard's definitions of those categories 6.2.5p4,p6. It qualifies as
an integer type only because it it explicitly listed in p17, not by
reason of qualifying for membership in either of the other two integer
sub-categories.

But that's just a curiosity of the way the standard defines those
categories.

I'm not sure whether you're unclear of the advantages of the standard
leaving 'char' unspecified, or the advantages of using 'char' as opposed
to 'signed char' or 'unsigned char'. In the first case, the advantage at
the time of standardization was that many existing C compilers defined
'char' as a signed type, while many others defined it as an unsigned
type. In both cases the choice was often made because of differences in
the underlying hardware. Mandating either choice would have made it
difficult and expensive to provide a conforming implementation of C on
many platforms, or would at least have required major re-writes to a lot
of existing code.

I've explained the key advantage of using plain 'char' in another message.
Perhaps there is a difference in some weird C semantical kind of way, but in
the "real world" no. I can't envision any real difference in behavior on
any platform I can think of. ...

You should get diagnostics in many contexts if you treat int and long as
identical types, even on platforms where they have identical size,
alignment requirements, and representations. The same is true when
comparing 'char' vs. 'signed char' or 'unsigned char'.
... It seems to me that C goes to lengths to try
and hide the true size of objects behind vague identifiers like "long" or
"short", yet the standard only guarantees that "short" is no longer than
"long". Not particularly helpful from a portability standpoint IMO.

Those features are intended to enable efficient implementation of C on a
wide variety of platforms. That flexibility is inherently at the cost of
the ease of writing portable code. However, the new <stdint.h> types
allow you to be more specific about the sizes of the types you want.
 
J

James Kuyper

Anthony said:
Keith said:
unsigned char Ptr[SIZE][MAXSTRLEN] =
{"HeArd","DiaMoNd","SprAde","AbC"}; [snip]
The disadvantage is that it allocates the maximum size for each
string. (And plain char would make more sense here than unsigned
char.)

Why would "plain" char make more sense? Leaving it unspecified leaves it up
to the implementation and means that I would need to cast it to unsigned
anyway. I don't get it.

Why would you need to convert to unsigned?
 
R

Richard

CBFalconer said:
I think the point is that tolower will do nothing if c is not
upper.

Incorrect. tolower ALWAYS does "something". What you meant, and to be
pedantic as is the group remit, is that tolower will not be called. Two
totally different things.

In addition, it might well be more efficient to always just call
tolower() without the check in most cases.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,780
Messages
2,569,611
Members
45,270
Latest member
TopCryptoTwitterChannels_

Latest Threads

Top