-3

Hello ;

I have a problem with my program, which needs to calculate the @host network of an IP address. The steps of the program are :

  1. Add the mask and the ip in a "network" variable in binary (in int)
  2. Convert the "reseau" variable in decimal (in char[])
  3. Return the "network" variable in main

Anyway, I'm at step 1. And to see that everything works, I am trying to return the string "reseau" which contains the binary addition.

Problem: I can't do it unfortunately. It's giving me the following error code:

returning 'char *' from a function with return type 'char' makes integer from without a cast [-Wint-conversion] 33. return reseau;

I suspect it's because you can't return a char tab like that (from my research) but I can't figure it out.

Code :

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

char calcul(char* octet, char* masque);

void main(){
    char octetB[]="11000000101010000000000100000111";
    char masqueB[]="11111111111111111111111100000000";

    printf("octetB : %s\n", octetB);
    printf("octetB 3e valeur : %c\n", octetB[2]);

    printf("Adresse réseau : %s\n", calcul(octetB, masqueB));
}

char calcul(char* octet, char* masque){
    int i;
    char reseau[100];

    printf("\n");
    printf("octet dans calcul : %s\n", octet);
    printf("masque dans calcul : %s\n", masque);

    for(i=0; i<32; i++){
        if((octet[i]='1') && (masque[i]='1')){
            reseau[i]='1';
        } else {
            reseau[i]='0';
        }
        printf("nombre %d : %d\n", i, reseau[i]);
    }
    return reseau;
}

So I would love to have some help to know where I went wrong? In order to understand. Thanks :D

Clifford
  • 88,407
  • 13
  • 85
  • 165
