incompatible return type from struct function - C

2019-05-31 11:22发布

问题:

When I attempt to run this code as it is, I receive the compiler message "error: incompatible types in return". I marked the location of the error in my code. If I take the line out, then the compiler is happy.

The problem is I want to return a value representing invalid input to the function (which in this case is calling f2(2).) I only want a struct returned with data if the function is called without using 2 as a parameter.

I feel the only two ways to go is to either:

  1. make the function return a struct pointer instead of a dead-on struct but then my caller function will look funny as I have to change y.b to y->b and the operation may be slower due to the extra step of fetching data in memory.

  2. Allocate extra memory, zero-byte fill it, and set the return value to the struct in that location in memory. (example: return x[nnn]; instead of return x[0];). This approach will use more memory and some processing to zero-byte fill it.

Ultimately, I'm looking for a solution that will be fastest and cleanest (in terms of code) in the long run. If I have to be stuck with using -> to address members of elements then I guess that's the way to go.

Does anyone have a solution that uses the least cpu power?

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

    typedef struct{
    long a;
    char b;
    }ab;

    static char dat[500];

    ab f2(int set){
      ab* x=(ab*)dat;
      if (set==2){return NULL;}
      if (set==1){
      x->a=1;
      x->b='1';
      x++;
      x->a=2;
      x->b='2';
      x=(ab*)dat;
      }
      return x[0];
    }

    int main(){
      ab y;
      y=f2(1);
      printf("%c",y.b);
      y.b='D';
      y=f2(0);
      printf("%c",y.b);
      return 0;
    }

回答1:

If you care about speed, it is implementation specific.

Notice that the Linux x86-64 ABI defines that a struct of two (exactly) scalar members (that is, integers, doubles, or pointers, -which all fit in a single machine register- but not struct etc... which are aggregate data) is returned thru two registers (without going thru the stack), and that is quite fast.

BTW

      if (set==2){return NULL;} //wrong

is obviously wrong. You could code:

   if (set==2) return (aa){0,0};

Also,

 ab* x=(ab*)dat; // suspicious

looks suspicious to me (since you return x[0]; later). You are not guaranteed that dat is suitably aligned (e.g. to 8 or 16 bytes), and on some platforms (notably x86-64) if dat is misaligned you are at least losing performance (actually, it is undefined behavior).

BTW, I would suggest to always return with instructions like return (aa){l,c}; (where l is an expression convertible to long and c is an expression convertible to char); this is probably the easiest to read, and will be optimized to load the two return registers.

Of course if you care about performance, for benchmarking purposes, you should enable optimizations (and warnings), e.g. compile with gcc -Wall -Wextra -O2 -march=native if using GCC; on my system (Linux/x86-64 with GCC 5.2) the small function

  ab give_both(long x, char y)
  { return (ab){x,y}; }

is compiled (with gcc -O2 -march=native -fverbose-asm -S) into:

         .globl  give_both
         .type   give_both, @function
 give_both:
 .LFB0:
         .file 1 "ab.c"
         .loc 1 7 0
         .cfi_startproc
 .LVL0:
         .loc 1 7 0
         xorl    %edx, %edx      # D.2139
         movq    %rdi, %rax      # x, x
         movb    %sil, %dl       # y, D.2139
         ret
         .cfi_endproc

you see that all the code is using registers, and no memory is used at all..



回答2:

I would use the return value as an error code, and the caller passes in a pointer to his struct such as:

int f2(int set, ab *outAb); // returns -1 if invalid input and 0 otherwise