I don't understand this part of the code below. I mean alloc_MY_CAR() returns some array and how does & work so that newTab->pFunFree = &free_MY_CAR sees this array which newTab->pDat returns?
I don't understand pointers well. I only know that & store address of variable and * is a pointer or a value of the variable.
Could anyone guide me on how to use it properly and how does it work? I'm a beginner, so don't be so hard on me.
Thanks in advance!
#pragma once
struct MY_CAR {
char *model;
int year;
};
void print_MY_CAR(void* pdata);
void free_MY_CAR(void *pdata);
MY_CAR* alloc_MY_CAR();
switch (typ) {
case 0:
newTab->pDat = alloc_MY_CAR();
newTab->pFunFree = &free_MY_CAR;
newTab->pFunPrint = &print_MY_CAR;
break;
}
MY_CAR* alloc_MY_CAR() {
MY_CAR* tab = (MY_CAR*)calloc(1, sizeof(MY_CAR));
if (!tab) {
exit(0);
}
else {
char model[125];
printf("Podaj model: ");
scanf("%s", model);
tab->model = (char*)calloc(strlen(model) + 1, sizeof(char));
strcpy(tab->model, model);
printf("Podaj rok: ");
scanf_s("%d", &tab->year);
}
return tab;
}
void free_MY_CAR(void *pdata) {
MY_CAR* car = (MY_CAR*)pdata;
if (!car || !car->model) return ;
free(car->model);
free(car);
}
Notice that the function free_MY_CAR has an argument of type void*,
a pointer to a "void" type
(which is a C idiom for a pointer to something without telling the type of the thing pointed to),
and the first thing it does is to reinterpret that pointer as a pointer to a MY_CAR.
So the function is probably intended to be called like this:
newTab->pFunFree(newTab->pDat);
That is, the way the functions "know" what pointer was returned by
alloc_MY_CAR() and stored in newTab->pDat
is that the programmer explicitly tells the functions what pointer
is stored in newTab->pDat.
The advantage of doing such things is that it allows some code to do some operation on a data structure without necessarily having to know what kind of data structure it will actually operate on when the program actually runs.
In the call to pFunFree above, newTab could have been initialized by the case 0 code shown in the question, but there could be another case
that initializes it with alloc_MY_JOB(), &free_MY_JOB, and &print_MY_JOB,
where the MY_JOB functions allocate/free/print a data structure that is quite different from the data structure used by
alloc_MY_CAR(), &free_MY_CAR, and &print_MY_CAR.
Then if you call
newTab->pFunPrint(newTab->pDat);
we might not be able to predict when we write the code whether it will print the contents of a data structure created by
alloc_MY_CAR() or by alloc_MY_JOB();
but we can predict that it will print the detailed information it has
about your car, or your job, or whatever was read from the data file and stored in newTab.
The property that we can make a function call that uses a data structure in a way appropriate to that data structure, without having to know when we write the code what the type of data structure will be, is called
polymorphism.
This is a cumbersome idiom and there are lots of ways to get it wrong.
One of the selling points of C++ is to enable people to write polymorphic objects more easily than this.
Related
I have just started learning C Programming and while trying to do a couple of example exercises, I found myself confused as to when is the best scenario to specify the function type.
I apologize in advance if my title is misleading.
One such example is getting the dollars (number of notes) where I am suppose to print out the results.
While I have it working, I am using void but while looking online, it seems that int function should be used instead?
I have 2 questions:
Initially I have my printf statement under void, in general coding, should such print statements be placed there or should it be in main()?
As mentioned above, using this case example of mine, is it a right/ wrong to use void?
My code is as follows:
#include <stdio.h>
void dollars_calculation(int *input_amt, int dollars_value)
{
int change_result = 0;
change_result = (*input_amt / dollars_value);
printf("Number of %3d-dollars:\t%d pcs\n", dollars_value, change_result);
if (change_result)
{
*input_amt = (*input_amt - (dollars_value * change_result));
}
}
int main()
{
int input_amt = 0;
int counter = 0;
int dollars_arr[3] = {100, 50, 10};
printf("Input amount: ");
scanf("%d", &input_amt);
for (counter=0; counter<3; counter++)
{
// Check for the number of denomiations required.
dollars_calculation(&input_amt, dollars_arr[counter]);
}
}
The "function type" as you call it is actually the data type that you might want the function to return.
For example your dollars_calculation function could return an int, which is the result of your calculation, rather than using the pointer value to set an external variable.
Alternatively, you may be want a value returned that can be used to indicate success or failure of a function. This need not be a Boolean value, but could be an integer or even an enumerated type.
In general you want to write functions so that they are fully re-entrant - meaning that they can be called multiple times and their function will remain the same regardless of program state. A function that has a void return type and takes no parameters is usually an indication of a non-re-entrant function.
The use and location of printf statements depends on your program structure and what you intend your program to do. There's no right or wrong place to use them, but in some environments you may wish to locate them in one module (code file) for convenience e.g. the output is formatted or directed in a particular way.
Pretty simple situation.
I have a response pointer to struct and i want to populate its values.
In one palce it's working:
janus_audiobridge_sync_endpoint_response *response = g_malloc0(sizeof(janus_audiobridge_sync_endpoint_response));
response->error_code = JANUS_AUDIOBRIDGE_ERROR_UNKNOWN_ERROR;
g_snprintf(response->error_cause, 512, "%s - %s", "Failed to find about page with locale - ", locale_text);
return response;
However when i do basicly the same thing another method, the response->error_cause turns out to be null:
janus_audiobridge_sync_endpoint_response *response = g_malloc0(sizeof(janus_audiobridge_sync_endpoint_response));
response->error_code = 0;
response->error_code = JANUS_AUDIOBRIDGE_ERROR_UNAUTHORIZED;
g_snprintf(response->error_cause, 512, "You need to pass a valid user_secret, before you continue.");
goto plugin_response;
My question: Why does it work in one context and not another? What is the best Practive in C, to do this kind of stuff?
Thanks!
EDIT: And even weirder is that when i do:
response->error_cause = "You need to pass a valid user_secret, before you continue.";
It works on the second example, why is that?
EDIT:
As requested:
typedef struct janus_audiobridge_sync_endpoint_response {
gint error_code;
gchar *error_cause;
json_t *message;
} janus_audiobridge_sync_endpoint_response;
It's clear from the declaration that error_cause is just a pointer, not an array.
So it won't point at anything valid when you allocate (and clear) an instance of janus_audiobridge_sync_endpoint_response. Thus you get undefined behavior.
To fix this, you need to allocate room for the string. In glib-land, you can use the nice g_strdup_printf() function for this:
response->error_cause = g_strdup_printf("%s - %s", "Failed to find about page with locale - ", "foo", locale_text);
Note that I added a foo to that call, your original code seems to fail to provide the proper number of arguments considering the format string, which (again!) gives you undefined behavior.
Doing e.g. error_cause = "hello"; is always kind of safe, since that just sets the pointer in the struct to point at a static array somewhere in memory, it doesn't copy any characters. The only risk with that is that since the pointer in the struct is not const, somebody might try to modify the string which again would bring undefined behavior.
For example, I have this block:
int nFirst, nSecond;
char sInput[10];
printf("Which variable to change to 10?");
scanf("%s", &sInput);
// BAD - inflexible and unmaintainable
if(strcmp(sInput,"nFirst") ==0){
nFirst = 10;
}
else if (strcmp(sInput,"nSecond")==0) {
nSecond =10;
}
Is there a nice way to do this? like treat a string as if its a variable name?
No, there is no "nice" way of doing this in C. Variable names (typically) aren't preserved in the generated machine code, except to support debugging. C doesn't have a built-in mechanism for translating a string value into a reference to a variable of the same name.
You would have to map variable names to variables manually. You could build a lookup table, associating a string value with the address of the corresponding variable:
struct vn {
char *varname;
void *addr;
Typeinfo t;
};
where Typeinfo is some enumeration or other mechanism for encoding the type of the variable, giving you something to the effect of
int foo;
double bar;
char *blurga;
struct vn varsByName[] = { {"foo", &foo, IntType},
{"bar", &bar, DoubleType},
{"blurga", blurga, CharPtrType} };
I don't recommend doing this.
Another, platform-dependent approach is to put all your variables into a shared library and then have access to them by names. Have a look at dlsym/dlopen functions.
void* handle = dlopen("mysymbols.so", RTLD_LOCAL | RTLD_LAZY);
int* var = (int*) dlsym(handle, user_typed_name);
*var = 10; /* modify the variable */
You could implement something like a dictionary or a two-dimensional array which contains the "variable name" and the value. Then this comes down to setting an array element to a new value.
Other than that: C# and other object oriented languages would allow this through reflection, but as C itself isn't object oriented, you can not do that (C++ support for this seems to be very limited).
You can do it with a macro:
#define MAYBESET(name) if (strcmp(sInput, #name) ==0 ){ name = 10; }
#name is the real value of name changed to a string literal.
For a small number of variables then your algorithm should perform well. If there are many variables that could be changed, rather than just two, then another algorithm should be considered. Making this pretty and clear isn't exactly easy in C.
If you really wanted this to be faster you could either do a hash table or use a switch/case like:
int First, Second; // Note that I got rid of your leading n
char sInput[10];
printf("Which variable to change to 10?");
scanf("%s", &sInput);
// BAD - inflexible and unmaintainable
// referring to character array overflow potential, I assume
switch (sInput[0])
{
case 'F':
if (0 == strcmp("irst", sInput+1) )
{
First = 10;
} else
{
// error
}
break;
case 'S':
if (0 == strcmp("econd", sInput+1) )
{
Second = 10;
} else
{
// error
}
break;
default:
// error
break;
}
If you don't like the way that this looks then you could use macros (#define) to make it less big looking, but it would turn out the same. Another option that you could employ would be to write a small program that output the source code of this program which would handle all of the repetitive and tedious parts.
Another way to do this, if all of the variables are of the same type, would be to create an array of them and input their index in rather than a name, but then you have to add code to check against inputting an index out of range of the size of the array.
I'm about to debug someone else's code and I stumbled across a certain 'way' of handling with global arrays which I consider deeply bad, but the one who first used it swears to it.
I need to find arguments against it.
Here is the code written simplified (this is not the original code, just an abstracted version)
So my question: which arguments would you bring against (or maybe some code which brings down this method) this?
int test(int i, int v, int type, int** t)
{
static int *teeest;
int result = 0;
switch(type)
{
case (1):
{
int testarr[i];
teeest = testarr;
}
break;
case (2):
result = teeest[i];
break;
case (3):
teeest[i] = v;
break;
}
if (t != NULL)
{
*t = teeest;
}
return result;
}
int main()
{
int *te = (int*)1;
test(5, 0, 1, &te);
printf("%p\n", te);
int i=0;
for(;i<5;i++)
{
test(i, i, 3, NULL);
printf("Value: %d\n", test(i,0,2, NULL));
}
return 0;
}
local variables are dead after the block they declared in, so this code is undefined behavior. Like every accessing random address, it may work, but it also may not work.
Note that if you use malloc instead of int testarr[i], (and worry to free the previous array, and to initialize teeest), it will be correct. the problems of this code have nothing about static pointers.
This is really bad. Just because the pointer is static doesn't mean the data it points to will be around. For example, testarr disappears when the function exits and the returned pointer, if used, might cause dragons to appear.
It seems to me the big downfall of this style is that you are hiding the fact that you are accessing a locally declared array which is on the stack. Then you persist a pointer to your stack which will persist through calls, which will have different stacks each call.
Another thing I was thinking about is that you have hidden from the developer what the data structure is. Indexing an array is a normal operation. Indexing a pointer makes the developer acknowledge it is an array and not a more complex data type. This also adds confusion to bounds checking.
Another thing is, that all disadvantages of global variables apply directly. The code is not reentrant, and hard to make thread-safe (if that's a concern).
I'm examining the source code (written in C) of bkhive -- a utility for dumping the SysKey bootkey from a Windows NT/2K/XP system hive -- and would like to know if there is any rationale for something the original author did: passing a pointer to a struct into a function and then returning the same pointer. Here is the relevant source code:
In main() there is a call that looks like this:
struct hive h;
char *root_key;
// Do some stuff to init
_RegGetRootKey(&h, &root_key)
Which calls:
int _RegGetRootKey(struct hive *h, char **root_key)
{
nk_hdr *n;
n = (nk_hdr*) malloc(sizeof(nk_hdr));
/* ************************************************
* RELEVANT FUNCTION CALL
* Why pass n as a parameter and use return value?
* ************************************************/
n = read_nk(n, h, 0x1020);
if (n->id == NK_ID && n->type == NK_ROOT)
{
*root_key = (char *) malloc(n->name_len + 1);
strncpy(*root_key, n->key_name, n->name_len);
(*root_key)[n->name_len] = 0;
free(n);
return 0;
}
free(n);
return -1;
}
Which calls:
nk_hdr* read_nk(nk_hdr *nk, struct hive *h, int offset)
{
memcpy(nk, h->base + offset + 4, sizeof(nk_hdr));
nk->key_name = (h->base + offset + 4 + 76);
return nk;
}
So, what is the purpose of passing the struct pointer and then returning it? Couldn't the function return nothing and use n after the function call?
The main benefit of this convention is to allow for simpler concatenation of function calls. If you want to use the return of one function as a parameter to another function, having it return the pointer can enable you to write the statement in a single line. Thus, instead of writing something like this:
foo(myPtr);
bar(myPtr);
You can do this:
bar(foo(myPtr));
The specific code you show doesn't use this, but this is a convention used in many C functions, and I guess the author of the code is used to this by now.
In this exact case, there doesn't appear to be much use for it. But in general, there are two reasons I've done the same thing in the past:
One is where the function might reallocate the item pointed at, in which case the return could be the same pointer that was passed, or could be a replacement for it with a different value.
Another, even when the pointer won't change, is that it allows the return from the function to be used immediately to access the pointed-to item, in constructs like somefunc(item)->member = 5; among others. It lets you drop the function call into another expression that needs the same pointer afterward.
It could also be just to make the function's use consistent with others in the API, some of which may have a reason to do this.
This allows you to pass the object by reference, and then get a handle (i.e., pointer) to the updated value on return ... it also allows you to pass back a NULL if something goes wrong, so that you know the state of the object that you passed in by reference is not good anymore. For instance:
struct my_struct* pass_by_ref = malloc(sizeof(my_struct));
//...some more code
if (foo(pass_by_ref) == NULL)
{
free(pass_by_ref); //pass_by_ref is no longer any good ...
perror();
}