-1

Initially, i ran this code on ubuntu and it worked just fine without any warnings whatsoever. However, when I run it on VS on windows it says _operand1 is not initialized. I'm wondering how it can go wrong.

I know about not casting results of malloc, but VS just keeps throwing warnings.

Program is supposed to take char array of 9 bytes. First byte represents arithmetic operation, and other 8 represent 2 ints 4 bytes each (4-digit numbers). Here is the code:

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

float* calculate(char *, int *, int *);

int main() {

    char buffer[9];

    gets_s(buffer);

    int a, b;

    float* rez = calculate(buffer, &a, &b);

    printf("Operand1: %d, Operand 2: %d\n Result: %f\n", a, b, *rez);

    return 0;
}

float* calculate(char *buffer, int *a, int *b) {
    char operation;
    char *_operand1;
    char *_operand2;

    int operand1, operand2;

    memcpy(_operand1, buffer + 1, sizeof(int));
    _operand2 = (buffer + 5);

    operand1 = atoi(_operand1);
    operand2 = atoi(_operand2);

    operation = buffer[0];

    float *rez = (float *)malloc(sizeof(float));

    switch (operation) {
    case '0':
        *rez = (float)(operand1 + operand2);
        break;
    case '1':
        *rez = (float)(operand1 - operand2);
        break;
    case '2':
        *rez = (float)(operand1 * operand2);
        break;
    case '3':
        *rez = (float)operand1 / operand2;
        break;
    }

    return rez;

}
too honest for this site
  • 12,050
  • 4
  • 30
  • 52
pr12015
  • 67
  • 7
  • You can also try running the program with Valgrind or with the address sanitizer on Ubuntu (compile and link with `-fsanitize=address`). This will prove that the program is also wrong on Ubuntu, not just Windows. – Dietrich Epp Mar 10 '17 at 20:07
  • If you get a warning/error about not casting `void *`, you don't use a C compiler, but most likely a C++ compiler. Thus this is C++, not C. And differentiating two variables just by a single underscore is very bad practice. Don't do it! – too honest for this site Mar 10 '17 at 20:11
  • 1
    The warning is very clear. **where** do you think `memcpy` copies the data? – too honest for this site Mar 10 '17 at 20:13

2 Answers2

3

I really don't know what you expect to happen, but this is 100% wrong.

char *_operand1; /* uninitialized */
char *_operand2; /* uninitialized */

int operand1, operand2;

/* _operand1 is still uninitialized... */
memcpy(_operand1, buffer + 1, sizeof(int));

Nothing good can happen when you call memcpy() here. The absolute best-case scenario is that your program will crash. However, it might not crash, and that is an awful thing to ponder… if it's not crashing, what is it doing? It's probably doing something you don't want it to do.

Further analysis

The code is very suspicious in general.

memcpy(_operand1, buffer + 1, sizeof(int));

Why sizeof(int)? buffer is a pointer to an array of characters, presumably, and there's no particular reason to choose sizeof(int), since the result is just passed into atoi. The atoi function just takes a pointer to an NUL-terminated array of characters, sizeof(int) is just the size of the result of atoi not the size of the input.

How to make it work

Here is how you would do this without invoking undefined behavior:

char tmp[5];

memcpy(tmp, buffer, 4); // don't use sizeof(int), just use 4
tmp[4] = '\0'; // add NUL terminator
int op0 = atoi(tmp);

memcpy(tmp, buffer + 5, 4);
// NUL terminator already present in tmp
int op1 = atoi(tmp);

Note that when you pass tmp to memcpy, it is converted to a pointer of type char * that points to the first element of tmp. Kind of like doing the following:

char tmp[5];
char *my_ptr = &tmp[0];
memcpy(my_ptr, ...);

You can see how this is different from:

char *my_ptr; // uninitialized
memcpy(my_ptr, ...);
Dietrich Epp
  • 205,541
  • 37
  • 345
  • 415
  • 1
    Isn't it supposed to copy what is in buffer? Buffer being function parameter. I mean it works on ubuntu, and it does exactly what i want, but not on windows. That's why i'm here.. – pr12015 Mar 10 '17 at 19:54
  • @sstefan: I can guarantee you that it is wrong on Ubuntu. It is 100% wrong, no matter what system you are using. – Dietrich Epp Mar 10 '17 at 19:56
  • @sstefan: Remember that functions cannot initialize their arguments. `memcpy` instead will initialize what its arguments point to, but in general it is impossible for a function to initialize its arguments. – Dietrich Epp Mar 10 '17 at 19:58
  • 1
    I can post screenshot if you like. Tried it multiple times – pr12015 Mar 10 '17 at 19:58
  • @sstefan: Please do not post a screenshot. Even if your program gives the right answer it is still a wrong program. – Dietrich Epp Mar 10 '17 at 19:59
  • @sstefan: We are not trying to confuse you here—sometimes it happens that a program that is wrong still gives you the right results. We are trying to help you write a correct program. If all you needed were the correct results, you could just get the answer yourself on a calculator. – Dietrich Epp Mar 10 '17 at 20:00
  • Thanks for your comments. The reason behind sizeof(int) is that i wanted to copy 4 bytes having in mind that sizeof(int) returns 4. I don't want it to just do the thing, i'm curious why does it even work if it is so wrong. Don't get me wrong i understand what you are saying, but im just looking for an explanation – pr12015 Mar 10 '17 at 20:10
  • @sstefan: Unfortunately there is not really a good explanation for why it happened to give you the right answer. Sometimes, when a compiler compiles incorrect code like in the program, weird things will happen, and values in one part of the program will appear in another part of the program for no discernible reason—since the program uses uninitialized values, you end up with garbage, but sometimes that "garbage" comes from somewhere else in the program which *coincidentally* results in the answer you were expecting. – Dietrich Epp Mar 10 '17 at 20:15
  • The worst part about these kind of errors is that it seems like the program is working fine… but then you change compilers, or change something else in the program, and it stops working. It can result in some really difficult debugging sessions—you make a change in file X, and suddenly file Y is not behaving correctly any more. You'd pull your hair out looking for the error. – Dietrich Epp Mar 10 '17 at 20:18
  • Now I can see how silly this was. Just for clarification, `memcpy` copies what is in buffer, but doesn't know where to copy it since `_operand1` doesn't point to anything. Did I get it right? – pr12015 Mar 10 '17 at 23:50
  • Yes, that's exactly right--except sometimes when you use uninitialized variables, you get garbage that just happens to work... so `_operand1` might have pointed somewhere by accident. – Dietrich Epp Mar 11 '17 at 00:20
  • Even weirder, if you use an uninitialized variable, it might have a different value each time you use it or do other bizarre things like that. So `int x; printf("x = %d, x = %d\n", x, x);` might print out two *different* values for `x`... C is like that. – Dietrich Epp Mar 11 '17 at 00:21
  • yeah, I know C is like that. What got me so confident I did it right is the fact that it kept giving correct results again and again, actually, never failed. – pr12015 Mar 11 '17 at 00:27
1

when I run it on VS on windows it says _operand1 is not initialized. I'm wondering how it can go wrong.

Using an uninitialized value results in undefined behavior, which means anything is allowed to happen. That's how it can go wrong.


I know about not casting results of malloc, but VS just keeps throwing warnings.

Are you by any chance compiling your code as C++? Because C++ doesn't allow implicit conversions from void*, whereas C does.

Emil Laine
  • 41,598
  • 9
  • 101
  • 157