Passing and returning arrays to and from functions

P

Pete

Can someone please help, I'm trying to pass an array to a function, do some
operation on that array, then return it for further use. The errors I am
getting for the following code are, differences in levels of indirection, so
I feel it must have something to do with the way I am representing the array
in the call and the return.

Below I have commented the problem parts.

Thanks in advance for any help offered.
Pete

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

char P10(char key[]); /* prototype */

void main()
{

char key[20];
int i;

printf("\nEnter a 10 bit Binary Sting: ");
gets(key); /* Copy representation of 10 bit binary string
entered from keyboard to key[]. "not to use for binary operations
just representation */

/* below I'm getting an error 'char[]' differs in levels of indirection to
'char'
and '=' left operand must be l-value */

key = P10(key); /* function call. copy returned array into key[] */
printf("Key is now : \n");
for(i=0; i<10; ++i) { /* loop to print returned array key[] */
printf("%c\n", key);
}
}


char P10(char Ctext[]) { /* Trivial function to permute input string */

int Perm10[10]={3,5,2,7,4,10,1,9,8,6};
int i;
int index;

/* Trace statement checking key[] has been passed */
for(i=0;i<10;++i) {
printf("Ctext = %c\n", Ctext);
}
/* Trivial Permutation function */
for(i=0; i<10; i++) {
index = Perm10;
printf("index = %d\n", index);
Ctext = Perm10[index-1];


}
/* Below shows the error 'return' : 'char' differs in
levels of indirection from 'char *' */

return Ctext; /* return Ctext[]/key[] to calling function */
}
 
M

Malcolm

Pete said:
#include <stdio.h>
#include <string.h>

char P10(char key[]); /* prototype */
This is right but it suggests misunderstanding.
void main()
This should be int main() to be ANSI-compliant.
{

char key[20];
int i;

printf("\nEnter a 10 bit Binary Sting: ");
gets(key); /* Copy representation of 10 bit binary string
entered from keyboard to key[]. "not to use for binary operations
just representation */
gets() is OK for a test program, but not for real code, because you will
corrupt memory if someone type in more than 19 characters (19 + NUL)
/* below I'm getting an error 'char[]' differs in levels of indirection to
'char'
and '=' left operand must be l-value */

key = P10(key); /* function call. copy returned array into key[] */
This is where the misunderstanding creeps in.
printf("Key is now : \n");
for(i=0; i<10; ++i) { /* loop to print returned array key[] */
printf("%c\n", key);
}
}


char P10(char Ctext[]) { /* Trivial function to permute input string */

int Perm10[10]={3,5,2,7,4,10,1,9,8,6};
int i;
int index;

/* Trace statement checking key[] has been passed */
for(i=0;i<10;++i) {
printf("Ctext = %c\n", Ctext);
}
/* Trivial Permutation function */
for(i=0; i<10; i++) {
index = Perm10;
printf("index = %d\n", index);
Ctext = Perm10[index-1];


}
/* Below shows the error 'return' : 'char' differs in
levels of indirection from 'char *' */

return Ctext; /* return Ctext[]/key[] to calling function */
}


The first point is that most C primers introduce multi-dimensional arrays at
about the same time as one-dimensional arrays. This is IMO a mistake. In C,
one-dimensional arrays are basic, but multi-dimensional arrays are advanced
features.

What does this have to do with you, since you only use a 1 dimensional
array?

The answer is that the primer obscures the truth that C does not pass or
return arrays to functions. It passes the address of the first element of
the array.

Your function should therefore be

char P10(char *Ctext)

However virtually any programmer would want some security about the length
of the array (and not just rely on the caller reading the comments and
guessing from the name that P10 takes 10 characters)

So the prototype becomes

/*
P2 - permute an array
Ctext - text to permute
N - length of text ( must be 10 in current version)
*/
char P10(char *Ctext, int N)

Now since we are passing a pointer to the array, the array will be permuted
in place. There is thus no need to return anything. So the function becomes

void P2(char *Ctext, int N)

However let's say you do not want to modify the array passed. What do we do
then? The answer is

void P2(const char *Ctext, int N, char *output)

If you are going to allow Ctext and output to be the same, you will have to
code carefully.
 
