Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
raminasi
Jan 25, 2005

a last drink with no ice
I would recommend against taking a C++ class to help you learn C. The languages are technically distinct but similar enough that I think you will be confused trying to learn that way. There a few subtle behavioral differences in the seemingly overlapping parts, but more importantly, even a good C++ class will be largely teaching the ++, not the C.

And as it turns out, while C++ might be "more complicated" than Java or some of the Lisps, it's hard to argue that C is. The language is actually pretty dang simple. This means that people use it to do some gnarly things, which is where its intimidating reputation comes from. (Also because teaching pointers is apparently rocket science for some reason that I haven't been able to figure out.)

If it were 20 years ago, all you'd need to do is work your way through K&R, but the language has been updated twice since then, and I don't know of a good equivalent for the new standard. Maybe someone else can chime in with a recommendation.

Adbot
ADBOT LOVES YOU

shrughes
Oct 11, 2008

(call/cc call/cc)
What classes you take shouldn't dictate what languages you end up knowing. If you want to learn C, then learn it. That has nothing to do with taking a class.

space kobold
Oct 3, 2009


C is much easier to understand in its entirety than C++.

Even if it's dated, if you're interested in learning C I'd highly recommend picking up The C Programming Language, 2nd Edition. The book is no wider than your thumb and covers the entirely of the language, obviously excluding the more recent developments.

Compare this to the C++ "Primer" book which is a good 6 times thicker and can be used as a weapon in dire situations.

Sadly I'm not sure what would be a good recommendation for learning modern C though. The most modern book focusing exclusively on C that I own was printed over 14 years ago.

edit: I'm really just re-hashing what GrumpyDoctor said aren't I? :v:

space kobold fucked around with this message at 11:53 on Jul 17, 2014

Suspicious Dish
Sep 24, 2011

2020 is the year of linux on the desktop, bro
Fun Shoe
Ask the professor to see if they'll be teaching basic C as you go along in Operating Systems, since they don't teach a C course? It doesn't make sense to offer a course with prereq's that no student could ever learn.

Blotto Skorzany
Nov 7, 2008

He's a PSoC, loose and runnin'
came the whisper from each lip
And he's here to do some business with
the bad ADC on his chip
bad ADC on his chiiiiip
It'll be probably be easier to learn C ahead of time rather than while also learning how I/O schedulers work. K&R 2E won't teach you modern C idioms, but it'll teach you well enough to do coursework. Just remember to check your return values!

Paul MaudDib
May 3, 2006

TEAM NVIDIA:
FORUM POLICE

Xun posted:

Not sure if this is the right place to ask, but next semester I'm required to take Operating Systems, which uses C. I only know java and python and I'm shaky at C++ at best. I'm feeling super unprepared because so far my programming education have been a progression of Java -> Java/LISP(???) -> A tiny bit of assembly and some C++ -> C

