1

I have a code where I have a buffer and I am trying to copy const string into it like this:

#include <stdio.h>

typedef struct _BIGWORD {
    unsigned char Byte[53]; //size of constant string
} BIGWORD;

int main() {
    char cBuffer[64] = { 0 };

    BIGWORD *bwBufferCast = (BIGWORD *)cBuffer;
    
    *bwBufferCast = *(BIGWORD *){ "I am trying to copy this whole text inside a buffer!" };

    printf("text: %s\n", cBuffer);

    return 0;
}

This works fine but I dont know how. Does it copy the constant string byte after byte, does it copy it all at once or is it a just coincidence that is it there and I wouldn't work in real scenarios.

I know I can create char array like: char cBuffer[64] = "this is an array!". I want to put the const string inside a buffer at demand.

I tried to add some other constant string to the code to check if it would affect the result but it didn't.

16
  • What does "copy it all at once" mean? Commented Jan 22 at 19:35
  • 1
    Can't recommend Microsoft Windows-esque, Hungarian notation when writing plain old (non-Windows) C questions for Stack Overflow.
    – jarmod
    Commented Jan 22 at 19:35
  • You are creating a string literal, takin it's pointer and cast it to the pointer to BIGWORD. Then you dereference it, and assigning the resulting struct to another struct pointed by bwBufferCast. When you assign structures, their contents are copied.
    – Eugene Sh.
    Commented Jan 22 at 19:39
  • 1
    The behavior of this code is not defined by the C standard because it aliases an array of char as a structure, and no member of the structure has a type suitable for aliasing. Commented Jan 22 at 20:35
  • 1
    @gulpr You can't initialize a compound literal of type somestruct* with a char* though. That's a constraint violation - the code is invalid C.
    – Lundin
    Commented Jan 23 at 7:28

3 Answers 3

3

This works fine but I dont know how.

If it works, it does so by exercising undefined behavior. It might as easily not work, or work intermittently, or have unexpected side effects. Your compiler ought to be cluing you in to that with a warning or error such as this:

nocpy.c: In function ‘main’:
nocpy.c:12:35: warning: initialization of ‘BIGWORD *’ {aka ‘struct _BIGWORD *’} from incompatible pointer type ‘char *’ [-Wincompatible-pointer-types]
     *bwBufferCast = *(BIGWORD*) { "I am trying to copy this whole text inside a buffer!" };
                                   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
nocpy.c:12:35: note: (near initialization for ‘(anonymous)’)

If your compiler is not emitting a warning about that issue then you should turn up its warning level until it does, or else get a better compiler.

Note also that at your level of experience, you should treat all warnings as errors. That a program seems to do what you want when you run it is not a safe basis for concluding that it is correct for all supported inputs or that it will work as intended on other machines.

Does it copy the constant string byte after byte, does it copy it all at once

Generally speaking, there is no such thing as copying an object larger than the machine's word size "all at once". However, it may be that your hardware features an efficient, hardware-assisted memory-to-memory copy, so that at the machine level, a copy can be performed via a couple of setup instructions and one more to actually execute the copy.

or is it a just coincidence that is it there and i wouldnt work in real scenarios

It is at significant risk of misbehaving. The program accesses an object of type char[64] as if it were an object of type struct _BIGWORD, and this produces undefined behavior, of the whole program. Don't do this.

I know i can create char array like: char cBuffer[64] = "this is an array!". I want to put the const string inside a buffer at demand

That is what strcpy() is for.

1
 *bwBufferCast = *(BIGWORD*) { "I am trying to copy this whole text inside a buffer!" };
  • You create compound literal of type pointer to structure BIGWORD and initialize this pointer with the reference to the string literal
  • then you assign two structs by dereferencing pointers to them. As structs are assigned by values it copies one the whole structure.

To avoid warnings and potential UB I would suggest this form:

    *bwBufferCast = *(BIGWORD[]) { {"I am trying to copy this whole text inside a buffer!" }};

or more simple

    *bwBufferCast = (BIGWORD) {"I am trying to copy this whole text inside a buffer!"};

Modern compilers will not create an additional object as it is only used for this assignment

The version of the program which does not invoke the UB:

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

typedef struct _BIGWORD {
    unsigned char Byte[53];//size of constant string
}BIGWORD;

