- Why should a "c" programmer learn c++ ? (V2) - 12 Updates
- max without ifs - 9 Updates
- Why should a "c" programmer learn c++ ? - 2 Updates
- Are there any asm-instructions to support OOP - 1 Update
- Available C++ Libraries FAQ - 1 Update
Ben Bacarisse <ben.usenet@bsb.me.uk>: Sep 15 01:30AM +0100 > Juha Nieminen to Anton Shepelev: <cut> > to do it, but here is my attempt, which did not come quite > from the tips of my fingers: > int **p[100]; It's not really the point of the thread, but that's an array of pointers to pointers to int. A pointer to an array must use parentheses, (*p)[...], so that the "pointer to" is read before the "array of" bit: int (*p)[][100]; > one learns its logic, and even then it reamains at a > considerable angle with human intuition. But is not this > syntax the same in C++? Yes, but in most cases a C++ program would use a different type altogether. >> programming in C (or C++) for decades. I kid you not. > Not to know that a language supports such a basic data > structure? I think the point is that few know you can have a "pointer to array" type. Pointers to the start of an array (which will have the same base type as the array) are ubiquitous in C but pointers to an array type are much rarer. <cut> > typedef void ( *f_signal)( int sig, f_disp disp ); > And again, as far as I know, this declaration syntax is as > essential part of C++ as it is of C. Why mention it? It's no longer "essential" as C++ has other syntax. For example: auto signal(int sig, auto (*disp)(int) -> void) -> auto (*)(int) -> void; <cut> -- Ben. |
Juha Nieminen <nospam@thanks.invalid>: Sep 15 07:28AM > to pointers to int. A pointer to an array must use parentheses, > (*p)[...], so that the "pointer to" is read before the "array of" bit: > int (*p)[][100]; Yeah, not really the point of the thread, but this beautifully demonstrates my point. The correct syntax for "pointer to a two-dimensional array of int, where the second dimension is 100" is: int (*p)[100]; Although, technically speaking, that's just "a pointer to an array of int of size 100", but since a "pointer to value" doubles as a "pointer to an array of values", that likewise doubles as a pointer to a two-dimensional array. While 'p' above is technically speaking "a pointer to an array of size 100", the difference with a mere "int *p;" is what it dereferences to, and how it behaves in terms of arithmetic. More precisely: int *p1; // pointer to int (or int array) int (*p2)[100]; // pointer to an int[100] (or array of such) sizeof(*p1) == sizeof(int); sizeof(*p2) == 100*sizeof(int); ++p1; // will increment p1 by sizeof(int) ++p2; // will increment p2 by 100*sizeof(int) A two-dimensional array, like "int table[200][100];" is essentially just "an array of 200 int[100] elements", which is why you can have a pointer that points to int[100] and have it point to such an array. It's perhaps understandable, but telling, that even many experienced C (and C++) programmers don't know this, even after decades. Incidentally in C++ you can have a reference to an actual 2-dimensional array, where both dimensions are specified: int (&r)[200][100]; The semantics of this would require another essay to explain. |
Keith Thompson <Keith.S.Thompson+u@gmail.com>: Sep 15 01:01AM -0700 > int of size 100", but since a "pointer to value" doubles as a "pointer > to an array of values", that likewise doubles as a pointer to a > two-dimensional array. So, technically speaking, int (*p)[100]; is *not* the correct syntax for "pointer to a two-dimensional array of int, where the second dimension is 100". In fact the correct syntax for that is: int (*p)[][100]; (Yes, I cheated and used cdecl.) The distinction between arrays and pointers is muddy enough without glossing over the very real differences. [...] -- Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com Working, but not speaking, for Philips Healthcare void Void(void) { Void(); } /* The recursive call of the void */ |
Paavo Helde <eesnimi@osa.pri.ee>: Sep 15 12:25PM +0300 15.09.2020 02:15 Anton Shepelev kirjutas: > Not at all, because neither of those languages is a near > subset of C++, which has all the complexity of C and tons > more to boot. These complexities do not add. When one programs in C++, one takes care to actively *avoid* unnecessarily complex subsets of C (and most probably also several subsets of C++, but that's beside the point). Foe example, std::string is much more easier to use correctly than strcat(). By using std::vector and std::array one can easily avoid all syntax and behavior complexities of raw arrays. The list goes on. |
Bart <bc@freeuk.com>: Sep 15 10:31AM +0100 On 15/09/2020 08:28, Juha Nieminen wrote: > int of size 100", but since a "pointer to value" doubles as a "pointer > to an array of values", that likewise doubles as a pointer to a > two-dimensional array. That's not helpful. Your example is a 'pointer to 2D array' in the same way that: int* p; doubles as 'pointer to array'. Although this is extremely common C idiom, it is NOT a pointer to 2D array. An actual 'pointer to 2D array' would actually double, due to the same idiom, as 'pointer to 3D array': int (*p)[][100]; int i,j,k; p[i][j][k]; What a language where you can have a discussion as to whether an array has 2 dimensions or 3! At any point where you end up with a T* type, you can optionally turn that into a gratuituous array dimension, where the value is set up for such an array or not. > array, where both dimensions are specified: > int (&r)[200][100]; > The semantics of this would require another essay to explain. (My language only has such references for parameters. There, assuming the most likely meaning of that C++ type, the same thing would be declared as: [200,100]int &r which means that the type is really 'ref[200,100]int r', but the necessary & and deref operations are done transparently.) |
Ben Bacarisse <ben.usenet@bsb.me.uk>: Sep 15 11:06AM +0100 > The correct syntax for "pointer to a two-dimensional array of int, where > the second dimension is 100" is: > int (*p)[100]; No, that's not the type you asked for. It is usual to /access/ an array of int using an int pointer, but a pointer to an array of int is another thing altogether. Likewise, you can use the above to access the elements of an array whose element type is int[100], but it is not a pointer to 2D array. > int of size 100", but since a "pointer to value" doubles as a "pointer > to an array of values", that likewise doubles as a pointer to a > two-dimensional array. Not at all. A pointer to int (int *) points to an int -- one single int. That may be a lone int, and int as the start of an array or an int in the middle of an array, but it is not a pointer to an array of int. > that points to int[100] and have it point to such an array. > It's perhaps understandable, but telling, that even many experienced > C (and C++) programmers don't know this, even after decades. I think you have just added to the general confusion. Don't call a pointer to int a pointer to an array of int, call it a pointer /into/ an array, if you must, or you can just stick with what it is: a pointer to int. Likewise, a pointer to an array of 100 ints is not a pointer to 2D array if ints, it is a pointer /into/ (i.e. to one member of) such an array. -- Ben. |
olcott <NoOne@NoWhere.com>: Sep 15 08:45AM -0500 On 9/15/2020 4:25 AM, Paavo Helde wrote: > Foe example, std::string is much more easier to use correctly than > strcat(). By using std::vector and std::array one can easily avoid all > syntax and behavior complexities of raw arrays. The list goes on. You totally get it !!! -- Copyright 2020 Pete Olcott |
olcott <NoOne@NoWhere.com>: Sep 15 08:46AM -0500 On 9/15/2020 5:06 AM, Ben Bacarisse wrote: > int. > Likewise, a pointer to an array of 100 ints is not a pointer to 2D array > if ints, it is a pointer /into/ (i.e. to one member of) such an array. I don't see the difference. -- Copyright 2020 Pete Olcott |
olcott <NoOne@NoWhere.com>: Sep 15 09:10AM -0500 On 9/15/2020 9:03 AM, Bonita Montero wrote: > Not automatically. You have to write "vecA = move( vecB );" > instead of "vecA = vecB;". That's trivial, but you have to > know how to use it. I don't rememeber ever needing to do this, I simply used the default assignment operator(). -- Copyright 2020 Pete Olcott |
olcott <NoOne@NoWhere.com>: Sep 15 09:16AM -0500 On 9/15/2020 8:46 AM, olcott wrote: >> Likewise, a pointer to an array of 100 ints is not a pointer to 2D array >> if ints, it is a pointer /into/ (i.e. to one member of) such an array. > I don't see the difference. Whenever I need a two-dimensional array I simply use this one: // comp.lang.c++ message from Gianni Mariani Nov 24, 2005 at 9:21 pm // http://groups.google.com/group/comp.lang.c++/msg/a9092f0f6c9bf13a -- Copyright 2020 Pete Olcott |
Bonita Montero <Bonita.Montero@gmail.com>: Sep 15 04:18PM +0200 >> know how to use it. > I don't rememeber ever needing to do this, I simply used the default > assignment operator(). This makes move-semantics only if vecB is a temporary. |
Bart <bc@freeuk.com>: Sep 15 03:39PM +0100 On 15/09/2020 14:46, olcott wrote: >> Likewise, a pointer to an array of 100 ints is not a pointer to 2D array >> if ints, it is a pointer /into/ (i.e. to one member of) such an array. > I don't see the difference. If P points to an array of 100 ints, and Q points to the first element of that array, what do you think each of these does: ++P; ++Q; ? Hint: the difference matters. |
Melzzzzz <Melzzzzz@zzzzz.com>: Sep 15 01:34AM >> You have pmaxsd instrunction and I guess intrinsic for x86. No need for >> this abomination and much more efficient. > I did try to ask for C++ not assembler nor compiler builtins. Then don't bother. -- current job title: senior software engineer skills: c++,c,rust,go,nim,haskell... press any key to continue or any other to quit... U ničemu ja ne uživam kao u svom statusu INVALIDA -- Zli Zec Svi smo svedoci - oko 3 godine intenzivne propagande je dovoljno da jedan narod poludi -- Zli Zec Na divljem zapadu i nije bilo tako puno nasilja, upravo zato jer su svi bili naoruzani. -- Mladen Gogala |
Bonita Montero <Bonita.Montero@gmail.com>: Sep 15 07:04AM +0200 > return choose[ b > a ]( a, b ); > } > Compiles cleanly as both C{90,99,11} and C++{98,03,11,14,17}. It needs a good compiler to optimize away these stupid function-calls. Otherwise this code would be rather slow. |
"Öö Tiib" <ootiib@hot.ee>: Sep 14 11:55PM -0700 On Tuesday, 15 September 2020 08:04:52 UTC+3, Bonita Montero wrote: > > Compiles cleanly as both C{90,99,11} and C++{98,03,11,14,17}. > It needs a good compiler to optimize away these stupid > function-calls. Otherwise this code would be rather slow. Yes, but it usually is not generating branches like I did ask. Your trick, Bonita, is also good as majority of compilers (and all popular) optimize that ?: away on targets. For example on RISC-V: bonita_max(int, int): slt a5,a0,a1 subw a5,zero,a5 xor a1,a0,a1 and a1,a1,a5 xor a0,a0,a1 sext.w a0,a0 ret tim_max(int, int): sgt a5,a1,a0 slli a4,a5,3 lui a5,%hi(.LANCHOR0) addi a5,a5,%lo(.LANCHOR0) add a5,a5,a4 ld t1,0(a5) jr t1 Interesting trick, jumping to address of called function and letting it to return for you. Haven't profiled, my own convoluted code most likely performs worst everywhere. :D |
Bonita Montero <Bonita.Montero@gmail.com>: Sep 15 08:58AM +0200 > Interesting trick, jumping to address of called function and letting > it to return for you. Haven't profiled, my own convoluted code most > likely performs worst everywhere. :D Wouldn't work with shadow-stacks which will be introduced by Intel's control-flow-enforcement technology. |
"Öö Tiib" <ootiib@hot.ee>: Sep 15 12:06AM -0700 On Tuesday, 15 September 2020 09:58:56 UTC+3, Bonita Montero wrote: > > likely performs worst everywhere. :D > Wouldn't work with shadow-stacks which will be introduced > by Intel's control-flow-enforcement technology. Doesn't matter as I mostly try to make Intel's competitors to look good. By my philosophy world without monopolies is better for everybody, Intel included. |
Juha Nieminen <nospam@thanks.invalid>: Sep 15 07:32AM > return a & mask | b & ~mask; > } > The ternary operation is often substituted by a "sbb regx, regx". Where's the carry flag coming from? (Btw, if you use #include <cstdint> you have to use std::int32_t, not int32_t. I don't think that the standard guarantees that <cstdint> will dump the names into the global namespace. You have to use <stdint.h> for that.) |
Bonita Montero <Bonita.Montero@gmail.com>: Sep 15 09:51AM +0200 >> } >> The ternary operation is often substituted by a "sbb regx, regx". > Where's the carry flag coming from? "cmp regA, regB; sbb regMask, regMask" |
Tim Rentsch <tr.17687@z991.linuxsc.com>: Sep 15 01:45AM -0700 >> It needs a good compiler to optimize away these stupid >> function-calls. Otherwise this code would be rather slow. > Yes, but it usually is not generating branches like I did ask. Do you mind if I ask what is motivating the original question? Or what the criteria are for a successful answer? The answer I gave was somewhat tongue-in-cheek since even though it was not using if() or ?: I was pretty sure it was not what you were looking for. I do have a solution that uses only common operators, and no function call trickery (and of course no library functions), and thus likely satisfies what you would call "raw" C++, but before I post something more I want to be sure I know what it is you're really looking for. > jr t1 > Interesting trick, jumping to address of called function and letting > it to return for you. As tail calls go it's one of the simplest, which makes it one of the easiest optimizations to do. I'm surprised in a way that compilers don't do this particular optimization even at level -O0. |
"Öö Tiib" <ootiib@hot.ee>: Sep 15 06:03AM -0700 On Tuesday, 15 September 2020 11:45:57 UTC+3, Tim Rentsch wrote: > I gave was somewhat tongue-in-cheek since even though it was > not using if() or ?: I was pretty sure it was not what you > were looking for. I understood it was tongue-in cheek but it was still interesting to look what compilers do from it at different platforms, thanks. Actually weather was bad at weekend and so I was reading various interesting computation and vectorising algorithm papers (doing lot of same calculation in parallel). So that silly idea just randomly popped into mind. > of the easiest optimizations to do. I'm surprised in a way > that compilers don't do this particular optimization even > at level -O0. Yes, these usually do with ordinary call. Here was (kind of calculated) function pointer, may be that somehow affects it. |
Chris Vine <chris@cvine--nospam--.freeserve.co.uk>: Sep 15 01:58AM +0100 On Mon, 14 Sep 2020 00:16:32 -0400 > such array, then 4.3 applies, and the behavior is undefined. > If C++2020 handles this issue better than C does, the different wording > that makes that true must lie in some other part of the document. I don't agree as a matter of English that C++20's [expr.add]/4 has the same effect as §6.5.6/8. Furthermore the C standard states of the C allocation functions and arrays that "The pointer returned if the allocation succeeds is suitably aligned so that it may be assigned to a pointer to any type of object with a fundamental alignment requirement and then used to access such an object or an array of such objects in the space allocated (until the space is explicitly deallocated)". You seem to be arguing that the only means of constructing such an array is via memcpy() or memmove(). If so I disagree: things must be read in context. Let's agree to disagree because I don't think this is worth spending further time on. > array, without either of those two special cases applying. Therefore, a > strict reading of the rules of pointer arithmetic gives access to any > element of such an array other than the first element undefined behavior. See above on arrays. The wording isn't ideal but it just about does it I think. > X *p = (X*)std::malloc(n*sizeof(struct X)); > would implicitly create an n-element array. I certainly wouldn't object > to that being the case, but it doesn't clearly say so. On considering the standard further I think it does clearly say so once you try to iterate over this memory by pointer arithmetic. [basic.types]/9 says that all array types array types are implicit-lifetime types. An implicit-lifetime type will automatically be constructed in malloc'ed memory if doing so would result in the program having defined behavior ([intro.object]/10 and [c.malloc]/4). If you iterate a pointer over malloc'ed memory as if it were an array then it becomes an array because that is necessary to give the program defined behaviour. (As it happens that is also indisputably the intention of p0593r6, and I think that intention has been achieved.) > undefined behavior, so this represents a defect in the standard, but I > don't see any way to justify saying that there's an array that can be > used to give meaning to such pointer arithmetic. See above > > array types are now implicit-lifetime types. I will need to consider > > revised [intro.object]/10 further on this. > Could you explain what that section says that makes you feel that way? See above > defined for 1 + &i, or have I missed something that defines it? I've > just started reading this version of the standard - I wouldn't be > surprised if I missed something. I have no idea what you mean. At any rate, I am pretty certain that you have misunderstood what I meant. If you want to continue (and I am not necessarily suggesting that you should, but do so if you would like), can you start from the beginning again without reference to the text of prior postings in a concise form. |
Chris Vine <chris@cvine--nospam--.freeserve.co.uk>: Sep 15 10:24AM +0100 On Tue, 15 Sep 2020 01:58:55 +0100 Chris Vine <chris@cvine--nospam--.freeserve.co.uk> wrote: [snip] > then it becomes an array because that is necessary to give the program > defined behaviour. (As it happens that is also indisputably the > intention of p0593r6, and I think that intention has been achieved.) By the way, please note that on my reading an array can come into existence as an implicit-lifetime type without the array elements coming into existence. That would have to be the case where the array elements are not themselves implicit-lifetime types: they would need to be constructed element by element by placement new (not placement new[], which cannot reliably be used). The fact that there is an array in existence means that you can iterate a pointer over the memory in order to use placement new. Of course, if the array elements are of implicit-lifetime type, then they can be constructed by memcpy()/memmove() or by assignment. Placement new is not necessary. > not necessarily suggesting that you should, but do so if you would > like), can you start from the beginning again without reference to the > text of prior postings in a concise form. OK, I now see your point. You are pointing out that the text which says that "For purposes of pointer arithmetic and comparison, an object that is not an array element whose address is taken in this way is considered to belong to an array with one element of type T" has been removed in C++20. I hadn't noticed that. That created a further problem with N4849. However, I don't think that is an issue with N4860 because of the implied existence of an array. |
Juha Nieminen <nospam@thanks.invalid>: Sep 15 07:11AM > Where did you get the idea that CPUs like consecutive memory accesses? Pretty much in any documentation you care to search. If you really want the nitty-gritty details, read eg. Intel's own optimization documentation. The CPU has liked consecutive memory accesses for pretty much as long as they have been using caches (which would be somewhere around the 80486 or the Pentium). When you read a value from RAM for the first time, the cache logic will not just load exactly the amount of bytes that you read, but a block of RAM from around that location (I don't remember now how large it was, but it might have been something like 64 bytes or the like). This means that if you read a value from a particular memory location and it's not already in L1 cache, a block of memory will be read into cache at once, and if your next access is within that block, it will be extraordinarily fast, because the value will already be in L1 cache. You can test this yourself (I have). For example, dynamically allocate like a 1-gigabyte array, and read its bytes in its entirety but jumping in steps of, like 256 bytes (ie. you first read the byte at offset 0, then the one at offset 256, then the one at offset 512 and so on. When you get to the end of the array, do the same but at offset 1, 257, 513 and so on. Keep doing this until you have read every single byte.) To stop the compiler from optimizing your reading loop away, calculate eg. the sum of all the bytes and print it. Then change the program to do the same, but just read the bytes in the array consecutively from beginning to end. The difference in speed is *staggering*. The reason for this is that in the first case every single read will cause a complete cache miss, and is extremely slow. In the second case there will be a cache miss only every 64th read or such. >>largely work against this, causing even more performance hits. > Again, modern processors handle this rather well using techniques > such as branch target buffers and return stack caches. Clearly you haven't actually tested the difference in speed that handling an array of values directly has compared to making a function call that handles one member variable at a time. >>examples of this. > Please do. Also note the relatively small number of code sequences that > are actually amenable to autovectorization. I'm a bit busy at the moment so I don't really have the time to write code, but I noticed this quite clearly in one hobby project of mine. I had a class that had a large std::vector as a member (containing instances of a struct with a few integers inside), and the class had a member function that returned a vector element and another that could be used to set it. Due to technical details it wasn't feasible to make these functions inline, so they were implemented in their own compilation unit (which caused an actual function call to be done when calling either one). At some point I was wondering why doing an operation to the entire vector from the outside using these two functions was so slow. If I added a third function that simply returned a pointer to the beginning of the vector and did the same operation directly to the data, without any member function calls, it was significantly faster, like 5 times faster or so. This made little sense. A slight slowdown, like a few percents, would have made sense because of the function call, but given that the CPU does predictive branching (especially on unconditional function calls), a function call shouldn't be any slower than if the code had been inlined. Even if calling the function had put some values on the stack, it wouldn't have made it 5 times slower. However, when I examined the asm produced by the compiler, it all became clear: The member function calls were hindering autovectorization, and that's the reason why it was about 5 times slower. You see, the version of the code that directly accessed the vector through a pointer was being heavily autovectorized by the compiler. However, the version that did so through the member function calls was not. The compiler cannot autovectorize code that jumps somewhere else to do something the compiler cannot see while compiling this particular compilation unit. (If the two member functions had been inline, it might have been a different story. However, as said, in this particular case there were technical reasons why they couldn't be inline, and needed to be in their own compilation unit.) |
Leo <usenet@gkbrk.com>: Sep 15 09:42AM +0300 On 9/15/20 1:23 AM, Nikki Locke wrote: > If you know of a library which is not in the list, why not fill > in the form at http://www.trumphurst.com/cpplibs/cppsub.php > Maintainer: Nikki Locke - if you wish to contact me, please use the form on the website. Won't this work better as an awesome list or a DuckDuckGo search with "[topic name] c++"? I think a whole webpage for it is overkill. |
You received this digest because you're subscribed to updates for this group. You can change your settings on the group membership page. To unsubscribe from this group and stop receiving emails from it send an email to comp.lang.c+++unsubscribe@googlegroups.com. |
No comments:
Post a Comment