What should I do? Should I take a C++ class next year (C isn't offered)? I'm not sure how to go about learning a more complex language like C on my own, most of the tutorials online seem to be basic programming stuff like ints and loops. I feel like I've been literally taught nothing.

As mentioned, there's pretty big differences between "C" and "C++" style programming, there's a different set of programming idioms and stuff. Assuming you want to learn C, the K&R book is the classic.

I'm generally of the opinion that programming is mostly language-agnostic, it's about breaking down your problem into discrete steps and then applying the tools your language gives you to solve that. Java is ultimately a distant descendent of C, the roughness of it (pointers/pointer arithmetic, the stdlib, etc) might be a bit of a slap but if you do OK at Java you'll eventually adapt to the syntax and behavior. It's something you'll probably have to do sooner or later given how much stuff uses C as a jumping-off point.

If you're worried about it, do some assignments using the stdlib that do stuff like timing, IO, etc and that should give you an introduction to the C style idioms.

Paul MaudDib fucked around with this message at 22:39 on Jul 18, 2014

Xeom
Mar 16, 2007
I am trying to understand some of the differences between the different constant types when defining a function in a class. Lets assume all this code is written within a class definition. Please let me know if I have misunderstood anything, I have a feeling I got something wrong.

code:
 classtype &thing1(int a, int b) { do things; return *this; };
Here we have a implied constant to this. That is the functions pointer will always point to the object that it was called with.

code:
 classtype &thing2(int a, int b) const { do things; return *this; };
Here we add a low level constant that makes it so this function cannot edit the object.

code:
const classtype &thing2(int a, int b) const { do things; return *this; };
This constant in front is just for having constant and non constant versions of you function.

Xeom fucked around with this message at 22:13 on Jul 19, 2014

Vanadium
Jan 8, 2005

The bit with const in the middle just means that the function can be called on class instances that are themselves const, and that the this pointer inside the function body will be a pointer to const.

It's kinda weird that your member function is returning *this but also returning type int&.

nielsm
Jun 1, 2009



Xeom posted:

code:
 classtype &thing2(int a, int b) const { do things; return *this; };
Here we add a low level constant that makes it so this function cannot edit the object.

This should fail to compile.

Inside the function, this is of type const classtype *, meaning that the type of *this will be const classtype. If you attempt to return a reference to that, the reference must of of type const classtype & because the object in question is const.

And it's not a "low level constant", you're declaring the function as being const.

Xeom
Mar 16, 2007

Vanadium posted:

The bit with const in the middle just means that the function can be called on class instances that are themselves const, and that the this pointer inside the function body will be a pointer to const.

It's kinda weird that your member function is returning *this but also returning type int&.

Sorry don't know what I was thinking when I wrote the type, its fixed now.

nielsm posted:

This should fail to compile.

Inside the function, this is of type const classtype *, meaning that the type of *this will be const classtype. If you attempt to return a reference to that, the reference must of of type const classtype & because the object in question is const.

And it's not a "low level constant", you're declaring the function as being const.

Ok I was hoping it wouldn't compile. I was hoping that it could access objects(const and non), but not write to them. I am get your first point, but not your second.

It sounds like you are saying that my third example does actually compile? I was hoping that it also would not compile. Aren't you assigning a new value to a constant object?

Xeom fucked around with this message at 23:08 on Jul 19, 2014

PRADA SLUT
Mar 14, 2006

Inexperienced,
heartless,
but even so
In OSX, if I'm trying to .open a file on my desktop, what do I enter as the path? "Users/Me/Desktop/file.txt" isn't it.

Malcolm XML
Aug 8, 2009

I always knew it would end like this.

PRADA SLUT posted:

In OSX, if I'm trying to .open a file on my desktop, what do I enter as the path? "Users/Me/Desktop/file.txt" isn't it.

/Users/Me/Desktop/file.txt

PRADA SLUT
Mar 14, 2006

Inexperienced,
heartless,
but even so
One other question. I'm writing a program that has the user input the directory of a .txt file (string location) and then it parses the data and does something. It's supposed to kill the program if the user enters an incorrect directory.

code:
ifstream fin;
    fin.open(location.c_str());
    if (!fin)
    {
        cout << "Invalid file: " << location << "\n";
        return 1;
    }
Normally, when I run this part and enter an invalid anything, it ends the program. However, if I enter the character "a" or "/", it continues to run the program, even though it shouldn't be valid. Any idea why?

Sinestro
Oct 31, 2010

The perfect day needs the perfect set of wheels.

PRADA SLUT posted:

One other question. I'm writing a program that has the user input the directory of a .txt file (string location) and then it parses the data and does something. It's supposed to kill the program if the user enters an incorrect directory.

code:
ifstream fin;
    fin.open(location.c_str());
    if (!fin)
    {
        cout << "Invalid file: " << location << "\n";
        return 1;
    }
Normally, when I run this part and enter an invalid anything, it ends the program. However, if I enter the character "a" or "/", it continues to run the program, even though it shouldn't be valid. Any idea why?

'/' is a valid path, though. It's the root directory. You probably have a folder 'a' in your CWD.

Zopotantor
Feb 24, 2013

...und ist er drin dann lassen wir ihn niemals wieder raus...

Xeom posted:

I am trying to understand some of the differences between the different constant types when defining a function in a class. Lets assume all this code is written within a class definition. Please let me know if I have misunderstood anything, I have a feeling I got something wrong.

code:
 classtype &thing1(int a, int b) { do things; return *this; };
Here we have a implied constant to this. That is the functions pointer will always point to the object that it was called with.

code:
 classtype &thing2(int a, int b) const { do things; return *this; };
Here we add a low level constant that makes it so this function cannot edit the object.

code:
const classtype &thing2(int a, int b) const { do things; return *this; };
This constant in front is just for having constant and non constant versions of you function.

The basic thing to understand is the difference between a constant pointer and a pointer to a constant.
code:
int a, b;

int *p = &a;                 // non-constant pointer to non-constant int
p = &b;                      // OK
*p = 42;                     // OK

int *const q = &a;           // constant pointer to non-constant int
q = &b;                      // error, q is constant
*q = 23;                     // still OK

int const *r = &a;           // non-constant pointer to constant int
r = &b;                      // OK
*r = 17;                     // error, *r is constant

int const *const s = &a;     // constant pointer to constant int
s = &b;                      // error, s is constant
*s = 4;                      // error, *s is constant
Now, in a class member function, this is always a constant pointer. The class instance that it points to (i.e., *this) is constant if and only if the function itself is marked const.

Two more traps for the unwary:
code:
int const *p;
const int *q;  // exactly the same as p!

const int foo();        // const is redundant here, nobody will care
const int *bar();       // but not here!
int *const baz();       // redundant again
const int *const zip(); // guess which of them is redundant

Zopotantor fucked around with this message at 08:57 on Jul 20, 2014

Xeom
Mar 16, 2007

Thanks Zopotantor. I did a few examples functions and I think I have a solid grasp on it now. I understood the differences between a constant pointer and a pointer to a constant, but for some reason when it related to functions I sort of got thrown off.

High Protein
Jul 12, 2009
I'm trying to call a member function on an unknown type, which is a template. This works:
code:
template <template <class, class> class T, class F>
auto CallFunc(F func, void* inst)
{
        typedef T<int, std::allocator<int>> RealType;
        return func(static_cast<RealType*>(inst));
}


int main()
{
    std::vector<int> v;
  
    CallFunc<std::vector>([](auto p){p->push_back(3);}, &v);
}
In the actual code, the type on which vector is templated (int here) depends on a type flag from a variant-like structure. This approach works fine, however it requires C++14 support; is there a way to do this without generic lambdas? I couldn't get anything with a template typedef of std::function to work, it always wanted an actual type for the member function. I did get it to work by using a template class functor, however that requires writing a new functor class for each call; that's taken care of by the generic lambdas here.

Harik
Sep 9, 2001

From the hard streets of Moscow
First dog to touch the stars


Plaster Town Cop
Stupid type-punning/aliasing question:

C++ code:
#include <cstdio>

struct m1 {
    unsigned short v;
};

struct m2 {
    unsigned int v;
}

struct reader {
    int pos;
    int expected;
    char buf[16];
} R = {0, {} };

void print_m2(struct m2 * tmp) {
	printf("%08x\n", tmp->v);
}

void onebyte(unsigned char c) {
    R.buf[R.pos]=c;
    if (R.pos++ == 0)
        R.expected=c;
    if (R.pos == R.expected) {
        switch(R.buf[1]) {
            case 0x00: {
                m1 *tmp = (m1 *) (R.buf+2); // breaks strict aliasing
                printf("%04hx\n", tmp->v);
            } break;
            case 0x01:
		print_m2((m2 *)(R.buf+2)); // also breaks strict aliasing
		break;
        }
        R.pos=0;
    }
}

int main() {
    onebyte(0x04);
    onebyte(0x00);
    onebyte(0x01);
    onebyte(0x02);
    return 0;
}
warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]

