What does the "&" do in my fscanf statement in C  - Page 1
Forum section: electronics / general

author: Dmeads
author_rank: Regular Contributor
timestamp: June 14, 2020, 08:15:50 am
content: Hullo!I thought the "&" operator (when dealing with pointers) just returns the address, so why do I need it to put a value from my file into an array?Thanks.-DomCode:#include <stdio.h>#include <math.h> // for rand()#include <stdlib.h> // for filesint main(){float my_array = {};float max = 2147483647; // RAND_MAXfloat read_array = {}; // to put the read values into.FILE * fp;fp = fopen("MyRandomNumbers.txt","w+"); // open/create file (w+ means write or read)for(int i = 0; i < 8; i++){my_array = rand() / max; // create normalized random numberfprintf(fp,"%f\n",my_array); // print a random number to the file}rewind(fp); // reset position to beginning of filefor(int i = 0; i < 8; i++){fscanf(fp,"%f", &read_array); // read each line of file and store in arrayprintf("line %d of the file holds the number %f\n", i, read_array); // print the values}fclose(fp); // close filereturn 0;}

author: greenpossum
author_rank: Frequent Contributor
timestamp: June 14, 2020, 08:21:34 am
content: When you index the array it's no longer an address so you need the &.

author: T3sl4co1l
author_rank: Super Contributor
timestamp: June 14, 2020, 11:41:32 am
content: Indeed, array accessfoois syntactic sugar for*(foo + bar). (Note that when C performs arithmetic on pointers, it's done in terms of elements of the defined type, in this casefloat, which is most likely 4 bytes on the physical platform. The physical address is fully(uint8_t*)foo + sizeof(foo) * bar.)In effect,&undoes*. Heh, hmm, I've never actually written&*&*&*&*&*&foobefore, but it should work.Likewise, you could writefscanf(fp,"%f", read_array + i);, which should work just as well.Tim

author: magic
author_rank: Super Contributor
timestamp: June 14, 2020, 04:33:01 pm
content: The physical address is really(char*)foo + sizeof(foo) * barbecause sizeof(foo) is the size of a pointer and unit8_t may not even be defined.Generally, read_array is of type "float pointer" while read_array is of type "float" because you wouldn't want to write *read_array to get the n-th element. Hence the need for &.

author: T3sl4co1l
author_rank: Super Contributor
timestamp: June 15, 2020, 03:19:06 am
content: Sorry, yes, sizeof element, not the array!Also, sizeof(char) is defined as 1 (should've looked this up a long time ago!). So uint8_t should? always be defined as unsigned char.With uint8_t, I am assuming #include <stdint.h>, yes.Tim

author: magic
author_rank: Super Contributor
timestamp: June 15, 2020, 06:19:36 am
content: Well, uint8_t may not be defined at all (old implementation, weird architecture, lazy implementator) while char is guaranteed to exist and mean the lowest addressable unit of storage, in which sizeofs are counted. I don't really get that recent fad to replace char* with uint8_t*, unless one wants to prevent accidental compilation on systems with 9 or 64 bit char because the code treats it as strictly 8 bit.As for whether sizeof(uint8_t) may be anything other than 1 when it exists, hopefully not. ISO mandates that char holds at least ±127 or 0-255 so there would be no obvious need for more than 1 char to store a uint8_t in a conforming implementation. But when it comes to strict lawyerism, I'm not sure if anything prohibits padding every uint8_t with a second dummy char just to break your code

author: brucehoult
author_rank: Super Contributor
timestamp: June 15, 2020, 11:12:01 am
content: Quote from: magic on June 15, 2020, 06:19:36 amIt is not defined whether plain char is signed or unsigned, so if you care about the difference (i.e. need negative values or ones greater than 127) then you either need to use int8_t or uint8_t or else create your own typedef.It's a bit pointless to create your own typedef to signify an 8 bit integer, but it makes complete sense to make a typedef for something in the application domain such as PortNum or whatever.

author: Nominal Animal
author_rank: Super Contributor
timestamp: June 15, 2020, 11:21:27 am
content: Quote from: magic on June 15, 2020, 06:19:36 amNo, it's the signedness thing. (Thatcharmay be signed on some systems and unsigned on others.)Here's an example. Did you know that to write a function that returns the number of whitespace characters in a string, sayCode:size_t whitespaces(const char *s){size_t n = 0;if (s) {while (*s) {n += isspace((unsigned char)(*s));s++;}}return n;}youhave tocast the argument ofisspace()tounsigned char, or it may not work for non-ASCII characters? (When using e.g. ISO Latin 1 or 9, ISO 8859-1 or ISO 8859-15, this includes non-breaking space.) And that this is actually stated in the C standard? The same for all theis*()functions declared in<ctype.h>.Clunky, yeah.The solution would be to useunsigned charinstead ofchareverywhere, butuint8_tis shorter.

author: magic
author_rank: Super Contributor
timestamp: June 15, 2020, 12:07:44 pm
content: I still wouldn't make this function take a uint8_t* argument. Can anyone actually guarantee that uint8_t is equivalent to unsigned char when it exists?I'm not talking about using char for maths, although you could if you specify signedness explicitly. I'm talking about the fad of treating random pointers as uint8_t* rather than char*. Begone, Satan.

author: Nominal Animal
author_rank: Super Contributor
timestamp: June 15, 2020, 02:00:15 pm
content: TL;DR:uint8_tis equivalent tounsigned charif you only use them to describe unsigned integers between 0 and 255. This is quaranteed.Quote from: magic on June 15, 2020, 12:07:44 pmMe neither, I'm quite comfy using the casts. Others don't like typing.In my own code, I tend to useconst unsigned charandunsigned charthroughout, which also avoids the problems and the need for aforementioned casts. I've gotten comments on how oddthatlooks, too.Quote from: magic on June 15, 2020, 12:07:44 pmIt is true if and only ifCHAR_BIT == 8. (It is a macro defined in<limits.h>which is available in both hosted and freestanding environments.)The underlying issue is that the C standard allowsunsigned charto represent values between 0 and 2CHAR_BIT-1, inclusive, withCHAR_BITrequired to be at least 8, but allowed to be larger.uint8_ton the other hand is an unsigned integer type that can represent values between 0 and 28-1, exactly. The arithmetic etc. rules are otherwise the same for both.An indirect consequence of the wording in the standard is thatsizeof (uint8_t) == sizeof (unsigned char) == sizeof (char) == 1is required/quaranteed for a conforming implementation. As far as I know, it has been true on even on oddball and experimental architectures.If you write code that expects an ASCII-compatible 7- or 8-bit execution character set, then havingCHAR_BIT > 8does not matter, andeffectivelyunsigned charanduint8_tare the same, since you only use the types for values 0 to 255, inclusive. Because of Unicode (and UTF-8), this is already a perfectly acceptable expectation.

author: magic
author_rank: Super Contributor
timestamp: June 15, 2020, 09:21:56 pm
content: Well, I found a few interesting counterarguments on the Interwebz.There is supposedly no guarantee that encoding is identical for the two types. Might be different bit order or perhaps even weirder bullshit, AFAIK the standard says nothing about encoding of non-stdint types.There are special aliasing rules concerning char pointers which affect optimizations and UB, nothing like that for uint8_t. I wouldn't be surprised if casting float* to uint8_t* and then trying to load the first byte as uint8_t actually results in UB, though it is explicitly legal to do so with char.Nobody mentioned it, but I'm still not convinced if an implementation which always stores uint8_t at 16 bit alignment wouldn't be legal.

author: golden_labels
author_rank: Super Contributor
timestamp: June 15, 2020, 11:47:42 pm
content: Quote from: magic on June 15, 2020, 06:19:36 amAren’t you conflatinguint8_twithuint_least8_toruint_fast8_t?uint8_t, like all exact-width types, has very strict requirements regarding size and implementation. It must be exactly that many bits, it can’t have any padding bits, it must have all the arithmetic behaviour of exactly that many bits. Signed exact-width types are even worse, as they must be 2's completent. This is why all exact types are optional: they may be simply unimplementable in a given platform. So ifCHAR_BITis anything greater than 8,uint8_tcan’t even be imlemented. Which also means thatsizeof(uint8_t)is always 1.leastNandfastN, on the other hand, may be larger thanN, may have padding bits, may have any representation and the arithmetic is not limited to the N-bit one. However,uint_least8_tandint_least8_tby definition must always be defined as (unsigned/signed) char and their size is always 1, which makes them pretty useless.Quote from: brucehoult on June 15, 2020, 11:12:01 amYou can simply usesigned charorunsigned char. No need for exact-width types here.Quote from: Nominal Animal on June 15, 2020, 11:21:27 amSince Nominal Animal han’t provided that: to get an explanation better than “standard requires” (in this case the specs are not clear on why you should do that), seeSTR37-Cin SEI CERT CCS.Quote from: Dmeads on June 14, 2020, 08:15:50 amThen simply useRAND_MAX— do not guess values, do not hardcode your guesses. This is exacly why you have this macro defined: because the value may be different.

author: magic
author_rank: Super Contributor
timestamp: June 16, 2020, 06:27:39 am
content: Quote from: golden_labels on June 15, 2020, 11:47:42 pmI think I'm not.Now, let's consider a platform with 16 bit registers and memory words and special "8 bit" instructions which truncate the input and output to accurately mimic 8 bit calculations. Say that I pack two chars per word for space efficiency but decide to store 8 bit integer types one per word to avoid shifting overhead on loads and stores at odd addresses. There are padding bits, but my implementation makes them invisible to arithmetic operations in C by the use of 8 bit truncating instruction. You can only discover those bits by reading a variable stored in memory char by char, because its sizeof is 2.Are you sure the wording about padding makes it illegal to call this contraption a uint8_t? I think it depends on how the "padding bits" are defined and to what extent the implementation needs to hide them from the user. I don't know, IANAL

author: golden_labels
author_rank: Super Contributor
timestamp: June 16, 2020, 07:21:20 am
content: Quote from: magic on June 16, 2020, 06:27:39 amNot only “let’s say”, but we would have to do it. Object pointers are expressed in terms ofsizeof(char)and can’t be “fractional”. This is the case with_Bool, which is required to only store integers in range [0, 1], but it can’t have less than 8 bits. So 8 wasted bits would be a requirement foruint8_t.Quote from: magic on June 16, 2020, 06:27:39 amThat would be even moreinteresting, in the negative sense, because it has potential to produce trap representations. But even assuming it does not, we have 8 padding bits. And those are explicitly forbidden for fixed-width types. And for a good reason: fixed-width types are expected to have very strictly defined behaviour in wide range of operations across platforms (if they are present).Padding bits of an integer are defined as any bits that are not value bits or sign bit.

author: hamster_nz
author_rank: Super Contributor
timestamp: June 16, 2020, 09:09:25 am
content: One thing that made it clear (to me at least) that char should be unsigned, even though it isn't, is the value used for EOF.Code:#include <stdio.h>int main(int argc, char *argv[]) {signed char sc = (signed char)EOF;unsigned char uc = (unsigned char)EOF;char c = (char)EOF;if(sc == EOF) {printf("Signed char can be EOF\n");} else {printf("Signed char cannot be EOF\n");}if(uc == EOF) {printf("Unsigned char can be EOF\n");} else {printf("Unsigned char cannot be EOF\n");}if(c == EOF) {printf("Char can be EOF\n");} else {printf("Char cannot be EOF\n");}return 0;}

author: magic
author_rank: Super Contributor
timestamp: June 16, 2020, 09:13:50 am
content: Quote from: golden_labels on June 16, 2020, 07:21:20 amThe matter is how it is defined whether padding bits exist or not. What kind of C statements are expected to produce what kind of results depending on presence or absence of padding bits. If it's only arithmetic, I will get away with it, but if I'm not allowed to store padding bits in memory in a manner which permits seeing them by scanning the variable char by char then my wonderful architecture is screwed.It probably is, I think you are right.But there is still the interesting issue of aliasing. I know for sure that loading a float value through a uint32_t pointer is undefined behavior, the same almost certainly applies to uint16_t and then why not to uint8_t? I suspect that the following actually is illegal but people do it all the time and implementations probably wouldn't dare to break it:Code:void *memcpy(void *dest, const void *src, size_t n) {uint8_t *d = (uint8_t*)dest;uint8_t *s = (uint8_t*)src;while (size--) *d++ = *s++;return dest;}

author: greenpossum
author_rank: Frequent Contributor
timestamp: June 16, 2020, 09:25:52 am
content: void *s don't need to be cast to any other (data) pointer, straight assignment will work.

author: andersm
author_rank: Super Contributor
timestamp: June 16, 2020, 10:27:39 am
content: Quote from: hamster_nz on June 16, 2020, 09:09:25 amEOF is deliberately defined to be of type int. It is a bug to assign a return value to a char (of any type) and only then test against EOF.

author: hamster_nz
author_rank: Super Contributor
timestamp: June 16, 2020, 12:34:35 pm
content: Quote from: andersm on June 16, 2020, 10:27:39 amNot in my Linux box's 'stdio.h':Code:/usr/include/stdio.h:# define EOF (-1)Anyhow we have what we have and learn to live with it

author: greenpossum
author_rank: Frequent Contributor
timestamp: June 16, 2020, 12:45:14 pm
content: The important fact is that the functions that return EOF like getchar() and getc() return int, not char.

author: Nominal Animal
author_rank: Super Contributor
timestamp: June 16, 2020, 01:44:38 pm
content: Quote from: golden_labels on June 15, 2020, 11:47:42 pmYeah, sorry about that; it's a bit of a chore to grab the exact references, as only the final draft PDFs (n1256 for C99,n1570 for C11, andn2176 for C18) are available on the net.In all three versions it is clearly stated in 7.4p1:QuoteC99 7.19.1p3, C11 7.21.1p3, and C18 7.21.1p3 further states thatQuoteThis means that if one intends to simply and easily use the standard C library, one must cast individual character codes tounsigned charbefore supplying to e.g. the<ctype.h>functions; and thatEOFis an integer macro and not a character at all: it indicates acondition(so like a non-error error) many standard C library functions can report.Quote from: Dmeads on June 14, 2020, 08:15:50 amUnfortunately,RAND_MAXcan be as small as 32767, and actually is on many older standard C libraries.For typical use, theXorshiftfamily of generators is superior: faster, and more random: these pass almost all BigCrush randomness tests, whereas standard C librarylinear congruential generatorsproduce quite poor sequences, and are slower to compute.For statistical work, useMersenne Twister. (It too fails two test in BigCrush, though, but if you are writing an article, you don't have to defend its use because it is the commonly used one; using Xorshift would require at least a minimal argument for it.)For cryptographic work, concatenate high bits of the generator output (say, half, or perhaps three quarters of the bits) to the block width of a cryptographic hash algorithm (like 512 bits for SHA512), and use the result. It will pass all randomness tests, and even with a complete output sequence of any reasonable length and a cryptographic oracle (that provides all ciphertexts given the hashes) you cannot reliably estimate the next generated value, because it depends on the discarded bits that cannot be extrapolated from the output. The number of opportunities depends on the number of bits you drop. If you drop a quarter of the bits, you have a 1 in 2128chance of guessing the next generated 512-bit value – and that is with a cryptographic oracle. (This assumes generators that mostly pass BigCrush, and whose period is on the order of their output size, or larger.)

author: Nominal Animal
author_rank: Super Contributor
timestamp: June 16, 2020, 01:50:26 pm
content: Quote from: hamster_nz on June 16, 2020, 12:34:35 pmBy standard C rules ("integer promotion" in the drafts I linked to above),-1(and(-1)) are of typeint, so even on that machine,EOFis defined to a negative value of typeint.

author: golden_labels
author_rank: Super Contributor
timestamp: June 16, 2020, 01:56:34 pm
content: Quote from: magic on June 16, 2020, 09:13:50 amPadding bits is about representation, not semantics of the expressions. In particular they are accessible if you access the object throughchar*, even if they have no meaning to the object itself.Quote from: magic on June 16, 2020, 09:13:50 amIt does too. It doesn’t produce UB only becauseuint8_tis typically just an alias tounsigned charand you can access any data through a pointer tochar,unsigned charandsigned char.Also that particular piece of code is an internal implementation tailored for particular compilers. Those are not written in “just any C”, but in the very specific flavour of C suppoted by the given compiler.A side note. Earlier I’ve mentioned padding bits and trap representations. This is something often dismissed as “this happens only on imaginary platforms and no one has ever seen that in the wild”. The truth is quite opposite. Both are present even on platforms as popular as x86_64.long doublehas padding bits. Right, no one useslong double, so what obscure type will I use for trap repreentation?bool!For example this code, if compiled withclangorgccon x86_64 Linux,with -O2 optimizations, produces undefined behaviour:Code:#include <stdbool.h>#include <stdio.h>#include <stdlib.h>static int decide(bool* b) {if (*b) {return 1;} else {return 0;}}int main(void) {bool cond;*(char*)&cond = 3;printf("Decide = %d\n", decide(&cond));return EXIT_SUCCESS;}It’s not hard to determine what has happened under the hood. But the code, as seen from programmer’s perspective, doen’t express such behaviour. The only two possible outputs are "Decide = 0\n" or "Decide = 1\n", becausedecidecan’t return anything other than 0 or 1. Yet, due to UB and optimization, it “returns” 3.Quote from: hamster_nz on June 16, 2020, 12:34:35 pmWhich is…int. The expression-1has typeint. The point is, however, not what is the type of the expression generated by theEOFmacro, but the type with which it is to be used, which isint.