Baptiste B
  • 27
  • 3
  • 2
    You can't return `reseau`, that variable is local, I'm sure there are many duplicates addressing this issue. – anastaciu Jun 18 '21 at 19:04
  • Also, to address the error, should you have a valid string that can be returned, your return type must be `char* calcul...`. – anastaciu Jun 18 '21 at 19:07
  • And, you are not providing the NUL terminator in `reseau` which is required when the array is passed to a function (`printf`) which expects a string. – Weather Vane Jun 18 '21 at 19:08
  • 1
    Please see [Function returning address of local variable error in C](https://stackoverflow.com/questions/22288871/function-returning-address-of-local-variable-error-in-c) – Weather Vane Jun 18 '21 at 19:10
  • @anastaciu I added to the code `return*reseau` (if I understood well your thinking) I have no more error code and it passes the compilation. But it doesn't show me variable :/ – Baptiste B Jun 18 '21 at 19:17
  • 1
    @Baptiste B - Of course you can return (and consequently use) (the address of) a _local_ variable; scope is no hindrance, lifetime is. Declare `static char reseau[100];`. - You made an unjustified change other than the one suggested by anastaciu. – Armali Jun 18 '21 at 19:20
  • @Armali 'static char reseau[100];' automatically thread-unsafe:( – Martin James Jun 19 '21 at 01:13
  • You may have misunderstood the assignment here. I would interpret the assignment _fragment_ as follows: You have `int` variables `mask` and `ip`, then after `network = mask & ip` you convert `network` to a string in standard IP address form `xxx.xxx.xxx.xxx`. Strictly speaking the inputs would be `uint32_t` or at least `unsigned`. That is to say step 1 is completed by `int network = mask & ip ;` - you have done a lot of unnecessary work here I think. – Clifford Jun 19 '21 at 06:38
  • ... Step 2 is ambiguous - what is to be done with that string? Is that also to be returned to `main`? – Clifford Jun 19 '21 at 06:45
  • I have outlined how I think an answer to this assignment should look in my answer (at the end after answering the question you _did_ ask), but avoided a complete solution because this question is not really about that. I suggest you start a new question if you need help with that. – Clifford Jun 19 '21 at 07:08
  • @anastaciu There are duplicates, many of which repeat the same ill-advised `malloc()` solution suggested in answers here. I was reluctant to close-vote it as a duplicate only to direct the OP to other poor advice. – Clifford Jun 19 '21 at 07:13
  • @Martin James - There's no need for getting sad about the usage a thread-unsafe construct in a program not using threading. – Armali Jun 19 '21 at 08:45
  • 1
    @Armali if the function uses no global/static data then you don't have to document it as thread-unsafe, will never need to rewrite it if re-used by the OP/others or the design changes and so will not become another strtok(). It may not be an issue here, but code with constraints should be documented as such, (especially on SO), or the constraints removed. – Martin James Jun 19 '21 at 10:26
  • @Armali That is not the point - code safe in very limited scenarios may later be reused in in scenarios where it is unsafe, or the pattern become a habit that does not apply in all cases. – Clifford Jun 19 '21 at 11:03
  • @Clifford I agree, the problem addressed in my comment is just the one that stands out as you look at the code, but the problem revealed to be more broad, because of it I ended up not voting to close, I believe your solution is the correct one, though this is aguably an opinion based issue. – anastaciu Jun 19 '21 at 11:58
  • @anastaciu It is not really a matter of opinion, there are clear technical differences between dynamic allocation, static allocation and call by reference solutions, and it is far from arbitrary which you choose, and there is a clear idiomatic solution in this case. If we dismiss it as a matter of opinion, then no answer is valid. Put it this way; if you presented the dynamic allocation solution to me in a job interview, I would not hire you. This could be your career. It is mine. – Clifford Jun 19 '21 at 16:00
  • @Clifford you think the other two options are worse and therefore should not be considered for this case, that is still an opinion, though based in technical analysis, just because two other options exist. In an interview I would define the three solutions and state my preference. Is it impossible that an interviewer in the company across the street would prefer static storage duration? – anastaciu Jun 19 '21 at 21:01
  • @anastaciu Of course that is possible it is possible. Their bug rates and code quality by objective metrics and ultimately the survival of thier business will reflect that no doubt. – Clifford Jun 19 '21 at 22:16

4 Answers4

3

The solution using malloc() has the disadvantage that memory is allocated but the responsibility for freeing that memory is not clear. The caller need not even know the memory is dynamically allocated (why should it?), and fail to free it resulting in a memory leak. Normally the responsibility for freeing an allocation should be clear. It is not a good solution in this case.

A simple (but still imperfect) solution is to statically allocate the string being returned:

const char* calcul( const char* octet, const char* masque )
{
    static char reseau[33] = "" ;

    ...

    return reseau;
}

This has a problem however of not being reentrant (an issue in multi-threading or recursive code) and caller still needs to understand restrictions and issues related to use of a static buffer. The pointer returned is the same for all callers, such that the string will be modified if the function is called subsequently. A caller may copy the string, or ensure that it has completed all processing on it before calling the function again. It is probably better that dynamic memory allocation, but still requires the caller to have knowledge of the internal memory management to uses it appropriately.

Another disadvantage of the solution is returning a const no write access can be performed outside the function, so if that is necessary the caller would have to copy the string to a local buffer. Returning a non-const would be a really bad idea - you would be modifying the "hidden" buffer inside the function.

The more usual and more flexible idiom is for the caller to "own" the buffer and pass it to the function for it to be filled. That way the memory may be an array, or dynamically allocated according to the needs of the caller. In this case you would also pass the length of the array, so that the function can avoid overrunning the caller's buffer:

const char* calcul( const char* octet, const char* masque, 
                    char* reseau, size_t reseau_len )
{
    ...

    for( i = 0; 
         octet[i] != 0 && masque[i] != 0 && i < reseau_len; 
         i++)
    {
        ...
    }

    return reseau;
}

So then the function might be called thus:

    char reseau_buf[sizeof(octetB)] ;
    printf( "Adresse réseau : %s\n", 
            calcul( octetB, masqueB, 
                    reseau_buf, sizeof(reseau_buf) ) ) ;

Note that this last version also makes no assumptions about the length of either masque or octet - the loop stops at the shorter of all the inputs. If they differ that is a bug at the caller, the function implementation will fail "gracefully" and deterministically and thus be easier to debug.


Aside on interpretation of the assignment

None of the above seems to correctly address the assignment, which you appear to have misunderstood. I would expect a solution to look something like:

#include <stdio.h>
#include <stdint.h>

uint32_t subnet( uint32_t ip, uint32_t mask, char* subnet_str ) ;

int main()
{
    uint32_t ip = 0xC0A80001 ;      // 192.168.0.1
    uint32_t netmask = 0xFFFFFF00 ; // 255.255.255.0

    char ipstring[] = "000.000.000.000" ;
    printf( "0x%08X : ", subnet(ip, netmask, ipstring) ) ;
    printf( "%s\n", ipstring ) ;
}

uint32_t subnet( uint32_t ip, uint32_t mask, char* subnet_str )
{
    // Step 1 : apply mask to get subnet
    uint32_t network = ...

    // Step to: convert network to "xxx.xxx.xxx.xxx" string form
    // in subnet_str
    ...

    // Step 3
    return network ;
}

With output:

0xC0A80000 : 192.168.1.0

Clearly help with any of that warrants a new question. But is is far simpler that the work you have already put in. Both step 1 and 2 can be implemented each as a single line of code.

Clifford
  • 88,407
  • 13
  • 85
  • 165
  • This is the best choice for those functions where the caller can be expected to know the size of the returned type. – Martin James Jun 19 '21 at 01:19
  • @MartinJames : Which is the case here. We are only talking about this question here. What is appropriate in other situations may of course differ, but a function hiding `malloc()` and leaving `free()` responsibility to a higher level is _very seldom_ a good design and very seldom necessary. – Clifford Jun 19 '21 at 06:01
  • 'a function hiding malloc()' oh yes, for sure that is bad. A function returning a dynamic should be commented and a destructor function supplied and documented/commented if required. – Martin James Jun 19 '21 at 10:29
1

The problem is that your function calcul() returns a character array or a pointer to a character (char*) although your function definition says the return type is char. This is why your getting the compile error.

But furthermore you can't return the array reseau. This variable is local and only available within your function. Outside of the function calcul() this character array isn't valid anymore.

And the next problem in your code is that reseau doesn't have a nul termination at the end. So your printf() will not work as you expected.

And finally I found another problem in your code. The statement octet[i]='1' is a assignment. In your case you would need a comparison. You will have to change it to octet[i]=='1' (with double equal signs).

Benjamin J.
  • 1,239
  • 1
  • 15
  • 28
  • 1
    Ditto with `masque[i] = '1'`. – Weather Vane Jun 18 '21 at 19:24
  • 1
    That is a comprehensive round-up of the _problems_ in the code in question. You don't offer a solution to the main issue of returning a string however. – Clifford Jun 18 '21 at 23:59
  • @Clifford: I only wanted to give some hints where the problems in the code are located. So the person who wrotes this question have the chance to find an own solution instead of just giving them a complete code snippet. – Benjamin J. Jun 19 '21 at 06:19
  • I understand. However SO is a Q&A and a resource for others seeking answers to similar issues. In this case there are at least three possible solutions, only one of which is appropriate, and here the OP has been led to the most inappropriate of them. Not by this answer in particular, but in the spirit of helping a novice improve, an answer with an explanation/rationale - rather than just a "copy this code" answer might be appropriate. – Clifford Jun 19 '21 at 06:25
1

UP :

I have taken into account your comments and explanations. And now that it works (with the malloc system).

Thanks for your comments.

Here is the updated code for those who will come across the topic :

char* calcul(char* octet, char* masque){
    int i;
    char reseau[100];

    printf("\n");
    printf("octet dans calcul : %s\n", octet);
    printf("masque dans calcul : %s\n", masque);

    for(i=0; i<32; i++){
        if((octet[i]=='1') && (masque[i]=='1')){
            reseau[i]='1';
        } else {
            reseau[i]='0';
        }
        printf("nombre %d : %d\n", i, reseau[i]);
    }
    char* result = malloc(strlen(reseau)+1);
    strcpy(result, reseau);
    return result;
}
Baptiste B
  • 27
  • 3
  • 1
    It is a recipe for a memory leak - do not to that. – Clifford Jun 18 '21 at 23:23
  • The `reseau` array, `strlen()` and `strcpy()` are unnecessary - the length is fixed (32+1), you could allocate that much space and write to it directly. Even if that were not true, the value if `i` is already equal to the string length, there is no need to call `strlen()` at least. – Clifford Jun 19 '21 at 05:46
0

Your code doesn't compile because you return char at the calcul() function and returning a character array. If you want to return reseau character array you can do it using pointer. See the difference with the code below with your code to see how to implement it.

You also have some logical problem in your code, like there are no endline in reseau array, which result in printing garbage values after your desire string. and you use one equal = insteed of double equal == to compare equal in your code.

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

const char* calcul(char* octet, char* masque);

int main(){
    char octetB[]="11000000101010000000000100000111";
    char masqueB[]="11111111111111111111111100000000";

    printf("octetB : %s\n", octetB);
    printf("octetB 3e valeur : %c\n", octetB[2]);

    printf("Adresse réseau : %s\n", calcul(octetB, masqueB));
}

const char* calcul(char* octet, char* masque){
    int i;
    char *reseau = malloc(100);
    printf("\n");
    printf("octet dans calcul : %s\n", octet);
    printf("masque dans calcul : %s\n", masque);

    for(i=0; i<32; i++){
        if((octet[i]=='1') && (masque[i]=='1')){//use double equal to compare
            reseau[i]='1';
        } else {
            reseau[i]='0';
        }
        printf("nombre %d : %c\n", i, reseau[i]);
    }
    reseau[i]= '\0';//add endline
    return reseau;
}
Aragorn
  • 39
  • 3
  • 1
    I would suggest that "hidden" dynamic memory allocation is an inappropriate and dangerous solution in this case. – Clifford Jun 19 '21 at 00:01