newbie program, pointers again...

B

Barry Schwarz

It works now, so I think I'll just stick to that unless somebody
has any really great idea that I didn't see.

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

You don't need these two.
#include <stdio.h>

This is a duplicate.
#include <stdlib.h>
#include <string.h> // for memcpy

Except that you don't use memcpy in your program. But you do need it
for strlen and strcpy.
void repeat(char **dest_array, const char ch, const int n);
void add_text(char **dest_array, const char *source_array);

//////////////
int main()
{
char *char_ptr; /* pointer to beginning of char (array) */
char *destination_arr;

FILE *outputfile;
unsigned long size = 1600*sizeof(char); /* space for 20 lines
*/

char_ptr = malloc(size);
if (!char_ptr)
{
fprintf(stderr, "%s: line %d, malloc(%lu) failed.\n",
__FILE__, __LINE__, size);
exit(EXIT_FAILURE);
}

destination_arr = char_ptr;

printf("sizeof(char) = %i byte(s)\n", sizeof(char) );

sizeof evaluates to a size_t. A size_t is unsigned but need not be
int. For example, it could be unsigned long. %i is the wrong code
for unsigned anything. If you compiler doesn't support the z modifier
for %u then you should cast the value to match your format.
printf("\nPointer values:\n");
printf("char_ptr = %15p\n", char_ptr);

%p requires a void*. Since a char* is guaranteed to look like a
void*, this is probably not a problem. However it would be a good
habit to get into.
add_text(&destination_arr, "Howdy! ");
add_text(&destination_arr, "You crazy dumb computer!");
add_text(&destination_arr, "I really hate you!");
repeat(&destination_arr, '*', 10);
add_text(&destination_arr, "You stink, go away!");
repeat(&destination_arr, '-', 20);

if ( (outputfile=fopen("result.txt", "w") )==NULL)
{
printf("\nError opening output-file:
\"result.txt\".\n\n");
}
else
{
fprintf(outputfile, "%s", char_ptr);
}

printf("%s", char_ptr);
fclose(outputfile);
system("PAUSE");
return 0;
}


* functions */


void add_text(char **dest_array, const char *source_array)
{
sprintf(*dest_array, ("%s", source_array) );

printf("strlen(source_array) = %i\n", strlen(source_array) );
*dest_array += strlen(source_array);

Why compute strlen twice?
}

void repeat(char **dest_array, const char ch, const int n)
{
int i; /* local variable: counter */

for(i=0; i<n; i++) (*dest_array) = ch;


memset is probably a little more efficient.
(*dest_array)[n] = '\0'; /* terminate string */

*dest_array += n; /* update position */
}


Best regards / Med venlig hilsen
Martin Jørgensen


Remove del for email
 
F

Flash Gordon

Barry said:
On Wed, 15 Mar 2006 20:21:16 +0100, "Martin Joergensen"


Why compute strlen twice?

<snip>

sprintf returns the number of characters written (or a negative number
on error), so why not use that and don't call strlen at all?
 
M

Martin Joergensen

Barry Schwarz said:
On Wed, 15 Mar 2006 20:21:16 +0100, "Martin Joergensen"
-snip, thanks for the comments in the beginning -
sizeof evaluates to a size_t. A size_t is unsigned but need
not be
int. For example, it could be unsigned long. %i is the wrong
code
for unsigned anything. If you compiler doesn't support the z
modifier
for %u then you should cast the value to match your format.

Thanks a lot. Although it is probably "overkill" I changed it to:

printf("sizeof(char) = %lu byte(s)\n", (unsigned long)
sizeof(char) );
%p requires a void*. Since a char* is guaranteed to look like
a
void*, this is probably not a problem. However it would be a
good
habit to get into.

So I should change the line to:

printf("char_ptr = %15p\n", (void*) char_ptr); //???

Or how to deal with this?
Why compute strlen twice?

I guess that takes up even less source code than to declare a
local variable and copy the value into that? Okay, it's just 2
ekstra lines.... You would have preferred the temporary variable
solution?
}

void repeat(char **dest_array, const char ch, const int n)
{
int i; /* local variable: counter */

for(i=0; i<n; i++) (*dest_array) = ch;


memset is probably a little more efficient.


Strange..... That command doesn't seem to be in my about 1000
pages thick "C primer plus" book....I have memcpy and memmove
functions although, by looking in the index.

Is that a C99 thing?

-snip the rest-


Best regards / Med venlig hilsen
Martin Jørgensen
 
M

Martin Joergensen

Flash Gordon said:
<snip>

sprintf returns the number of characters written (or a negative
number on error), so why not use that and don't call strlen at
all?

Really???

My C-book doesn't say anything about that... I might test it out
tomorrow evening or so - not likely before that. Got a couple of
things to do today and I must also look at my "count columns+
rows from data-file" program now (see subject is another thread
here in this group).


