Newsgroups: ukc.bugs Subject: Re: Beginners problem Summary: Expires: References: <429@falcon.ukc.ac.uk> <975@gos.ukc.ac.uk> Sender: Reply-To: mg Followup-To: Distribution: ukc Organization: Computing Lab, University of Kent at Canterbury, UK. Keywords: This article is mostly nit-picking, so if you are a beginner and understood Tim's article, you would be well advised to skip this one. C language lawyers, read on... In article <975@gos.ukc.ac.uk> tjo@gos.UUCP (Tim Oldham) writes: >In article <429@falcon.ukc.ac.uk> mlo@ukc.ac.uk (M.L.Oldfield) writes: >> >> ... The use of the line... >> >> fscanf(fp,"%d",core[a].op); >> >>where core[a].op is defined as an integer structure array, kept giving a >>segmentation error. > >fscanf requires the address of the variable to be read into - the equivalent of >a Pascal var parameter. In effect, yes, but not in usage. In Pascal, the function declaration says whether something is a var parameter or not, and the calling syntax is identical; In C, *all* parameters are passed by value (ie the function only gets a copy of the original variable), so the effect of a var parameter, ie one that is modified by the function, has to be achieved by hand. This is done by passing a copy of the numeric memory address of the memory location allocated to the variable you want to modify, and the function then has to poke into the memory location whose address it has been told. If you understand simple microprocessor architectures and the difference between memory addresses and their contents, this should all appear horribly mechanical, which it is. If you hand fscanf, which is just an ordinary C function which you could write yourself, the name of an integer variable, it gets a copy of the contents of that integer variable, say 3, and then goes to fill memory location 3 with whatever it reads in from its input. To get fscanf to fill your dear integer with interesting things, you have to tell it the memory address of the locations you want twiddled, namely &object where object is any lvalue of type "integer". The odds of a random 32-bit integer being in your virtual address space, which is probably under 30Kbytes, is vanishingly small, so you are very likely to be stopped by the virtual memory manager. If you are *really* unlucky, the random number in your integer will indeed be in your address space. Then *really* freaky things start happening as fscanf starts poking numbers into arbitrary locations in your program. (usually when you next call malloc()! :-) ) >... The name of an array is a synonym for the address of the first element >of an array, so if "string" is a char array, &string[0] == string. Urg. Sort of. C arrays masquerading as pointers all seems very good for a while, but the more you think about it, the more you realise that they almost got it right. Array names get converted to the address of the first element when they are passed unadorned to functions, but other than that, the apparent similarity has to be treated with some thought. Consider sizeof(array) and sizeof(pointer). Consider also that arrays are the only(?) C objects that are passed to functions by reference instead of by value, but that struct a { int contents[100]; } vessel; function(vessel); copies the array by value as part of a structure passed by value. It strikes me that array-by-reference is a burnt-in efficiency hack to the language, for the same reason the Pascal arrays are often passed by reference, but in C they are hard-wired into the language and difficult to escape. Also the late appearance of passing structures to functions in the C language (a similar task to passing arrays by value) suggests either that they couldn't be bothered to write the code to do it, or thet they couldn't bear to think of anybody doing anything so horrendously inefficient on a PDP-7. Talking of arrays, multi-dimensional arrays disturb me because: Suppose you have a 2-dimensional array consisting of two 12-element arrays, called day_tab. To access the 7th element of the second row, you use day_tab[1][6]. *-footnote This can be read as a two-step process: day_tab[1] gives you a one-dimensional array, or which you then select the 7th item. It would be consistent, then, to declare it as int day_tab[12][2], read ("red") "(int daytab[12])[2]", an aray of twelve ints, twice. However, this clashes with the way you would logically access the elements, such that the first argument goes from 0..1 and the second goes from 0..11. You can't win, so they make it the intuitive way round. Note that if you now have a square buffer of pixels, say pix[640][400] and you want to access it pix[x][y], then the picture will be stored as columns of 400 pixels which are consecutive in memory, 640 off. This is not the intuitive (to me) way to store things, and has a bad impact on the behaviour of the virtual memory manager if you read stuff in a row at a time because you have to scan the block of memory "against the grain". sigh. > fscanf(fp, "%d", &core[a].op); > >If you'd used lint, the C program verifier, it would have told you that arg 1 >of your fscanf call was of the wrong type. see man lint. ??? Ok, yes, it would, but arg 1 is not the one that's wrong - that's a bug in lint. Args 3 onwards of fscanf cannot be checked because they are of variable type depending upon the contents of arg 2. True, but an elephant**. ----- *) Before any BASIC lovers start whinging about arrays going from 0..n-1, try using it for a year and you will see that it is the only way to travel. No more adding and subtracting one all the time! **) An elephant == irrelevant