(Toy code specifically to demonstrate the question)

Now, if I understand this correctly if I write to the structures and then attempt to read from the char *, it's perfectly legal for the writes to not go through, or to be re-ordered with respect to access of the underlying char * buffer. Also, if I write to the buffer it's legal that a read from the structure won't reflect that. I can live with those restrictions.

The question is: Is the cast itself undefined behavior that can be elided by a compiler, or is a read-only usage like that guaranteed to do what I expect? Do I need a sequence point before the read to prevent re-ordering? (In this specific example I don't think so, because the compiler can't assume expected != 1, but there's probably ways to write this where that's not true)

I'd prefer to avoid memcpy since this is for embedded - and a union is problematic due to the multitude of data-structs that could be returned - either my serial code would have to know about all of them, or I'd have to use a caller-supplied buffer that has all the unions.

Subjunctive
Sep 12, 2006

✨sparkle and shine✨

Strict aliasing lets the compiler assume that pointers to different types are never the same object, so mutation of one can't affect what's seen of the other. For your read-only case, there's no ordering constraint that matters, so IMO you should be fine.

It seems like with some template magic (containing an inner union type) you should be able to make it work too?

Harik
Sep 9, 2001

From the hard streets of Moscow
First dog to touch the stars


Plaster Town Cop

Subjunctive posted:

Strict aliasing lets the compiler assume that pointers to different types are never the same object, so mutation of one can't affect what's seen of the other. For your read-only case, there's no ordering constraint that matters, so IMO you should be fine.

It seems like with some template magic (containing an inner union type) you should be able to make it work too?

Well the problem is if I've hit UB who the gently caress knows what the compiler will do - if casting results in UB then the actual pointer assignment could be elided leading to not a segfault on embedded because there is no zero-page, so I just end up reading/writing from IO registers bad things happening

As far as a union that's also problematic since the serial code is shared so I'd have to either teach it every structure type in on gigantic union or have each subsystem pass-in a buffer/union with the types it knows about, etc.

Yeah, it sucks that a copy of a 32 byte structure is something you need to worry about. On a real machine I'd just memcpy to a struct on the stack and call it a day.

Also:
C code:
void do_bar(const char * buf) {
    struct Bar b;
    memcpy(&b, buf, sizeof(b));
}
void do_foo(const char * buf) {
    struct Foo f;
    memcpy(&f, buf, sizeof(b));
}


switch(buf[0]) {
 case 0:
	do_foo(buf);
        break;
 case 1:
	do_bar(buf);
   	break;
}
Burns stack space for both Foo and Bar, since do_foo and do_bar are simple functions that are probably only used once, the compiler inlines them. Linux kernel hit that on one of the bigger ioctl() switches, it smashed the tiny per-thread kernel stack by putting something like 4k of data structures and variables on the stack at once when a GCC version added automatic inlining of trivial functions. (Previously only when you specifically marked them inline).

Actually, how the hell do you write malloc() without strict-aliasing warnings? Does the pass through void* clear them?

Edit: Oh, it's firing the warning on my code because char[] != char *, and as such isn't exempted from strict-aliasing rules. If I add the pointer and a malloc the warning goes away - but does the danger? Am I reading the standard right that the compiler has to assume that type-punning a char * into a struct may alias?

I hit the error because on a tiny platform without dynamic allocation I baked the buffersize in. It's easy enough to have char * buf; char _rawbuf[16]; buf=_rawbuf;- I can live with two bytes of overhead much more than 30+.

Harik fucked around with this message at 05:34 on Jul 22, 2014

Subjunctive
Sep 12, 2006

✨sparkle and shine✨

Harik posted:

Well the problem is if I've hit UB who the gently caress knows what the compiler will do - if casting results in UB then the actual pointer assignment could be elided leading to not a segfault on embedded because there is no zero-page, so I just end up reading/writing from IO registers bad things happening

As far as a union that's also problematic since the serial code is shared so I'd have to either teach it every structure type in on gigantic union or have each subsystem pass-in a buffer/union with the types it knows about, etc.

Actually, how the hell do you write malloc() without strict-aliasing warnings? Does the pass through void* clear them?

Edit: Oh, it's firing the warning on my code because char[] != char *, and as such isn't exempted from strict-aliasing rules. If I add the pointer and a malloc the warning goes away - but does the danger? Am I reading the standard right that the compiler has to assume that type-punning a char * into a struct may alias?

Technically, it would be UB. The only standards-certain thing is to memcpy. I would be very, very surprised if read-only aliasing could ever cause problems, since the ability for the compiler to treat the pointers as unrelated doesn't have any effect on what code it can generate. (I'm not sure how the sockaddr parts of the sockets API can be used without tripping the UB technicalities here, similarly.)

malloc gets special treatment (newly allocated objects have no declared type, and take the type of the thing stored into them); I forget which passage in C99.

If you're using gcc or clang, "may_alias" on your structure types could be helpful, but you might be giving away a lot of performance elsewhere.

Edit: v8 uses this, which I frankly find a bit suspect unless you're verifying codegen every time compiler config or calling source change:

C++ code:
// The type-based aliasing rule allows the compiler to assume that pointers of
// different types (for some definition of different) never alias each other.
// Thus the following code does not work:
//
// float f = foo();
// int fbits = *(int*)(&f);
//
// The compiler 'knows' that the int pointer can't refer to f since the types
// don't match, so the compiler may cache f in a register, leaving random data
// in fbits.  Using C++ style casts makes no difference, however a pointer to
// char data is assumed to alias any other pointer.  This is the 'memcpy
// exception'.
//
// Bit_cast uses the memcpy exception to move the bits from a variable of one
// type of a variable of another type.  Of course the end result is likely to
// be implementation dependent.  Most compilers (gcc-4.2 and MSVC 2005)
// will completely optimize BitCast away.
//
// There is an additional use for BitCast.
// Recent gccs will warn when they see casts that may result in breakage due to
// the type-based aliasing rule.  If you have checked that there is no breakage
// you can use BitCast to cast one pointer type to another.  This confuses gcc
// enough that it can no longer see that you have cast one pointer type to
// another thus avoiding the warning.
// We need different implementations of BitCast for pointer and non-pointer
// values. We use partial specialization of auxiliary struct to work around
// issues with template functions overloading.

template <class Dest, class Source>
struct BitCastHelper {
  STATIC_ASSERT(sizeof(Dest) == sizeof(Source));
  INLINE(static Dest cast(const Source& source)) {
    Dest dest;
    memcpy(&dest, &source, sizeof(dest));
    return dest;
  }
};

template <class Dest, class Source>
struct BitCastHelper<Dest, Source*> {
  INLINE(static Dest cast(Source* source)) {
    return BitCastHelper<Dest, uintptr_t>::
        cast(reinterpret_cast<uintptr_t>(source));
  }
};

template <class Dest, class Source>
INLINE(Dest BitCast(const Source& source));
template <class Dest, class Source>

inline Dest BitCast(const Source& source) {
  return BitCastHelper<Dest, Source>::cast(source);
}

Subjunctive fucked around with this message at 11:37 on Jul 22, 2014

Bonfire Lit
Jul 9, 2008

If you're one of the sinners who caused this please unfriend me now.

Subjunctive posted:

(I'm not sure how the sockaddr parts of the sockets API can be used without tripping the UB technicalities here, similarly.)
There's a special exception for stuff like this, by use of unions.

C99, 6.5.2.3 posted:

One special guarantee is made in order to simplify the use of unions: if a union contains several structures that share a common initial sequence (see below), and if the union object currently contains one of these structures, it is permitted to inspect the common initial part of any of them ... Two structures share a common initial sequence if corresponding members have compatible types ... for a sequence of one or more initial members.

Bonfire Lit fucked around with this message at 11:55 on Jul 22, 2014

Subjunctive
Sep 12, 2006

✨sparkle and shine✨

Bonfire Lit posted:

There's a special exception for stuff like this, by use of unions.

Sure, but sockaddr/sockaddr_in/sockaddr_in6 aren't unions, they're structures that share the same initial layout, right?

Bonfire Lit
Jul 9, 2008

If you're one of the sinners who caused this please unfriend me now.

That's right. But that exception allows you to turn a sockaddr_in (or sockaddr_in6 or whatever) into a sockaddr to pass to bind(2), via a union. bind then can (legally) inspect the sa_family member to figure out what kind of object the sockaddr* it got actually is, and is allowed to cast to that back to its original type.
But I'm not a compiler writer so I might be interpreting the standard incorrectly.

Subjunctive
Sep 12, 2006

✨sparkle and shine✨

No, it falls apart on the cast. That's what violates the aliasing rule. Once you cast they are assumed to not alias each other. The language you quoted says that if I had sockaddr and sockaddr_in in a union, I could fill one and read the other -- but only for the sub-fields that made up the common prefix. Writing to one field of a union and reading from another is otherwise undefined. :smith:

Blotto Skorzany
Nov 7, 2008

He's a PSoC, loose and runnin'
came the whisper from each lip
And he's here to do some business with
the bad ADC on his chip
bad ADC on his chiiiiip

Harik posted:

Yeah, it sucks that a copy of a 32 byte structure is something you need to worry about. On a real machine I'd just memcpy to a struct on the stack and call it a day.

What uC are you working with where memcpy doesn't work? I do it all day long on Cortex-M and Freescale S08.

e: and when you look at the gen'd asm, you see that the actual call to memcpy will get elided in most cases where you're using it for type punning

Blotto Skorzany fucked around with this message at 14:22 on Jul 22, 2014

pseudorandom name
May 6, 2007

Bonfire Lit posted:

That's right. But that exception allows you to turn a sockaddr_in (or sockaddr_in6 or whatever) into a sockaddr to pass to bind(2), via a union. bind then can (legally) inspect the sa_family member to figure out what kind of object the sockaddr* it got actually is, and is allowed to cast to that back to its original type.
But I'm not a compiler writer so I might be interpreting the standard incorrectly.

There's a compiler extension called transparent unions that makes this work, standard C doesn't allow it.

Subjunctive
Sep 12, 2006

✨sparkle and shine✨

pseudorandom name posted:

There's a compiler extension called transparent unions that makes this work, standard C doesn't allow it.

Transparent unions don't even let that work, in the general case. They just let you do overloading at call time, really, and AFAICT it's only legal for the callee to access the same member that was originally set.

You still can't take a sockaddr, look at its family, and then cast to the right one safely. (You couldn't put the different flavours of sockaddr into a transparent union either, because they have to have the same size.)