Best regards / Med venlig hilsen
Martin Jørgensen
 
K

Keith Thompson

I haven't been following this thread very closely, but I just noticed
that that call to sprintf is incorrect.

It calls the sprintf() function with two arguments. The first is
*dest_array, which is fine (assuming there's enough space allocated).
The second argument is:
("%s", source_array)
That's a parenthesized comma expression. The first argument to the
comma operator, "%s", is evaluated and the result is discarded
(effectively a no-op); the value yielded is the value of the second
argument, source_array.

So the call is equivalent to:

sprintf(*dest_array, source_array);

which means that any format specifiers in source_array will be
interpreted -- presumably not what you want. You probably meant:

sprintf(*dest_array, "%s", source_array);

If you just want to copy the string, though, why use sprintf() with
its extra overhead? Just use strcpy():

strcpy(*dest_array, source_array);

The only drawback is that strcpy() doesn't return a useful value (it
just returns the value of its first argument), so if you want the
length you'll have to recompute it (or use a previously computed value
if you have it lying around). But I strongly suspect that the
overhead of sprintf() interpreting the format string is much more
expensive than a call to strlen(). And the strcpy() more clearly
expresses what you're trying to do, which is usually the most
important point.
<snip>

sprintf returns the number of characters written (or a negative number
on error), so why not use that and don't call strlen at all?

To be picky, it returns the number of characters written not counting
the terminating '\0'.
 
K

Keith Thompson

Martin Joergensen said:
"Barry Schwarz" <[email protected]> skrev i en meddelelse


Strange..... That command doesn't seem to be in my about 1000 pages
thick "C primer plus" book....I have memcpy and memmove functions
although, by looking in the index.

Is that a C99 thing?

No, memset() has been in the language at least since C89.

If you want a good C reference, try _C: A Reference Manual_ by
Harbison & Steele, 5th Edition.

(The definitive reference is the standard, of course, but it's not
written for newbies.)
 
?

=?ISO-8859-1?Q?Martin_J=F8rgensen?=

Keith said:
I haven't been following this thread very closely, but I just noticed
that that call to sprintf is incorrect.
Ok.