int main() {
    char *cBuffer = malloc(64);
    char *cBuffer1 = malloc(64);

    BIGWORD* bwBufferCast = (BIGWORD*)cBuffer;
    BIGWORD* bwBufferCast1 = (BIGWORD*)cBuffer1;
    
    *bwBufferCast = (BIGWORD) {"I am trying to copy this whole text inside a buffer!"};
    *bwBufferCast1 = *(BIGWORD[]) {{"I am trying to copy this whole text inside a buffer!"}};

    printf("text: %s\n", cBuffer);
    printf("text: %s\n", cBuffer1);

   free(cBuffer);
   free(cBuffer1);
}
7
  • You can silence warnings that way, but it does not avoid UB. Which is arguably worse. Commented Jan 22 at 20:57
  • Is it really a compound literal (the OP variant)? Isn't it just a string literal casted into struct pointer?
    – Eugene Sh.
    Commented Jan 22 at 21:18
  • 1
    @EugeneSh. no it is not
    – gulpr
    Commented Jan 22 at 21:36
  • @JohnBollinger it is not silencing the warnings only creating a valid compound literal - it DOES NOT involve undefined behaviour* - maybe except the cast from line 11 in the original code.
    – gulpr
    Commented Jan 22 at 21:41
  • 1
    It is a compound literal, and no undefined behavior arises due to that, but the assignment to the original ` *bwBufferCast` had undefined behavior because ` bwBufferCast` pointed to an array of char so it was aliasing that array with an inappropriate type. Commented Jan 22 at 21:50
0

There seems to be a lot of confusion in comments regarding what is "undefined behavior" and what isn't, so let me go through this line by line:

  • BIGWORD* bwBufferCast = (BIGWORD*)cBuffer;
    This is valid C since all manner of wild and crazy pointer casts are allowed in C (C17 6.3.2.3 §7).

    However, this pointer conversion may in some cases invoke undefined behavior because the character array you start from may not be aligned and the struct type might have an alignment requirement. If this happens, some CPUs might instruction trap on the pointer assignment itself. On other CPUs you might get problem later when you de-reference the pointer.

  • (BIGWORD*) { "I am trying to copy this whole text inside a buffer!" };
    This is invalid C and will not compile cleanly. You create a compound literal consisting of a pointer. Initialization of that pointer (a so-called scalar) is done "as if by assignment" (C17 6.7.9 §11). Specifically, you are trying to initialize a BIGWORD* with a char*.

    We may then check the rules of valid assignment (C17 6.5.16.1) and find that for an implicit pointer conversion to be allowed during assignment, the pointers need to be compatible. They are not (C17 6.2.7), so this is a constraint violation and the compiler must issue a diagnostic.

    A program with constrain violations still resulting in a binary executable is non-conforming C and there are no guarantees by the standard for any behavior.

  • *(BIGWORD*) is fishy. Again there is the previously mentioned potential alignment issue, if the string literal was misaligned for the struct, which would be undefined behavior.

    But also fishy since you make an lvalue access of an object using a different type than the declared effective type. Normally we might call this a so-called "strict aliasing violation", What is the strict aliasing rule?. TL;DR a violation of the type system rules resulting in undefined behavior. That could result in incorrect code generation by the compiler.

    But as it happens, this very line is not a strict aliasing violation because (by luck?) you managed to fulfill one of the exceptions to that rule (C16 6.5 §7). We have a struct, an aggregate type, and there's an exception: "an aggregate or union type that includes one of the aforementioned types among its members". Where "aforementioned types" includes "a type that is the signed or unsigned type corresponding to the effective type of the object". A struct with an unsigned char[] member is the unsigned type corresponding to the effective type char[] of the object.

  • *bwBufferCast Same issues here as above. Potential misalignment, fishy but not strict aliasing violation.


Conclusion:

The program does not work fine because it does not compile. With a conforming compiler (gcc 13.3 -std=c17 -pedantic-errors) I get the expected diagnostic message:

error: initialization of 'BIGWORD *' {aka 'struct _BIGWORD *'} from incompatible pointer type 'char *' [-Wincompatible-pointer-types]

If you ran the executable anyway and it didn't trip over misalignment, then you might still get the expected results, either by (bad) luck or by a guarantee made through a compiler extension.

Also please note that "I only got a warning" still means that the program could be invalid C, without guaranteed behavior. Check out What must a C compiler do when it finds an error?

1
  • malloc sorts out the first issue
    – gulpr
    Commented Jan 23 at 8:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.