Plorkyeran
Mar 22, 2007

To Escape The Shackles Of The Old Forums, We Must Reject The Tribal Negativity He Endorsed

Otto Skorzeny posted:

e: and when you look at the gen'd asm, you see that the actual call to memcpy will get elided in most cases where you're using it for type punning
Compilers are also reasonably good at eliding shift+masks which are equivalent to a pointer cast, so you should definitely be starting with doing things the clearly legal way and only trying to get away with pointer aliasing if you've actually verified that the compiler is failing to optimize things sufficiently.

Blotto Skorzany
Nov 7, 2008

He's a PSoC, loose and runnin'
came the whisper from each lip
And he's here to do some business with
the bad ADC on his chip
bad ADC on his chiiiiip
Incidentally, C99 eventually legalized union type punning, but I don't know whether compilers other than GCC will properly not warn about it when in C99 mode:

ISO/IEC 9899:1999 Technical Corrigendum 3 posted:

15. Page 073, 6.5.2.3
Attach a new footnote to the words "named member" in paragraph 3:
*) If the member used to access the contents of a union object is not the same as the member last used to store
a value in the object, the appropriate part of the object representation of the value is reinterpreted as an object representation in the new type as described in 6.2.6 (a process sometimes called "type punning"). This might
be a trap representation.

Cf. http://www.open-std.org/Jtc1/sc22/wg14/www/docs/n1235.pdf

rjmccall
Sep 7, 2007

no worries friend
Fun Shoe
Okay.

First, the cast itself is not undefined behavior. The aliasing rules apply to accesses (e.g. loads, stores, member accesses), and casts aren't accesses. You are explicitly allowed to cast pointers to arbitrary other pointer types and then cast back (as long as you don't cast a less-aligned pointer to a more-aligned type; that's undefined behavior, but not because of the aliasing rule).

Second, GCC is technically correct but probably on inadvisable ground when strictly applying the effective type rule to declared arrays of char, which is an extremely common idiom for embedding arbitrary object storage within a type. Note that C++'s object lifetime rules are okay with doing this as long as you construct the object properly in the storage. (Although C++'s rule is in no way limited to arrays of char.)

Third, the C committee isn't really sure how aliasing in unions is supposed to work. I, personally, would agree that the union exceptions should only apply when the l-value is clearly derived from a union member, but the last time I heard, there are committee members who favor a rule about whether there is any union visible in the translation unit. It's a mess.

Fourth, memcpys of small constant size are extremely likely to be turned into the obvious load/store sequence; I would not stress about it unless you are using a terrible custom embedded compiler (which probably does not implement type-based aliasing optimizations anyway).

Finally, clang explicitly permits "obvious" type-puns that formally violate the strict aliasing rule. If the compiler can see that two pointers obviously overlap, it will give that information precedence over strict-aliasing information, and so still recognize that the pointers alias. This is why clang doesn't bother implementing that warning. Last time I checked, GCC will give the strict-aliasing information precedence, so the warning is prudent for them.

PiCroft
Jun 11, 2010

I'm sorry, did I break all your shit? I didn't know it was yours

I'm looking for some help with some c++ code.

I'm brushing up on my pointer arithmetic and I was pointed to this site: http://www.interqiew.com/tests?type=cpp to get some good questions to test myself. I arrived at the following question however, and I have no idea whats going on:


code:
#include <cstddef>
#include <iostream>

class A
{
public:
    A() : m_x(0) { }

public:
    static ptrdiff_t member_offset(const A &a)
    {
        const char *p = reinterpret_cast<const char*>(&a);
        const char *q = reinterpret_cast<const char*>(&a.m_x);

        return q - p;
    }

private:
    int m_x;
};