It calls the sprintf() function with two arguments. The first is
*dest_array, which is fine (assuming there's enough space allocated).
The second argument is:
("%s", source_array)
That's a parenthesized comma expression. The first argument to the
comma operator, "%s", is evaluated and the result is discarded
(effectively a no-op); the value yielded is the value of the second
argument, source_array.

So the call is equivalent to:

sprintf(*dest_array, source_array);

I tested it and it also works...
which means that any format specifiers in source_array will be
interpreted -- presumably not what you want. You probably meant:

sprintf(*dest_array, "%s", source_array);

What was the difference compared to strcpy(*dest_array, source_array),
again? The last %s-method interprets what it gets as a string. But it is
already a string since the function is called with:

add_text(&destination_arr, "Howdy! ");
If you just want to copy the string, though, why use sprintf() with
its extra overhead? Just use strcpy():

strcpy(*dest_array, source_array);

You're right. I changed it. Thanks.


-snip the rest-


Best regards / Med venlig hilsen
Martin Jørgensen
 
K

Keith Thompson

Martin Jørgensen said:
I tested it and it also works...

It may work in some cases.

Using ("%s", source_array) as an argument in a function call makes no
sense. The comma isn't a delimiter between function arguments, it's a
comma operator, which causes the "%s" to be completely ignored.

The second argument to sprintf() is a format string. To simplify the
discussion a bit, consider printf():

char *s1 = "hello, world\n";
char *s2 = "hello, world %d\n";
printf(s1);
printf(s2);

In both printf() calls, the argument is a format string. In the first
call, the string happens not to contain any '%' characters, so it's
just printed as-is. In the second call, the "%s" tells printf() to
expect a second argument that should be of type int; since you haven't
given it one, it invokes undefined behavior. (Most likely it will
print some garbage integer value, but anything could happen).

Both calls become safe if you use:

printf("%s", s1);
printf("%s", s2);

It's the same for sprintf(), except that in this case your sprintf()
call happens to be equivalent (other than the value returned) to
a simpler strcpy() call.

Never use something as a format string unless you're sure that it's a
valid format, and that you're actually providing all the arguments it
specifies.

The classic "hello, world" program uses:

printf("hello, world\n");

That's ok because you can see in the call itself that there are no '%'
characters. But if it were a variable:

char *s = "hello, world\n";
/* ... */
printf(s);

it would be very easy to modify the variable and break the call.
 
?

=?ISO-8859-1?Q?Martin_J=F8rgensen?=

Keith said:
discussion a bit, consider printf():

char *s1 = "hello, world\n";
char *s2 = "hello, world %d\n";
printf(s1);
printf(s2);

In both printf() calls, the argument is a format string. In the first
call, the string happens not to contain any '%' characters, so it's
just printed as-is. In the second call, the "%s" tells printf() to
expect a second argument that should be of type int; since you haven't
given it one, it invokes undefined behavior. (Most likely it will
print some garbage integer value, but anything could happen).

Both calls become safe if you use:

printf("%s", s1);
printf("%s", s2);

This is *exactly* why I did it the way I did.... I took whatever inside
the parenthesis as second argument:

sprintf(*dest_array, ("%s", source_array) );

But if you say it's wrong, I trust you... Besides that, I now use strcpy
as you proposed.
It's the same for sprintf(), except that in this case your sprintf()
call happens to be equivalent (other than the value returned) to
a simpler strcpy() call.
yep.

Never use something as a format string unless you're sure that it's a
valid format, and that you're actually providing all the arguments it
specifies.

The classic "hello, world" program uses:

printf("hello, world\n");

That's ok because you can see in the call itself that there are no '%'
characters. But if it were a variable:

char *s = "hello, world\n";
/* ... */
printf(s);

it would be very easy to modify the variable and break the call.

By using % inside the string?


Best regards / Med venlig hilsen
Martin Jørgensen
 
K

Keith Thompson

Martin Jørgensen said:
This is *exactly* why I did it the way I did.... I took whatever
inside the parenthesis as second argument:

sprintf(*dest_array, ("%s", source_array) );

But if you say it's wrong, I trust you... Besides that, I now use
strcpy as you proposed.

No, no, please don't just trust me. You should understand what's
wrong with the call yourself.

The first argument to sprintf() is a pointer to the destination
buffer. In your call

sprintf(*dest_array, ("%s", source_array) );

that's *dest_array, which is fine (assuming enough memory is
allocated).

The second argument is the format string. You're not passing "%s" as
the format string; you're passing source_array.

Your call:

sprintf(*dest_array, ("%s", source_array) );

is *very* different from what you probably intended:

sprintf(*dest_array, "%s", source_array);

In your call, "%s" is not a function argument; it's just an operand to
the comma operator, and it's effectively ignored.

Parentheses and commas are used for a number of different things. In
a function call, the outermost parentheses delimit the arguments to
the function, and the commas separate the arguments. In an expression
other than a function call, parentheses just form a parenthesized
expression, and a comma is a comma operator. You've used both forms
in a single statement. The compiler didn't complain because you
managed to construct a legal function call, but one that isn't what
you intended.

Sorry if I'm being redundant, but this is an important point.
 
?

=?ISO-8859-1?Q?Martin_J=F8rgensen?=

Keith said:
No, no, please don't just trust me. You should understand what's
wrong with the call yourself.

The first argument to sprintf() is a pointer to the destination
buffer. In your call

Got that...
sprintf(*dest_array, ("%s", source_array) );

that's *dest_array, which is fine (assuming enough memory is
allocated).

The second argument is the format string. You're not passing "%s" as
the format string; you're passing source_array.

Your call:

sprintf(*dest_array, ("%s", source_array) );

is *very* different from what you probably intended:

sprintf(*dest_array, "%s", source_array);

In your call, "%s" is not a function argument; it's just an operand to
the comma operator, and it's effectively ignored.

Why is it ignored then? That's what I didn't understand...
Parentheses and commas are used for a number of different things. In
a function call, the outermost parentheses delimit the arguments to
the function, and the commas separate the arguments. In an expression

Got that.
other than a function call, parentheses just form a parenthesized
expression, and a comma is a comma operator. You've used both forms
in a single statement. The compiler didn't complain because you
managed to construct a legal function call, but one that isn't what
you intended.

I just don't understand *why* it is legal, if it has no meaning?
Sorry if I'm being redundant, but this is an important point.

No, that's okay... Actually I would like very much to be corrected and
that is why I post much of my code here... I looked up sprintf in my
book again and now I can see that the parameters are like the ones you
told was correct.


