3

I have a 1D array A[3] & a 2D array B[4][3]. I want to assign the array A[3] to one of the rows of array B[4][3]. How to do it correctly?

#include<stdio.h>

void main()
{
   int A[3]={1,2,3};
   int B[4][3]={0};
   int row_select=2;

   B[row_select][] = A;
}   

But this assignment doesn't work. I don't want to assign element by element using a for loop. I want to do it in one statement.

Mad Physicist
  • 107,652
  • 25
  • 181
  • 264
KharoBangdo
  • 321
  • 5
  • 14
  • 1
    `memcpy` would be a good option. – iBug Feb 11 '19 at 05:27
  • 1
    You can't assign arrays directly. Since you don't want to assign element by element, you'll have to use assignment 'byte by byte' with `memmove()` or `memcpy()`. The functions probably won't do byte copying, but that's how they're specified. (You can assign arrays indirectly if they're part of a structure; that wouldn't help you in this case, though.) – Jonathan Leffler Feb 11 '19 at 05:31
  • @JonathanLeffler. You could define a throwaway structure containing an array of the right size and nothing else, do some pointer casting, and probably get away with it. – Mad Physicist Feb 11 '19 at 05:37
  • @MadPhysicist: Hmmm; not sure how you're going to get the last row of `B` into an appropriate structure, or union. Can you clarify? – Jonathan Leffler Feb 11 '19 at 05:39
  • @JonathanLeffler unless I'm missing your original point, it won't work: https://ideone.com/pH1hS2, https://ideone.com/IQ6ttg – Mad Physicist Feb 11 '19 at 05:47
  • 1
    @MadPhysicist — You could use `*b = *a;` in your examples to assign the structures without getting the assignment errors, but I think you're running into strict aliassing problems instead. It's certainly devious, and not the sort of thing to show to someone asking this question. – Jonathan Leffler Feb 11 '19 at 05:51
  • @MadPhysicist "Probably get away with it" line of thinking and C language don't really go well together... – hyde Feb 11 '19 at 06:09
  • @JonathanLeffler. I've posted an answer despite your excellent advice :) – Mad Physicist Feb 11 '19 at 06:15
  • @hyde. While I generally agree with you while coding, I've posted a valid counterexample, for a sufficiently loose definition of "valid" of course :) – Mad Physicist Feb 11 '19 at 06:29

2 Answers2

4

memcpy could be a good option, although it's very likely it uses a loop internally.

memcpy(B[row_select], A, sizeof(A));
iBug
  • 35,554
  • 7
  • 89
  • 134
1

Don't Do This: Use memcpy

There is a way to do the assignment with a single statement, as long as you are willing to do some preliminary setup to render your code illegible. Your can use the fact that structures can (1) be assigned to each other in one step, and (2) contain fixed-size arrays. The compiler will probably run memcpy under the hood anyway, but it's a fun exercise in ridiculousness:

#include<stdio.h>

#define SZ 3 // this is just for convenience

// a pointer to an anonymous structure containing our array
typedef struct {
    int x[SZ];
} *pthrowaway;

int main(void)
{
    int A[SZ]={1,2,3};
    int B[4][SZ]={0};
    int row_select=2;

    pthrowaway a = (pthrowaway)&A;
    pthrowaway b = (pthrowaway)&B[row_select];

    *b = *a; // magic

    return 0;
}

The variables a and b are unnecessary. You can actually assign the arrays in a single statement:

*(pthrowaway)&B[row_select] = *(pthrowaway)&A;

Here is an IDEOne link showing the C99 version: https://ideone.com/IQ6ttg And here is a regular C one: https://ideone.com/pH1hS2

Mad Physicist
  • 107,652
  • 25
  • 181
  • 264
  • Which is 'regular C'? C11/C18? Or C90? – Jonathan Leffler Feb 11 '19 at 06:30
  • @JonathanLeffler. Whatever IDEOne uses. So GCC 6.3, which probably doesn't conform to any of those. – Mad Physicist Feb 11 '19 at 06:31
  • IIRC, you (the IDEOne site) uses GCC 6.x, and GCC 6.x treats C11 (effectively C18) as the default (and the change was at GCC 5.x; prior to that, the default was C90 — so poor old C99 never got to be the default version of the standard for GCC). (A quick check of the GCC releases confirms my memory isn't ailing too much — https://gcc.gnu.org/gcc-5/changes.html has the change to C11 as the top item, and GCC 5.1 was released in April 2015.) – Jonathan Leffler Feb 11 '19 at 06:35
  • @JonathanLeffler. As far as I'm aware this syntax was available for a long time, possibly before 89. I'm on a mobile platform, and lazy, so IDEOne is my idea of "checking what works". – Mad Physicist Feb 11 '19 at 06:38
  • @Nate. It's probably not. But why? – Mad Physicist Feb 11 '19 at 06:40
  • The syntax is fine in C90, as well as C99, C11, C18. The semantics could be straying into problems because the memory is being accessed by different pointer types and the compiler is entitled to assume no aliassing because neither type is a variant of `char *`. Whether a compiler can actually find a way to screw it up is open to debate, but optimizing compilers take delight in doing the damnedest things because of the strict aliassing rules — so it's playing with fire. Most likely you won't get burnt; neither in all probability will the OP. But someone coming across this and applying it might. – Jonathan Leffler Feb 11 '19 at 06:44
  • 1
    Well, looking at https://stackoverflow.com/questions/98650/what-is-the-strict-aliasing-rule, and https://en.cppreference.com/w/c/language/type, I don't think `int[3]` and `pthrowaway` are compatible types. Indeed, there seems to be no way an array and a struct can ever be compatible types. – Nate Eldredge Feb 11 '19 at 06:47
  • @JonathanLeffler. I did put a disclaimer at the top for that exact reason. At the same time, is there a sequence of casts that would make this more robust? – Mad Physicist Feb 11 '19 at 06:47
  • I see the "Don't do this" disclaimer. It is probably sufficient — but people have very poor reading skills. I am not sufficiently sure of the interpretation of strict aliasing rules to know whether there are cast sequences that exempt the code from strict aliasing problems. I _think_ that there aren't such casting sequences, but I don't _know_ for sure. My thinking is that it depends on the types used to access the data, and simply casting via `char *` does not avoid the problem that the arrays are not the same a the structure containing an array. However, I might need to be educated on it. – Jonathan Leffler Feb 11 '19 at 06:51
  • @Nate. Reading through the link, they can through a union. I'll add that in tomorrow morning. – Mad Physicist Feb 11 '19 at 06:51
  • 1
    Beware the example 3 in C11 [§6.5.2.3 Structure and union members](https://port70.net/~nsz/c/c11/n1570.html#6.5.2.3p9). I'm not entirely sure how to interpret that either — be cautious. – Jonathan Leffler Feb 11 '19 at 06:58
  • @MadPhysicist: I don't see how the union will help, unless the arrays are already in a union. If you want to be sure of writing something legal, you may want to ask a new [tag:language-lawyer] question. – Nate Eldredge Feb 11 '19 at 07:21