How to update one element of an array of struct? - Page 1
Forum section: electronics / general

author: Jester
author_rank: Frequent Contributor
timestamp: November 14, 2022, 02:01:46 pm
content: Help please.If I have The following array of struct, declared at the top of the file.......Code:struct cfgSettings {char name;Uint16 value;Uint16 color;};Code:struct cfgSettings cfgSet = {{"Setting #1:", 1, RED},{"Setting #2:", 12, WHITE},{"Setting #3:", 200, BLUE},};later in a function, I can manipulate one of the values as follows:Code:cfgSet.value--;However if try to replace an entire element as follows:Code:cfgSet = {"Setting # 99 :", 777, GREY};I receive a compile error "expected an expression"What is the correct syntax to replace the entire element (preferably in one line as shown?

author: mfro
author_rank: Regular Contributor
timestamp: November 14, 2022, 02:18:03 pm
content: Quote from: Jester on November 14, 2022, 02:01:46 pmWhat you can do (although rarely seen, but perfectly valid and closest to what you already tried) isCode:cfgSet = (struct cfgSettings) {"Setting # 99 :", 777, GREY};[edit: fixed typo]

author: Jester
author_rank: Frequent Contributor
timestamp: November 14, 2022, 02:22:33 pm
content: mfro,Thanks works, elegant!

author: DavidAlfa
author_rank: Super Contributor
timestamp: November 14, 2022, 02:25:34 pm
content: Doing name ="Anna", Anna is a constant and won't change.If name is always written like that you can use a pointer instead:Code:struct cfgSettings {char * name;Uint16 value;Uint16 color;};name = "Anna";You'll save space and remove any size limitation, names could be as long as you want.However this doesn't really copy the data, so if you use a working buffer to generate some name and later use for anything else, the name will also change, in that case use strcpy or make your own version:Code:strcpy(char *dst, char *src){do {*dst++ = *src;} while (*src++);}strcpy (name, "Anna");That's the usual method. mfro approach is interesting

author: Siwastaja
author_rank: Super Contributor
timestamp: November 14, 2022, 02:32:05 pm
content: C is a surprisingly high-level language. Here mfro shows a high-level approach to the problem, which is more writable, readable, and maintainable than "classic" solutions using strcpy, memcpy, or self-implemented copy loops. Yes, assignment operator works in C, even for complex types like structs, yet many have picked up habit of not doing it.The only thing assignment operator doesnotwork with, are bare arrays; but if an array is within a struct, then you can assign structs, and arrays are copied, too, like in higher-level languages. One can also pass arrays by values, and return them by values to/from functions, when wrapped in structs.The only ugly thing that reminds of simplicity of C is, with assignment operator, right-hand side has absolutely no idea what's going on left-hand side. This is important when considering casting of input operands when doing arithmetic, but it's also relevant here: the compiler can't look at the type from the left-hand side, so given {}-style initializer, you need to tell compiler what type it is, how else would it know how to construct the object? This is why the cast shown by mfro is needed.

author: JPortici
author_rank: Super Contributor
timestamp: November 14, 2022, 03:14:03 pm
content: Quote from: mfro on November 14, 2022, 02:18:03 pmwouldn't have thought in a million years it would work

author: Siwastaja
author_rank: Super Contributor
timestamp: November 14, 2022, 03:32:44 pm
content: Quote from: JPortici on November 14, 2022, 03:14:03 pmThis! People assume C is much more crappy than it actually is. I remember writing somewhere in 2015 a macro like this:file.htypedef struct{...} my_type_t#define COPY_MY_STRUCT(a, b) memcpy(&a, &b, sizeof (my_type_t))or something utterly stupid like this, but I did it because we can see code like this all the freaking time, yet assigning structs has been normal since first C standards I think? At least in C89! All this tedious and error-prone bullcrap is not needed, just assign structs like any variables. It's worth understanding, though, structs can be large, so large memory copy might get inefficient. If this is of concern, consider naming scheme that reminds you. Not using typedef, to force you to typestructevery time, is one way, as preferred by Linus Torvalds.It goes without saying that if there are pointers inside structs, their values (the addresses) are copied as is; obviously struct copy does not magically allocate more memory and copy content pointed to by the pointers. C is simple, after all.

author: DavidAlfa
author_rank: Super Contributor
timestamp: November 14, 2022, 03:54:24 pm
content: A doubt appeared. Would the rest of the elements remain untouched when doing this? (value, color)Code:cfgSet = (struct cfgSettings) { "Hello there" };Also I never use "struct something_t{}" and later "struct something_t something".I find typedefs much cleaner and easier to understand:Code:typedef struct {char name;uint16_t value;uint16_t color;}cfgSettings_t;cfgSettings_t cfgSet = {{"Setting #1:", 1, RED},{"Setting #2:", 12, WHITE},{"Setting #3:", 200, BLUE},};cfgSet = (cfgSettings_t) {"Setting # 99 :", 777, GREY};

author: newbrain
author_rank: Super Contributor
timestamp: November 14, 2022, 04:15:52 pm
content: Quote from: Siwastaja on November 14, 2022, 03:32:44 pmNo, this has become possible only with C99 (and it's one of my favourite C99 features - so much that I found aclang bug...).To be precise, that(type)is not a cast at all, but part of a compound literal ("6.5.2.5 Compound literals" in C11 standard).The "abstract machine" will create an unnamed object of type 'type' and assign it to the left hand operand of the = .In practice, no extra object will be created if the compound literal has automatic duration (as in this case).Quote from: DavidAlfa on November 14, 2022, 03:54:24 pmOf course not. The compound literal object is initialized as any other structure with an initializer list - so the other members will be zeroed.But you can do:Code:cfgSet = (struct cfgSettings) { "Hello there", cfgSet.value, cfgSet.color };

author: Siwastaja
author_rank: Super Contributor
timestamp: November 14, 2022, 04:39:24 pm
content: Quote from: newbrain on November 14, 2022, 04:15:52 pmYeah, (type){initializer} is a C99 feature, but what you quoted me on, I was talking about struct assignment, and isn't that older? At least gcc --std=c89 -Wall --pedantic lets a s1 = s2 assignment pass. People seem to avoid even that for whatever reason!

author: mfro
author_rank: Regular Contributor
timestamp: November 14, 2022, 05:15:28 pm
content: Quote from: Siwastaja on November 14, 2022, 04:39:24 pmIt is. The SYSV4 C compilers could already pass structures by value and also understood structure assignment. But because of the lack of standardization, you couldn't rely on these features available, so they weren't widely used until "ANSI C" (aka C89).

author: JPortici
author_rank: Super Contributor
timestamp: November 14, 2022, 05:56:27 pm
content: Quote from: newbrain on November 14, 2022, 04:15:52 pmmakes sense, all my favourite compilers got updated to C99 just very recently... (and i'm not using that version yet as it has showstopper bugs)QuoteI bet it is

author: NorthGuy
author_rank: Super Contributor
timestamp: November 14, 2022, 06:05:14 pm
content: Quote from: Siwastaja on November 14, 2022, 04:39:24 pmCompound literals are not about assigning structures, but about having literals of compound types (i.e. array or structures). You can use compound literals for arrays too, even though you cannot assign to arrays. For example:Code:#include <stdio.h>int* x;int main() {x = (int[]){1,2,3};printf("%d %d %d\r\n",x,x,x);}Here, the compound literal yields a pointer to a pre-initialized array.

author: SiliconWizard
author_rank: Super Contributor
timestamp: November 14, 2022, 07:42:25 pm
content: Yet another cool example of RTFM.Compound literals have been introduced in C99 as newbrain mentioned. Yes, about 23 years ago.Don't hesitate to use designated initializers as well. Makes code more readable and avoids breaking stuff if you ever change the order of struct members, insert new members between existing ones, etc.

author: Siwastaja
author_rank: Super Contributor
timestamp: November 14, 2022, 07:46:46 pm
content: C99 brought in a lots of very useful stuff which makes C a much less crappy language while keeping all the power. Yet, C99 didn't introduce useless or hard to understand concepts, or too much redundant stuff, so modern C isn't overly complicated like modern C++ is. C99 is also old enough to be very widely supported.Unless you are targeting some very weird architectures with no up-to-date compilers, use C99 to the fullest to make your code easier to write, read and maintain.

author: SiliconWizard
author_rank: Super Contributor
timestamp: November 14, 2022, 07:55:10 pm
content: C11 also has a few new cool tricks up its sleeve, although maybe not as "useful" as what was introduced in C99.If you're using anything GCC- or LLVM-based (which probably makes up 99% of the current tools for embedded dev), full support is no problem.It's interesting to see how "conservative" in general people using C have been (to the point of ignoring anything more recent than 30+ years old), while they have no problem embarking on brand-new languages that are moving targets and barely stable. Go figure. I guess one reason is that C is mostly seen as "boring", and thus, while still widely used, doesn't get much traction when it comes to learning it. It's not seen as "exciting".

author: newbrain
author_rank: Super Contributor
timestamp: November 14, 2022, 08:22:17 pm
content: Quote from: Siwastaja on November 14, 2022, 04:39:24 pmAbsolutely! Sorry for misinterpreting!I don't have the C89 standard, but from K&R 2nd edition (talking about structures):QuoteQuote from: SiliconWizard on November 14, 2022, 07:42:25 pmAnd array member initializers, too (they are also designators):Code:typedef struct {char name;uint16_t value;uint16_t color;} cfgSettings_t;/* Here, e.g., I only want to initialize the 2nd array member, the others will be zeroed */cfgSettings_t cfgSet = { = { .value = {12}, .name = "Setting #2:", .color = WHITE}, /* Order is not important, with designators! */};And yes, initializers for scalars can be enclosed in braces (but you might get a warning).Quote from: SiliconWizard on November 14, 2022, 07:55:10 pmWhile we are on the topic of compound objects, let's mention C11 anonymous structures and unions!One of the reasons I use C11* (but for my stuff will move to C23 ASAP!).* Among many: _Atomic, _Alignas, generics, static assertions...

author: SiliconWizard
author_rank: Super Contributor
timestamp: November 14, 2022, 08:30:58 pm
content: Quote from: newbrain on November 14, 2022, 08:22:17 pmThose have been supported as an extension in GCC for a long time, so many people using GCC were already using that. But good thing it ended up in the standard.Still haven't seen a lot of really good stuff in C23 though. Literal separators? Yeah.

author: newbrain
author_rank: Super Contributor
timestamp: November 14, 2022, 08:50:53 pm
content: Quote from: SiliconWizard on November 14, 2022, 08:30:58 pmYes, that's one of the reasons. Only way to make binary literals (supported by gcc since ages) reasonably useful.Other things I like: _BitInt(N) (bit precise integers), true/false/bool are now keywords, attributes, unnamed function parameters.Nothing like C89 to C99, or even C99 to C11 (C17 is just C11.1), but still welcome.

author: SiliconWizard
author_rank: Super Contributor
timestamp: November 14, 2022, 08:55:29 pm
content: Quote from: newbrain on November 14, 2022, 08:50:53 pmI guess _BitInt(N) allow arbitrary N as opposed to stdint's which only have predefined sizes (which not all are available on a given implementation)? If so, sounds cool. Would allow more readable code, and probably more efficient than handling it manually with masks all over the place.What are unnamed function parameters useful for?

author: mfro
author_rank: Regular Contributor
timestamp: November 14, 2022, 09:30:22 pm
content: Quote from: DavidAlfa on November 14, 2022, 03:54:24 pmWhile this is surely a matter of personal taste (and maybe even depends on how you "grew up" with C), for me "cleaner" and "easier to read and understand" means coding as expressive as possible (C is certainly cryptic enough by itself). The struct keyword always reminds you that you are dealing with an aggregate which can be helpful. Typedef doesn't add any value in readability here (IMHO).I've even seen things likeCode:typedef struct something something_struct;in code reviews, which is complete nonsense to me.

author: newbrain
author_rank: Super Contributor
timestamp: November 14, 2022, 09:31:18 pm
content: Quote from: SiliconWizard on November 14, 2022, 08:55:29 pmYes, I hate doing it manually, and for some reason I've always been allergic to bit fields.They are useful to make it explicit that a parameter is not going to be used (as it is inaccessible inside the function).I like when I can express intent clearly without added syntactical clutter.Mostly, I see the use for functions that must conform to a specific signature (e.g. to be used as callbacks, or in general, stored in a function pointer).Nothing to write home about, but enough to list it here.Another small thing: void f(); is now equivalent to void f(void); (and K&R function declarations are out).BTW: that was the only previous case where one had an unnamed parameter (of type void).

author: newbrain
author_rank: Super Contributor
timestamp: November 14, 2022, 09:34:01 pm
content: Quote from: mfro on November 14, 2022, 09:30:22 pmLinus,is that you?

author: mfro
author_rank: Regular Contributor
timestamp: November 14, 2022, 09:37:14 pm
content: Quote from: newbrain on November 14, 2022, 09:34:01 pmHaha. No. Just 30 years of C programming experience

author: JPortici
author_rank: Super Contributor
timestamp: November 15, 2022, 06:43:51 am
content: Quote from: mfro on November 14, 2022, 09:30:22 pmI prefer to do that when pointers start to get involved. Also when pointer to pointer (to pointer to pointer or other monstruosity i see from time to time). Helps me keeping everything clean and readable, helped me get rid of bugs over the year due to improper parenthesis or maybe even subtle compiler bugs. Typedef costs nothing, adds readability, your code style can add a prefix to the type to indicate it is a structure so everyone is happy (besides linus of course)My opinion of course.

author: mfro
author_rank: Regular Contributor
timestamp: November 15, 2022, 10:45:37 am
content: that specific example was seen literally: a typedef that just shuffles wording. Exactly the same number of characters to type, zero added information, basically just a space replaced with an underscore.Can't see how that could add to readability?