Best regards / Med venlig hilsen
Martin Jørgensen
 
K

Keith Thompson

Martin Jørgensen said:
Got that...


Why is it ignored then? That's what I didn't understand...

Because that's what the comma operator does. It evaluates its left
operand ("%s" in this case) and discards the result, then it evaluates
its right operand and yields its result. (There's a sequence point
between the evaluation of the left and right operands.)

The comma operator can be useful when the left operand has a side
effect, but it's perfectly legal to use it whether there's a side
effect or not. Conceivably a compiler might warn you if the left
operand is useless, but it's not required to.
Got that.


I just don't understand *why* it is legal, if it has no meaning?

It's legal, and it does have a meaning -- it's just not the meaning
you intended. Once again:

sprintf(*dest_array, ("%s", source_array));

This calls sprintf() with two arguments. The argument first is
*dest_array. The second argument is a parenthesized expression
containing a comma operator, with a well-defined meaning. This was a
logical error, not a syntax or semantic error, so it's not up to the
compiler to second-guess you; it did exactly what you told it to do.
 
B

Barry Schwarz

-snip, thanks for the comments in the beginning -


Thanks a lot. Although it is probably "overkill" I changed it to:

printf("sizeof(char) = %lu byte(s)\n", (unsigned long)
sizeof(char) );

This is a popular choice. Unless you have 4GB strings, it should be
adequate.
So I should change the line to:

printf("char_ptr = %15p\n", (void*) char_ptr); //???
Yup.


Or how to deal with this?


I guess that takes up even less source code than to declare a
local variable and copy the value into that? Okay, it's just 2
ekstra lines.... You would have preferred the temporary variable
solution?

Yes. Unless the compiler is smart enough to optimize away the second
call, it is more efficient. Whether it is noticeably so depends on
how often you call add_text and how big source_array is.
}

void repeat(char **dest_array, const char ch, const int n)
{
int i; /* local variable: counter */

for(i=0; i<n; i++) (*dest_array) = ch;


memset is probably a little more efficient.


Strange..... That command doesn't seem to be in my about 1000
pages thick "C primer plus" book....I have memcpy and memmove
functions although, by looking in the index.

Is that a C99 thing?


memset is on page 250 of K&R II and prototyped in string.h. Much
older than C99.

Please say the book is not by Schildt.


Remove del for email
 
R

Richard Heathfield

Barry Schwarz said:
memset is on page 250 of K&R II and prototyped in string.h. Much
older than C99.

Please say the book is not by Schildt.

The book is not by Schildt.

But it might as well be, IIRC.
 
C

code break

/printf("\n");

// printf("********************** INPUT AND RESULTS:
*************************\n");

// printf("* X_A * X_B * Y_A * Y_B *** r(test) * r(degrees) *\n");

//
printf("***********************************************************************\n");

// printf("* %7.2lf * %7.2lf * %7.2lf * %7.2lf *** %7.2lf * %7.2lf
*\n",
X_A, X_B, Y_A, Y_B, r_test, r_degrees);

//
printf("***********************************************************************\n");

*/

#include <stdio.h>

#include <math.h>

#include <ctype.h>

#include <stdio.h>

#include <stdlib.h>

#include <string.h> // for memcpy

void repeat(char *dest_array, char ch, int n, unsigned int *position);

void insert(char *dest_array, char *source_str, unsigned int
*position);

//////////////

main()

{

char *text;

char *str[80]; /* static mem. allocated, max 1 line = 80 chars */

FILE *outputfile;

unsigned long size = 1600*sizeof(char); /* space for 20 lines */

unsigned int *position = 0; // u cann't dereference the NULL pointer &
junk pointer.
//first make position varaible to
point to some variable...

text = malloc(size);

if (!text)

{

fprintf(stderr, "%s: line %d, malloc(%lu) failed.\n", __FILE__,
__LINE__,
size);

exit(EXIT_FAILURE);

}

memcpy(text, "\n", (size_t) 2 ); /* start by moving to the next line */

insert(text, str, position); /* insert string into text-array (this
array is
the one to be written to both screen+file) */

repeat(text, '*', 20, position); /* make 20 stars: ******************
etc...
*/

memcpy(text, " INPUT ", (size_t) 7 ); /* move to text-array */

insert(text, str, position);

if ( (outputfile=fopen("result.txt", "w") )==NULL)

{

printf("\nError file: \"result.txt\".\n\n");

}

else

{

/* write data from text both to screen *and* to file - still unsure
about
how to do this part too */

}

fclose(outputfile);

system("PAUSE");

return 0;

}