class B
    : public A
{
public:
    B() : m_x('a') { }

public:
    static int m_n;

public:
    static ptrdiff_t member_offset(const B &b)
    {
        const char *p = reinterpret_cast<const char*>(&b);
        const char *q = reinterpret_cast<const char*>(&b.m_x);

        return q - p;
    }

private:
    char m_x;
};

int B::m_n = 1;

class C
{
public:
    C() : m_x(0) { }
    virtual ~C() { }

public:
    static ptrdiff_t member_offset(const C &c)
    {
        const char *p = reinterpret_cast<const char*>(&c);
        const char *q = reinterpret_cast<const char*>(&c.m_x);

        return q - p;
    }

private:
    int m_x;
};

int main()
{
    A a;
    B b;
    C c;
    std::cout << ((A::member_offset(a) == 0) ? 0 : 1);
    std::cout << ((B::member_offset(b) == 0) ? 0 : 2);
    std::cout << ((A::member_offset(b) == 0) ? 0 : 3);
    std::cout << ((C::member_offset(c) == 0) ? 0 : 4);
    std::cout << std::endl;

    return 0;
}
I can get access to the answer but thats not good enough, I'd like to know what is happening. For example, I can see that in Class A, its creating a couple of char* pointers, one to the A object thats passed in and one for the m_x variable it contains. I don't know how to interpret what is going on though. My theory is that it is trying to demonstrate where in memory an object member lies relative to where the object itself lies, so in the case of A, the pointer to the object itself and the pointer to the member m_x within it both point to the same space in memory and thus subtracting one from the other gives 0. This is my theory though, I'd appreciate it if someone could tell me if I'm right.

The output at the end is 0204 for the record,

Hiowf
Jun 28, 2013

We don't do .DOC in my cave.

PiCroft posted:

My theory is that it is trying to demonstrate where in memory an object member lies relative to where the object itself lies, so in the case of A, the pointer to the object itself and the pointer to the member m_x within it both point to the same space in memory and thus subtracting one from the other gives 0. This is my theory though, I'd appreciate it if someone could tell me if I'm right.

That's correct.

B demonstrates that replacing a var in a derived class hides it but the original var still exists, so the ptr is offset for the second var.

C has a virtual function, so there is a vtable in its instances and it sits before the vars.

PiCroft
Jun 11, 2010

I'm sorry, did I break all your shit? I didn't know it was yours

Skuto posted:

That's correct.

B demonstrates that replacing a var in a derived class hides it but the original var still exists, so the ptr is offset for the second var.

C has a virtual function, so there is a vtable in its instances and it sits before the vars.

So I take it that static int m_n in B doesn't actually take up memory for a B object? I read that a static member variable is a special case because being static makes it a class-level object and not strictly speaking a part of the instance-level object, so a static int in B wouldn't count for memory purposes.

Malcolm XML
Aug 8, 2009

I always knew it would end like this.

PiCroft posted:

I'm looking for some help with some c++ code.

I'm brushing up on my pointer arithmetic and I was pointed to this site: http://www.interqiew.com/tests?type=cpp to get some good questions to test myself. I arrived at the following question however, and I have no idea whats going on:


code:
#include <cstddef>
#include <iostream>

class A
{
public:
    A() : m_x(0) { }

public:
    static ptrdiff_t member_offset(const A &a)
    {
        const char *p = reinterpret_cast<const char*>(&a);
        const char *q = reinterpret_cast<const char*>(&a.m_x);

        return q - p;
    }

private:
    int m_x;
};

class B
    : public A
{
public:
    B() : m_x('a') { }

public:
    static int m_n;

public:
    static ptrdiff_t member_offset(const B &b)
    {
        const char *p = reinterpret_cast<const char*>(&b);
        const char *q = reinterpret_cast<const char*>(&b.m_x);

        return q - p;
    }

private:
    char m_x;
};

int B::m_n = 1;

class C
{
public:
    C() : m_x(0) { }
    virtual ~C() { }

public:
    static ptrdiff_t member_offset(const C &c)
    {
        const char *p = reinterpret_cast<const char*>(&c);
        const char *q = reinterpret_cast<const char*>(&c.m_x);

        return q - p;
    }

private:
    int m_x;
};