J

Jack Klein

Can someone please help, I'm trying to pass an array to a function, do some
operation on that array, then return it for further use. The errors I am
getting for the following code are, differences in levels of indirection, so
I feel it must have something to do with the way I am representing the array
in the call and the return.

That's because you can't pass or return bare arrays to or from
functions.
Below I have commented the problem parts.

Thanks in advance for any help offered.
Pete

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

char P10(char key[]); /* prototype */

This prototype is exactly equivalent to:

char P10(char *key);

....because the name of an array is always converted to a pointer to
its first element when used as an argument to a function. C allows
the notation that you used as a synonym to the one that I used. I
really wished that had never been allowed, because it only adds to the
array/pointer confusion that many newbies have.

And even if you could return an array from a function, which you
can't, you have prototyped a function with a return type of a single
char only.

Actually, you want to change the return type of this function to void,
see my comments in the function itself.
void main()

This is a problem part that you neglected to comment. The only
standard, portable return type for main() in a hosted C environment is
'int'. No matter what the illiterate help files for some compilers
say.

int main()

....or even better:

int main(void)
{

char key[20];
int i;

printf("\nEnter a 10 bit Binary Sting: ");
gets(key); /* Copy representation of 10 bit binary string

Here's another problem that you didn't comment. Any, any, ANY use of
the function gets() is a defect. It is the only function in the
standard C library that can NEVER be used safely, and most especially
NOT TO BE used for interactive user input.

What do you think will happen is a user types 40 or 50 characters
before pressing enter, and gets() tries to put them all in your 20
character string? Bad things will happen.
entered from keyboard to key[]. "not to use for binary operations
just representation */

/* below I'm getting an error 'char[]' differs in levels of indirection to
'char'
and '=' left operand must be l-value */

key = P10(key); /* function call. copy returned array into key[] */

Right, you can't assign to an array like 'key', or any other array for
that matter.

Fortunately, you don't need to. When you pass 'key' to P10, that
function will actually receive a pointer to key[0]. Any change it
makes to key[0] and other characters via the pointer it receives
change the contents of the original array. So you don't need to
return it, it just changes as the function runs.

So make this:

P10(key);
printf("Key is now : \n");
for(i=0; i<10; ++i) { /* loop to print returned array key[] */
printf("%c\n", key);
}


....and since you are going to fix your source to properly define
main() as returning an int, add this here:

return 0;
}


char P10(char Ctext[]) { /* Trivial function to permute input string */

Change to:

void P10(char Ctext[])

....or even better to help avoid confusion in the future:

void P10(char *Ctext)
int Perm10[10]={3,5,2,7,4,10,1,9,8,6};
int i;
int index;

/* Trace statement checking key[] has been passed */
for(i=0;i<10;++i) {
printf("Ctext = %c\n", Ctext);
}
/* Trivial Permutation function */
for(i=0; i<10; i++) {
index = Perm10;
printf("index = %d\n", index);
Ctext = Perm10[index-1];


}
/* Below shows the error 'return' : 'char' differs in
levels of indirection from 'char *' */


The body of this function above here actually does what you want it to
do, because even though P10 receives Ctext as a pointer to char, you
can use subscripting on a pointer.
return Ctext; /* return Ctext[]/key[] to calling function */

Since this is now a function returning 'void', just omit this line.

Run, do not walk, to the FAQ for this group, link in my signature, and
read all of the entire section on pointers and arrays.

Then get a good C book, such as "The C Programming Language, Second
Edition" by Kernighan & Ritchie.
 
M

Michael Mair

Hello,
However let's say you do not want to modify the array passed. What do we do
then? The answer is

void P2(const char *Ctext, int N, char *output)

size_t N is not only nicer but even makes most sense when using char
arrays... :)
If you are going to allow Ctext and output to be the same, you will have to
code carefully.

Spell it out: The actual "permutation function" provided by the OP
is crap. I reordered the posting and comment on it now:

I enter with Ctext[]="1111110000";
>>char P10(char Ctext[]) { /* Trivial function to permute input string */
>>
>>int Perm10[10]={3,5,2,7,4,10,1,9,8,6};
>>int i;
>>int index;
>>
>>/* Trace statement checking key[] has been passed */
>>for(i=0;i<10;++i) {
>>printf("Ctext = %c\n", Ctext);
>>}
>>/* Trivial Permutation function */
>> for(i=0; i<10; i++) {
>> index = Perm10; i=0: index=3;
>> printf("index = %d\n", index);
>> Ctext = Perm10[index-1];

Ctext[0]=Perm10[3-1]=2;
There was never a two in the original Ctext.
>>
>>
>> }
>>/* Below shows the error 'return' : 'char' differs in
>> levels of indirection from 'char *' */
>>
>> return Ctext; /* return Ctext[]/key[] to calling function */
>> }

I am not sure what this is to mean. I think it should have been
something along the lines
Ctext=Ctext[index-1];
However, this is crap, too: Imagine the very simple permutation
{3,1,2} working on {'0','0','1'}. This gives us the outcome:
Ctext[0]=Ctext[3-1]; /*='1';*/
Ctext[1]=Ctext[1-1]; /*='1';*/
Ctext[2]=Ctext[2-1]; /*='1';*/
Either you break up your permutation in a series of permutations
of two values and perform an exchange using an intermediate
variable:
/* Perm: {{3,2},{1,2}} */
temp=Ctext[3-1]; Ctext[3-1]=Ctext[2-1]; /*='0';*/
Ctext[2-1]=temp; /*='1';*/
temp=Ctext[1-1]; Ctext[1-1]=Ctext[2-1]; /*='1';*/
Ctext[2-1]=temp; /*='0';*/
or you use a temporary array to which you write the
outcome of your permutations. After that, you copy your
temporary values into the original array:
tempArr=Ctext[index-1];
in your original loop effects tempArr holding the right permutation
of Ctext. Before returning, do
for (i=0; i<10; i++)
Ctext = tempArr;

Try to wrap your mind arround the ideas of zero-based arrays and
zero-based permutations to get rid of the "-1".


Cheers
Michael
 
J

John Bode

Pete said:
Can someone please help, I'm trying to pass an array to a function, do some
operation on that array, then return it for further use. The errors I am
getting for the following code are, differences in levels of indirection, so
I feel it must have something to do with the way I am representing the array
in the call and the return.

Below I have commented the problem parts.

Thanks in advance for any help offered.
Pete

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

char P10(char key[]); /* prototype */

As Jack explained, "char key[]" in this context is synonymous with
"char *key"; in both cases, what you're actually passing to the
function is a pointer to the first element in the array, not a copy of
the array itself. Among other things, this means that you can't
automatically determine how many elements are in the array. When
passing an array argument, it's always good practice to pass the array
size (number of elements) as a separate argument, e.g.

char P10 (char *key, size_t keycount);
void main()

On a compliant, hosted implementation, this should be

int main (void)
{

char key[20];
int i;

printf("\nEnter a 10 bit Binary Sting: ");
gets(key); /* Copy representation of 10 bit binary string
entered from keyboard to key[]. "not to use for binary operations
just representation */

Never ever ever *EVER* use gets(). It *will* introduce a point of
failure in your program. Use fgets() instead:

fgets (key, sizeof key, stdin);

fgets() behaves slightly differently from gets() -- for example, if
you're entering data from the keyboard and hit Return, fgets() will
attempt to store that newline character to the target buffer if
there's room. The lines below will strip out the newline character if
it's present:

if (strchr (key, '\n'))
*(strchr (key, '\n')) = 0;

If the newline character isn't present, then you know that the user
typed a string longer than what you're sized to store.
/* below I'm getting an error 'char[]' differs in levels of indirection to
'char'
and '=' left operand must be l-value */

key = P10(key); /* function call. copy returned array into key[] */

First problem is that you have your function typed to return char
(single character) and you're attempting to assign that result to an
array of char (key). Second problem is that you cannot assign values
to an array identifier directly.

Since you are writing your results to the array parameter, there's no
reason to return the array value (functions cannot return array values
directly anyway).

Change the function so that it returns "void" (i.e., no value), and
pass the array size as a parameter, like so:

P10 (key, sizeof key);

Also remember to change the prototype above.
printf("Key is now : \n");
for(i=0; i<10; ++i) { /* loop to print returned array key[] */
printf("%c\n", key);
}
}


char P10(char Ctext[]) { /* Trivial function to permute input string */


void P10 (char *Ctext, size_t CtextSize)
int Perm10[10]={3,5,2,7,4,10,1,9,8,6};
int i;
int index;

/* Trace statement checking key[] has been passed */
for(i=0;i<10;++i) {

for (i = 0; i < 10 && i < CtextSize; i++) {
printf("Ctext = %c\n", Ctext);
}
/* Trivial Permutation function */
for(i=0; i<10; i++) {
index = Perm10;
printf("index = %d\n", index);
Ctext = Perm10[index-1];


}
/* Below shows the error 'return' : 'char' differs in
levels of indirection from 'char *' */

return Ctext; /* return Ctext[]/key[] to calling function */


Again, this is because you typed the function to return char, but
you're attempting to return a value that's type char *. Type the
function to return void and remove the return statement completely.
 
M

Malcolm

John Bode said:
printf("\nEnter a 10 bit Binary Sting: ");
gets(key); /* Copy representation of 10 bit binary string
entered from keyboard to key[]. "not to use for binary operations
just representation */

Never ever ever *EVER* use gets(). It *will* introduce a point of
failure in your program. Use fgets() instead:

fgets (key, sizeof key, stdin);

fgets() behaves slightly differently from gets() -- for example, if
you're entering data from the keyboard and hit Return, fgets() will
attempt to store that newline character to the target buffer if
there's room. The lines below will strip out the newline character if
it's present:

if (strchr (key, '\n'))
*(strchr (key, '\n')) = 0;
No, no, no.
These lines are an error.
Think about it, what will happen on overflow?

Replacing undefined behaviour with wrong behaviour is not an improvement.
If the newline character isn't present, then you know that the user
typed a string longer than what you're sized to store.
So to use fgets() you need to check for the newline, and if it is not
present print out an error message such as "line too long", and then gobble
the rest of the input.
 
J

John Bode

Malcolm said:
John Bode said:
printf("\nEnter a 10 bit Binary Sting: ");
gets(key); /* Copy representation of 10 bit binary string
entered from keyboard to key[]. "not to use for binary operations
just representation */

Never ever ever *EVER* use gets(). It *will* introduce a point of
failure in your program. Use fgets() instead:

fgets (key, sizeof key, stdin);

fgets() behaves slightly differently from gets() -- for example, if
you're entering data from the keyboard and hit Return, fgets() will
attempt to store that newline character to the target buffer if
there's room. The lines below will strip out the newline character if
it's present:

if (strchr (key, '\n'))
*(strchr (key, '\n')) = 0;
No, no, no.
These lines are an error.
Think about it, what will happen on overflow?

What overflow?
Replacing undefined behaviour with wrong behaviour is not an improvement.
So to use fgets() you need to check for the newline, and if it is not
present print out an error message such as "line too long", and then gobble
the rest of the input.

Or, alternately, store what's been read so far to a larger (preferably
extendable) buffer and repeat the operation until a newline (or EOF)
is seen, then work on the target buffer.
 
M

Malcolm

John Bode said:
What overflow?
If the line the user enters is longer than sizeof key, what will happen?
What fatal mistake are you making in these three lines?
 
J

John Bode

Malcolm said:
If the line the user enters is longer than sizeof key, what will happen?

fgets() will read sizeof key-1 characters, write them to key along
with a terminating null character, and the remaining characters will
remain in standard input.

Remember, I'm using _f_gets() instead of gets().

If no newline character is found in key, strchr(key, '\n') returns
NULL, so the branch *(strchr (key, '\n')) = 0 isn't taken.
What fatal mistake are you making in these three lines?

Why don't you tell me?

I *have* tested this, you know. But, if you need convincing:

[root@rh170 other]# cat key.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main (void)
{
char key[10];

printf ("sockitome: ");
fflush (stdout);

fgets (key, sizeof key, stdin);
if (strchr(key, '\n'))
*(strchr(key, '\n')) = 0;

printf ("key = %s\n", key);

return 0;
}

[root@rh170 other]# gcc -o key -ansi -pedantic -Wall key.c
[root@rh170 other]# ./key
sockitome: blurga
key = blurga
[root@rh170 other]# ./key
sockitome: Supercalifragilisticexpealidocious
key = Supercali
[root@rh170 other]#

Did it yak when I typed in more characters than key was sized to hold?
No. The leftover characters in standard input need to be dealt with,
but that's a separate problem.

I freely admit that under most circumstances I *should* be checking
the result of fgets() for NULL, but for the purposes of this
demonstration I didn't think it necessary.
 
M

Malcolm

John Bode said:
I *have* tested this, you know. But, if you need convincing:

[root@rh170 other]# cat key.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main (void)
{
char key[10];

printf ("sockitome: ");
fflush (stdout);

fgets (key, sizeof key, stdin);
if (strchr(key, '\n'))
*(strchr(key, '\n')) = 0;

printf ("key = %s\n", key);

return 0;
}

Did it yak when I typed in more characters than key was sized to hold?
No. The leftover characters in standard input need to be dealt with,
but that's a separate problem.
The mistake is to think that getting rid of undefined behaviour solves all
your problems.
It gives the wrong result on overflow, and by throwing away the newline you
make it impossible to know if the user typed a whole line or not.Now you can say, "in this case we always take the first nine characters, so
in fact throwing away the newline is OK". This is why no compiler can ever
catch wrong behaviour, as opposed to undefined behaviour. In fact for an
interactive program just truncating is very unlikely to be acceptable - the
user needs to know why a portion of his entry is missing - and even for an
automated system it is hardly conceivable that you wouldn't want to know if
fed an overlong key.
 
J

John F. Bode

John Bode said:
I *have* tested this, you know. But, if you need convincing:

[root@rh170 other]# cat key.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main (void)
{
char key[10];

printf ("sockitome: ");
fflush (stdout);

fgets (key, sizeof key, stdin);
if (strchr(key, '\n'))
*(strchr(key, '\n')) = 0;

printf ("key = %s\n", key);

return 0;
}

Did it yak when I typed in more characters than key was sized to hold?
No. The leftover characters in standard input need to be dealt with,
but that's a separate problem.
The mistake is to think that getting rid of undefined behaviour solves all
your problems.
It gives the wrong result on overflow, and by throwing away the newline you
make it impossible to know if the user typed a whole line or not.

I'm sorry. In my original response I made the point that unlike
gets(), fgets() saves the newline (if present) to the target buffer,
and demonstrated one method for removing it. It was not meant to be a
comprehensive tutorial on how to use fgets() to handle all situations.
I believe I said something about dealing with characters remaining in
standard input, and how that was a separate issue.

You took three lines out of context and started yammering about
overflow and fatal errors, which I took to mean a core dump. Excuse
me for not getting your point right away; next time, say what you
mean.

But if you want something a little more comprehensive (if untested):

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

#define START_SIZE 80

char *nextInputLine (FILE *stream)
{
char *targetBuf = NULL;
char *r = NULL;
size_t bufsize = 0;
char readBuf[START_SIZE+1];
int gotNewline = 0;

targetBuf = malloc (START_SIZE+1);
if (!targetBuf)
return NULL;

*targetBuf = 0;
bufsize = START_SIZE;

while ((r = fgets(readBuf, sizeof readBuf, stream)) != NULL &&
!gotNewline)
{
if (bufsize - strlen (targetBuf) < strlen (readBuf))
{
char *tmp = realloc (targetBuf, bufsize + strlen (readBuf)
+ 1);
if (tmp)
{
targetBuf = tmp;
bufsize += strlen (targetBuf) + 1;
}
else
{
printf ("Fatal error: unable to extend target
buffer\n");
free (targetBuf);
return NULL;
}
}

if (strchr (readBuf, '\n'))
{
*(strchr (readBuf, '\n')) = 0;
gotNewline = 1;
}
strcat (targetBuf, readBuf);
}

if (ferror (stream))
{
printf ("Error occured on input stream; line may be
incomplete\n");
}
return targetBuf;
}

This *should* handle input lines of arbitrary length (as I said, it's
untested). Happy?
 

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

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top