/* functions */

void repeat(char *dest_array, char ch, int n, unsigned int *position)

{

int i;

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

memcpy(dest_array[position+i], ch, (size_t) 1);

*position += n;

}

void insert(char *dest_array, char *source_str, unsigned int *position)

{

int i, n;

n = strlen(source_str);

while(i=0; i<n; i++)

*(dest_array[position+i]) = *source_str; //Hi position varaible is
pointer to NULL u cann't dereference it.

*position += n;//error in dereference

}
 
?

=?ISO-8859-1?Q?Martin_J=F8rgensen?=

Barry said:
On Thu, 16 Mar 2006 15:49:32 +0100, "Martin Joergensen"



Yup.

Thanks... Nice to know that %p needs a void*. It's easy to understand
that a pointer to int points to somewhere in memory that is size(int) =
4 bytes large, for instance. Also easy with pointer to double, - it
points to something that is size(double) = 8 bytes, at least on my computer.

A void always points to something that is 1 byte? I think I read
somebody write that sometimes char could be 2 bytes long, on some systems?

Or how to deal with this?



I guess that takes up even less source code than to declare a
local variable and copy the value into that? Okay, it's just 2
ekstra lines.... You would have preferred the temporary variable
solution?


Yes. Unless the compiler is smart enough to optimize away the second
call, it is more efficient. Whether it is noticeably so depends on
how often you call add_text and how big source_array is.
Ok...
}

void repeat(char **dest_array, const char ch, const int n)
{
int i; /* local variable: counter */

for(i=0; i<n; i++) (*dest_array) = ch;

memset is probably a little more efficient.


Strange..... That command doesn't seem to be in my about 1000
pages thick "C primer plus" book....I have memcpy and memmove
functions although, by looking in the index.

Is that a C99 thing?



memset is on page 250 of K&R II and prototyped in string.h. Much
older than C99.


I read a lot of people referring to K&R...
Please say the book is not by Schildt.

The book I have? My book is by Stephen Prata.


Best regards / Med venlig hilsen
Martin Jørgensen
 
?

=?ISO-8859-1?Q?Martin_J=F8rgensen?=

Keith said:
There's a review at
<http://accu.org/index.php/book_reviews?url=view.xqy?review=c003203>.
The review is of the 4th edition; I don't know which edition the OP
has.

I have the 5. edition... But it probably didn't change much really since
the 4.ed. The front cover is exactly the same. I agree with this
reviewer, that I like the index and the examples... But I have nothing
to compare the book with. Having spend some time in this group, I also
agree that it's really stupid that in this book they don't write
anything about accidentally overwriting memory using scanf/gets...


Best regards / Med venlig hilsen
Martin Jørgensen
 
P

Pedro Graca

Martin said:
A void always points to something that is 1 byte?

<pedantic>
A "void" does not exist.
What exists is "void*"
</pedantic>

I guess not.
When you malloc() memory the return type is void*.
What size do you think
malloc(100)
creates?

Anyway, you can't dereference a void*, so it's irrelevant what size it
points to.

======== foo.c ========
#include <stdio.h>
#include <stdlib.h>

int main(void) {
void * p;
p = malloc(100);
if (p) {
printf("sizeof *p == %lu\n", (unsigned long)sizeof *p);
free(p);
}
printf("sizeof(void) == %lu\n", (unsigned long)sizeof(void));
return 0;
}

$ gcc -W -Wall -Werror -std=c89 -pedantic -O2 foo.c -o foo
x.c: In function `main':
x.c:8: warning: invalid application of `sizeof' to a void type
x.c:11: warning: invalid application of `sizeof' to a void type

Because I have "-Werror" for my incantation of gcc no executable was
created.

I think I read
somebody write that sometimes char could be 2 bytes long, on some systems?

Either you didn't read it accurately or the writer didn't write it
accurately.

By definition sizeof(char) is 1 on *every* standard C implementation.
 
V

Vladimir S. Oka

Martin Jørgensen wrote:


Either you didn't read it accurately or the writer didn't write it
accurately.

By definition sizeof(char) is 1 on *every* standard C implementation.

I guess, what "somebody" wanted to say is that the size of `char`
in /bits/ is not necessarily 8 (a common assumption: byte = 8 bits).
The Standard states that CHAR_BIT is _at_least_ 8. It is conceivable to
have more than 8 bits per `char`. There were machines that had 9, for
example.
 

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
474,176
Messages
2,570,950
Members
47,500
Latest member
ArianneJsb

Latest Threads

Top