int main()
{
    A a;
    B b;
    C c;
    std::cout << ((A::member_offset(a) == 0) ? 0 : 1);
    std::cout << ((B::member_offset(b) == 0) ? 0 : 2);
    std::cout << ((A::member_offset(b) == 0) ? 0 : 3);
    std::cout << ((C::member_offset(c) == 0) ? 0 : 4);
    std::cout << std::endl;

    return 0;
}
I can get access to the answer but thats not good enough, I'd like to know what is happening. For example, I can see that in Class A, its creating a couple of char* pointers, one to the A object thats passed in and one for the m_x variable it contains. I don't know how to interpret what is going on though. My theory is that it is trying to demonstrate where in memory an object member lies relative to where the object itself lies, so in the case of A, the pointer to the object itself and the pointer to the member m_x within it both point to the same space in memory and thus subtracting one from the other gives 0. This is my theory though, I'd appreciate it if someone could tell me if I'm right.

The output at the end is 0204 for the record,

Man this is a giant-rear end way of showing off offsetof

Hiowf
Jun 28, 2013

We don't do .DOC in my cave.

PiCroft posted:

So I take it that static int m_n in B doesn't actually take up memory for a B object? I read that a static member variable is a special case because being static makes it a class-level object and not strictly speaking a part of the instance-level object, so a static int in B wouldn't count for memory purposes.

That's correct. The static is per-class, so instances don't need a separate copy.

Harik
Sep 9, 2001

From the hard streets of Moscow
First dog to touch the stars


Plaster Town Cop

Otto Skorzeny posted:

What uC are you working with where memcpy doesn't work? I do it all day long on Cortex-M and Freescale S08.

e: and when you look at the gen'd asm, you see that the actual call to memcpy will get elided in most cases where you're using it for type punning

You misunderstand - memcpy works just fine, but I've only got 128/256 bytes of RAM depending on the part, so I'm trying to use the data in-place in the receive buffer rather than using more RAM to make a copy.

As for your edit: How does the memcpy legally get elided? If I push a structure on the stack and memcpy to it, the compiler will just skip that whole bit and alias the structure to the buffer? That seems problematic.

Blotto Skorzany
Nov 7, 2008

He's a PSoC, loose and runnin'
came the whisper from each lip
And he's here to do some business with
the bad ADC on his chip
bad ADC on his chiiiiip

Harik posted:

You misunderstand - memcpy works just fine, but I've only got 128/256 bytes of RAM depending on the part, so I'm trying to use the data in-place in the receive buffer rather than using more RAM to make a copy.

As for your edit: How does the memcpy legally get elided? If I push a structure on the stack and memcpy to it, the compiler will just skip that whole bit and alias the structure to the buffer? That seems problematic.

Cf. http://blog.regehr.org/archives/959

quote:

code:
uint64_t c5 (const uint16_t *buf)
{
  uint64_t num;
  memcpy(&num, buf, 8);
  return num;
}
Again, although we might expect that adding a function call would make the code harder to optimize, both compilers understand memcpy deeply enough that we get the desired object code:

code:
c5:
        movq    (%rdi), %rax
        ret

And also what clang developer and forums user rjmccall said:

rjmccall posted:

Fourth, memcpys of small constant size are extremely likely to be turned into the obvious load/store sequence; I would not stress about it unless you are using a terrible custom embedded compiler (which probably does not implement type-based aliasing optimizations anyway).

Subjunctive
Sep 12, 2006

✨sparkle and shine✨

Oh, I thought that what he wanted was to return a view into an existing buffer. The relevant loads would then be in other functions, separated in time and compilation unit. (The stores have already happened off in interrupt-service land or whatever.)

Adbot
ADBOT LOVES YOU

Harik
Sep 9, 2001

From the hard streets of Moscow
First dog to touch the stars


Plaster Town Cop

rjmccall posted:

aliasing discussion

Thanks, that's a lot of info to go through.


Holy poo poo, that's great. Gcc 4.8 does that for atmel, I'll do it properly with memcpy and not worry about it anymore, thanks!

Edit: goddamn clang is smart enough to get the round-trip right:

C code:
void bar(char * buf) {
	struct foo f2;
	memcpy(&f2, buf, sizeof(f2));
	f2.a++;
	memcpy(buf, &f2, sizeof(f2));
}

Disassembly of section .text:

0000000000000000 <bar>:
   0:	48 ff 07             	incq   (%rdi)
   3:	c3                   	retq   
Gcc 4.8 doesn't though.

Harik fucked around with this message at 00:46 on Jul 23, 2014

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply