Is there a best practice for ouput function parameters of big structures that should not be changed? return pointer to struct or return whole structure?
Example:
I have a big data structure in file A that i will call from file B: (typedef struct is in header)
.h
typedef struct
{
int x1;
int x2;
int x3;
int x4;
...
} myStruct;
.c
static myStruct data = {...};
errorType myfunction1(mystruct *outData)
{
*outData = data; //copy data to output
...
}
errorType myfunction2(mystruct **outData)
{
*outData = &data; //just return pointer to structure
...
}
myfunction1 copies the whole structure, so the stack size will rise if i will call this function a lot and the processing time is rising, but the advantage is that the original data could not be changed outside of this file like with myfunction2.
Is there a best practice what of the both to use?
I would use the const keyword for the parameter in myfunction2 so that
Case 1: At compilation any changes on a struct used in the caller would definitely make an error if the argument is also a const struct ;
Case 2: And at least a warning if the argument of the caller is not a const.
Case 1:
foo.h
#ifndef FOO_H
#define FOO_H
typedef struct fooStruct
{
int a;
int b;
int c;
}t_fooStruct;
void cpy_foo(const t_fooStruct ** structToCpy);
#endif
foo.c
#include <stdio.h>
#include <stdlib.h>
#include "foo.h"
t_fooStruct myData = {0,1,2};
void cpy_foo(const t_fooStruct ** structToCpy){
*structToCpy = &myData;
}
main.c
#include <stdio.h>
#include <stdlib.h>
#include "foo.h"
int main(){
const t_fooStruct * mainData;
cpy_foo(&mainData);
printf("After cpy print %d %d %d\n", mainData->a, mainData->b, mainData->c);
mainData->a = 10;
mainData->b = 20;
mainData->c = 30;
printf("After changes print %d %d %d\n\n", mainData->a, mainData->b, mainData->c);
return 0;
}
At compilation this gives:
main.c: In function 'main':
main.c:12:15: error: assignment of member 'a' in read-only object
12 | mainData->a = 10;
| ^
main.c:13:15: error: assignment of member 'b' in read-only object
13 | mainData->b = 20;
| ^
main.c:14:15: error: assignment of member 'c' in read-only object
14 | mainData->c = 30;
|
Case 2:
And if the structure is not a const in the main :
main.c (version without the const)
int main(){
t_fooStruct * mainData;
cpy_foo(&mainData);
[...]
At compilation this outputs:
9 | cpy_foo(&mainData);
| ^~~~~~~~~
| |
| t_fooStruct ** {aka struct fooStruct **}
In file included from main.c:3:
foo.h:12:35: note: expected 'const t_fooStruct **' {aka 'const struct fooStruct **'} but argument is of type 't_fooStruct **' {aka 'struct fooStruct **'}
12 | void cpy_foo(const t_fooStruct ** structToCpy);
Related
Here is my code.
// test.c
#include <stdio.h>
#define ARRAY_SIZE 4
struct example_t {
int field0;
int field1;
int field2;
int field3;
int field4;
};
struct example_t func(int *in, int array[ARRAY_SIZE]) {
struct example_t out;
return out;
}
int main() {
int array[ARRAY_SIZE] = { 0, 0, 0, 0 };
int a = 0;
struct example_t col = func(&a, array);
return 0;
}
gcc 11.1.0 gave
$ gcc test.c -o test
test.c: In function ‘main’:
test.c:22:26: warning: ‘func’ accessing 16 bytes in a region of size 4 [-Wstringop-overflow=]
22 | struct example_t col = func(&a, array);
| ^~~~~~~~~~~~~~~
test.c:22:26: note: referencing argument 2 of type ‘int *’
test.c:14:18: note: in a call to function ‘func’
14 | struct example_t func(int *in, int array[ARRAY_SIZE]) {
| ^~~~
but g++ didn't.
I don't understand why the warning message is there, since there is no string operation in my code and I never read array in func.
If there are only 4 or fewer fields in struct example_t, GCC won't complain. Can someone please explain why is the message here and how can i fix it?
Thank you in advance.
warning: returning 'struct example_str **' from a function with incompatible return type 'struct example_str *' [-Wincompatible-pointer-types]
return &(example1_str->member2[index]);
Please help understand why the warning is being generated. When example1_struct->member[index] is called, my understanding is it should return (struct example_struct*) Please help me understand the reason for warning. Thank you in advance.
#include <stdio.h>
#include "stdbool.h"
struct example_str{
int member1;
struct example_str* member2[5];
};
struct example_str* Reftest(int index);
void function2(int id, int* SigVal);
struct example_str* example1_str;
int main()
{
_Bool test;
int Val = (int) test;
function2(10,&Val);
return 0;
}
void function2(int id, int* SigVal){
struct example_str* ptr_to_str;
ptr_to_str = Reftest(1);
ptr_to_str -> member2[1] -> member1 = id;
printf("%d",ptr_to_str -> member2[1] -> member1);
}
struct example_str* Reftest(int index){
return &(example1_str->member2[index]);
}
struct example_str
{
int member1;
struct example_str* member2[5];
};
example_str has 2 members and
member1 is int
member2 is struct example_str*[5], a.k.a. an array of 5 pointers to struct example_str, from member2[0] to member2[4]. 5 pointers.
refTest returns struct example_str*, the same type of each member2
You can align the declarations to see better
struct example_str
{
int member1;
struct example_str* member2[5];
};
struct example_str* Reftest(int index);
So it is clear that whathever member2 is, it is the same that Reftest() returns.
So if you declare, as you did
struct example_str* Reftest(int index)
{
return &(example1_str->member2[index]);
}
As & is the operator address of, you are returning the address of member2[], that is itself a pointer --- struct example_str* --- and for sure its type is struct example_str**
The compilers says that
so0729.c:35:12: warning: returning 'struct example_str **' from a function
with incompatible return type
struct example_str *' [-Wincompatible-pointer-types]
35 | return &(example1_str->member2[index]);
| ^~~~~~~~~~~~~~
And it is what it is. Just take the & off.
Consider using typedef. It is usual and can lead to a code easier to read. And avoid global things declared outside main()
Example
This is a variation of your code using the pointers to pass references back and forth to the structs
#include <stdio.h>
typedef struct st_example_str
{
int member1;
struct st_example_str* member2[5];
} Example;
Example* Reftest(Example*, unsigned);
void function2(Example*, unsigned, int* SigVal);
int main()
{
Example A = {.member1 = 10};
Example B = {.member1 = 11};
Example C = {.member1 = 12};
Example D = {.member1 = 13};
Example E = {.member1 = 14};
Example ex1;
ex1.member1 = 1;
ex1.member2[0] = &A;
ex1.member2[1] = &B;
ex1.member2[2] = &C;
ex1.member2[3] = &D;
ex1.member2[4] = &E; // not used
C.member2[0] = &ex1; // cycle
D.member2[0] = &ex1; // cycle
E.member2[0] = &ex1; // cycle
char test = 42;
int Val = (int)test;
function2(&ex1, 2, &Val); // should set member1 of C to 42...
printf("ids (member1) pointed from the array inside ex1: ");
for (int i = 0; i < 5; i += 1)
printf("%d ", ex1.member2[i]->member1);
printf("\n");
printf("member1 for ex1: %d\n", ex1.member1);
return 0;
}
void function2(Example* E, unsigned id, int* SigVal)
{
// sets member1 of struct pointed by E->member[id] to
// the value pointed to by SigVal
Example* ptr_to_str = Reftest(E, id);
ptr_to_str->member2[0]->member1 = *SigVal;
}
Example* Reftest(Example* E, unsigned index)
{
return E->member2[index];
}
Example output
ids (member1) pointed from the array inside ex1: 10 11 12 13 14
member1 for ex1: 42
And you see that 42, the value of test, gets set in ex1 using a few references
So I have had this problem where I keep getting error codes only in my main that (1) The struct has already been defined when I keep my struct in a header file and (2) I am using incompatible pointer types.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "functDefs.h"
#include "writeToFile.c"
#include "readFile.c"
#include "inputContactInfo.c"
#include "contactInfoStruct.h"
int main(void) {
int i = 0;
char *ynAns;
struct contactId *contactInfo;
contactInfo = malloc(sizeof(struct contactId));
do {
if(ynAns != NULL) {
free(ynAns);
}
ynAns = malloc(sizeof(char) * 5);
printf("\nDo you wish to enter a new contact (Yes or No)?: ");
fgets(ynAns, 5, stdin);
ynAns[(strlen(ynAns) - 1)] = '\0';
if (strcmp(ynAns, "Yes") == 0) {
printf("\n");
contactInfo = realloc(contactInfo, sizeof(struct contactId) * (i + 1));
contactInfo[i] = inputContactInfo();
i++;
}
} while(strcmp(ynAns, "No") != 0);
writeToFile(contactInfo, i);
readFile(i);
free(contactInfo);
return 0;
}
Then here are my function definitions:
void writeToFile(struct contactId *contInfo, int numContacts);
struct contactId *inputContactInfo();
void readFile(int numContacts);
And this is the struct header file:
struct contactId {
char firstName[20];
char lastName[20];
char companyName[50];
char phoneNumber[15];
char email[50];
};
I get errors like:
IOlist.c: In function ‘main’:
IOlist.c:28:40: error: incompatible types when assigning to type ‘struct contactId’ from type ‘struct contactId *’
contactInfo[i] = inputContactInfo();
^
IOlist.c:34:21: warning: passing argument 1 of ‘writeToFile’ from incompatible pointer type
writeToFile(contactInfo, i);
^
In file included from IOlist.c:5:0:
writeToFile.c:7:6: note: expected ‘struct contactId *’ but argument is of type ‘struct contactId *’
void writeToFile(struct contactId *contInfo, int numContacts) {
^
And these errors as well:
In file included from IOlist.c:5:0:
writeToFile.c:7:6: error: conflicting types for ‘writeToFile’
void writeToFile(struct contactId *contInfo, int numContacts) {
^
In file included from IOlist.c:4:0:
functDefs.h:1:6: note: previous declaration of ‘writeToFile’ was here
void writeToFile(struct contactId *contInfo, int numContacts);
^
In file included from readFile.c:4:0,
from IOlist.c:6:
contactStruct.h:1:8: error: redefinition of ‘struct contact’
struct contact {
^
In file included from writeToFile.c:4:0,
from IOlist.c:5:
contactStruct.h:1:8: note: originally defined here
struct contact {
^
Your function inputContactInfo() returns a pointer to struct. But the place where it tries to returnn pointer is a struct. You need declare struct contactId **contactInfo, allocate memory for each element, and then you can correctly assign your pointer to contactInfo[i].
I am trying to pass a structure by reference in C so that I can modify the values from within the function. This is the code I have so far, but it produces some warnings and one error.
main.c
#include <stdio.h>
#include "myfunctions.h"
#include "structures.h"
int main(int argc, char const *argv[] {
struct MyStruct data;
data.value = 6;
printf("Before change: %d\n", data.value);
changeData(data);
printf("After change: %d\n", data.value);
}
myfunctions.c
#include "structures.h"
void changeData(MyStruct data) {
data.value = 7;
}
myfunctions.h
#ifndef MyStruct
#define MyStruct
void changeData(MyStruct data);
#endif
structures.h
typedef struct {
int value;
} MyStruct;
Errors Produced
In file included from main.c:2:0:
myfunctions.h:4:1: warning: parameter names (without types) in function declaration
void changeData(MyStruct data);
^
In file included from main.c:3:0:
structures.h:5:1: warning: unnamed struct/union that defines no instances
} MyStruct;
^
main.c: In function ‘main’:
main.c:9:5: error: ‘data’ undeclared (first use in this function)
data.value = 6;
^
main.c:9:5: note: each undeclared identifier is reported only once for each function it appears in
That's all caused by
#define MyStruct
With this line, you've defined MyStruct as a macro that expands to nothing. I.e. you've effectively removed all occurrences of MyStruct in the following code, which is why the compiler is so confused about seeing things like
typedef struct {
int value;
} ;
or
void changeData( data);
To fix this, use
#ifndef MYFUNCTIONS_H_
#define MYFUNCTIONS_H_
instead. (This is the reason why we use ALL_UPPERCASE names for macros: To avoid accidental name clashes with normal identifiers.)
applying all my comments and elimination of the unnecessary 'typedef', and placing it all in one file ( Note: there is no problem with extracting the various files), results in the following code:
#ifndef STRUCTURES_H
#define STRUCTURES_H
struct MyStruct
{
int value;
};
#endif // STRUCTURES_H
#ifndef MYFUNCTIONS_H
#define MYFUNCTIONS_H
void changeData( struct MyStruct *data);
#endif // MYFUNCTIONS_H
#include <stdio.h>
//#include "myfunctions.h"
//#include "structures.h"
int main( void )
{
struct MyStruct data;
data.value = 6;
printf("Before change: %d\n", data.value);
changeData(&data);
printf("After change: %d\n", data.value);
} // end function: main
//#include "structures.h"
void changeData( struct MyStruct *data)
{
data->value = 7;
} // end function: changeData
which cleanly compiles and does do the desired operation
Im trying to have an unlimited array in C to store some data. My header file stock.h looks like this
#ifndef STOCK_H
#define STOCK_H
typedef struct {
char* id;
char* descripcion;
int precio;
} tAppliance;
typedef struct {
int cantidad;
tAppliance electrodomestico;
} tElectroStock;
typedef struct {
int size;
int capacity;
tElectroStock *electrodomesticos;
} tStock;
void tstock_init(tStock *stock);
void tstock_add(tStock *stock, tAppliance item, int cantidad);
#endif
My stock.c file
#include <stdio.h>
#include "stock.h"
void tstock_init(tStock *stock) {
stock->size = 0;
stock->capacity = 10;
stock->electrodomesticos = malloc(sizeof(tElectroStock) * stock->capacity);
}
void tstock_add(tStock *stock, tAppliance item, int cantidad) {
if(stock->size >= stock->capacity) {
stock->capacity = stock->capacity * 2;
stock->electrodomesticos = realloc(stock->electrodomesticos, sizeof(tElectroStock) * stock->capacity);
}
tElectroStock t;
t.cantidad = cantidad;
t.electrodomestico = item;
stock->size++;
stock->electrodomesticos[stock->size] = t;
}
And finally my main function
#include <stdio.h>
#include "stock.h"
int main(int argc, char **argv)
{
tStock t; // Creamos nuestra variable de stock
tstock_init(&t); // Iniciamos el stock
tAppliance item;
item.descripcion = "Television SONY";
item.id = "apeid9";
item.precio = 20;
tstock_add(&t, item, 1);
tstock_add(&t, item, 1);
}
As you can see on my main function I try to add 2 items to tStock. however adding the second item seems to crash the whole applicattion and no idea why.
Enable all warnings, and treat every single one of them as error. That way you could've found the issue with the member not being a pointer that's already been mentioned yourself.
Check the return value of library functions like malloc and realloc for errors and handle them appropriately.
Finally, you'll want to swap these two lines:
stock->size++;
stock->electrodomesticos[stock->size] = t;
Compile with -Wall you'll be warn:
test.c: In function ‘tstock_init’:
test.c:634:5: warning: implicit declaration of function ‘malloc’ [-Wimplicit-function-declaration]
stock->electrodomesticos = malloc(sizeof(tElectroStock) * stock->capacity);
^
test.c:634:32: warning: incompatible implicit declaration of built-in function ‘malloc’
stock->electrodomesticos = malloc(sizeof(tElectroStock) * stock->capacity);
^
test.c: In function ‘tstock_add’:
test.c:640:9: warning: implicit declaration of function ‘realloc’ [-Wimplicit-function-declaration]
stock->electrodomesticos = realloc(stock->electrodomesticos, sizeof(tElectroStock) * stock->capacity);
^
test.c:640:36: warning: incompatible implicit declaration of built-in function ‘realloc’
stock->electrodomesticos = realloc(stock->electrodomesticos, sizeof(tElectroStock) * stock->capacity);
Add to stock.c:
#include <stdlib.h>
You must also switch instruction as below:
stock->electrodomesticos[stock->size] = t;
stock->size++;
Otherwise your code is UB when stock->size++ == stock->capacity