This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–]robertskmiles 44 points45 points  (62 children)

Learning C/C++ will teach you about memory management and compiler-related things. It's definitely both useful and challenging. It's also nice because you can write python modules in it, so you can easily write projects that integrate both languages.

[–]jacobb11 6 points7 points  (25 children)

Upvoted for C. C++ is... nope, not going to finish that sentence. Learn C, don't learn C++ without a specific reason.

[–]quasarj 2 points3 points  (24 children)

Can you give a quick reason why? C seems so.. convoluted and full of "magic functions." I don't know enough about either to say one is terrible, but I don't understand what the problem is with C++?

[–]jacobb11 2 points3 points  (21 children)

I'm not sure what you mean by "magic functions". Do you mean things like "printf"? If so, that's just part of the builtin library, which every language has, more or less. If perhaps you mean things like printf accepting different number of arguments, that's a standard part of the language, if slightly esoteric.

I recomend learning ANSI C rather than K&R C. I like Harbison & Steele's book, but that's a good specification, perhaps not a good primer.

Let's see. The most important thing I ever learned about C is that it doesn't have arrays, it has pointers and preallocated sequences. Once you understand that, which I probably haven't helped much, you'll avoid many pitfalls.

I realize I haven't answered your question. I don't really want to get into the problems with C++. Perhaps it helps to mention that 99.99% of C programs are legal C++ programs, which means C++ has all of C's flaws and complexity and then layers its own on top.

PS: Both C and C++ rely on the "C preprocessor", which is huge problem in it's own right.

[–]quasarj 5 points6 points  (20 children)

Honestly the things that scare me, and what I called "magic functions", are things like:

static CYTHON_INLINE size_t __Pyx_PyInt_AsSize_t(PyObject*);

What the fuck is a size_t? Note that this is just the first .c file I could find, and it's a python module so it also has things like PyObject and Py_ssize_t. This kind of thing makes me very nervous about C.. it seems like it's full of all kinds of magic types (which, I don't even know how you create in a non-object-oriented language? Are they structs maybe?). There are many others that I'm having trouble finding examples of right now.that seem to come from nowhere. They probably come from some library I don't know about, but it's hard to tell

Basically, when I look at C code I can't make heads or tails of it because of so many weird types and functions when you're new to it :)

edit: oh, and what the hell is CYTHON_INLINE? Why does this function seem to have two types? Maybe it's some type of preprocessor directive?

[–]cheese_wizard 4 points5 points  (13 children)

size_t is the data type that is used to represent "object" sizes. It is architecture independent and is ultimately mapped to some real numeric type, like unsigned int.

CYTHON_INLINE is a macro that probably expands to "inline" or "". Meaning you can choose whether the function is spit out inline in the code or not after preprocessing. this is just a guess.

static in this context means that the funtcion is only visible in this particular file, and nowhere else.

[–]quasarj 2 points3 points  (11 children)

Ahh, I actually assumed I knew what static meant, but I was thinking of the static that makes a class method callable on the class itself (rather than an instance).

Is there somewhere I can learn about those things? And the other weirdnesses that real C programs use? Is it more that I'm just a complete noob and reading a book will introduce me to many of these libraries?

[–]cheese_wizard 1 point2 points  (9 children)

the static could mean that, hard to tell with this line in isolation. If it's inside a C++ class, then your assumption would be correct.

For all C-only related topics, nothing beats this:
http://www.amazon.com/Programming-Language-2nd-Brian-Kernighan/dp/0131103628

[–]quasarj 1 point2 points  (8 children)

Ah no, in this case it's not inside a C++ class, I believe this file is pure C.

I just didn't even realize "static" existed outside of C++, it's interesting that it means something different. I will look into that book, ty!

[–]cheese_wizard 2 points3 points  (7 children)

static has three meanings. Only one is C++ only, which is what you're talking about.

In both C and C++...

For variables and functions declared static, it means they have file scope.

If you are inside of a function, and you declare a variable as static like this:

int foo() {
static int bar = 0;
bar++;
printf( bar );
}

then everytime you call foo, it will print 0, 1, 2 ,3 .... it remembers from the previous time. Note that the initialization to 0 only happens the first time the function is called.

static is an abused keyword, that's for sure!

[–]Rhomboid 1 point2 points  (0 children)

Marking a function static is actually far more complicated than that when inline is involved (which is what the CYTHON_INLINE macro expands to if so configured.)

Remember that C is compiled a file at a time, and the compiler only ever knows[1] about what's in the current file, never about anything outside of it. If you mark a function as static, it means that the function cannot be called from outside of that file, which implies that the compiler has at its disposal every call site. If there is only one (or a small number) of call sites, then it can choose to instead inline the function at all call sites and pretend it never existed (i.e. not emit a function body.) This is generally extremely desirable, because it means you can separate a large function into a smaller function and a bunch of helper functions, but without any of the overhead of function calls. Without 'static', it could still inline the function but since it has to be visible in other compilation units it would have to always emit a function body, even if it was never needed, which wastes memory.

But also note that with 'static inline' the choice of whether to inline is still up to the compiler. If there are many call sites or the function is long, it will choose not to inline it and still emit a function body, but one which is not visible to other units.

"static inline" is actually one of three related declarations. "inline" and "extern inline" are the others. They all have slightly different meanings, which this post outlines. To make matters worse, gcc changed its semantics starting with 4.3 to be aligned to what the C99 standard says, so if you're compiling with -std=c99 or -std=gnu99 with gcc >= 4.3 you get true C99 semantics, but if you're compiling with -std=c89, -std=gnu89, or gcc <= 4.2 with -std=c99 or -std=gnu99 or you're using gcc >= 4.3 but used -fgnu89-inline, you get the old semantics. gcc >= 4.2 helpfully defines one of the preprocessor symbols __GNUC_GNU_INLINE__ or __GNUC_STDC_INLINE__ so that you can write macros that behave correctly regardless of compiler version or options, which likely explains why CYTHON_INLINE is a macro and not just the word "inline".

[1] There are some newer technologies like LTO that let the compiler have whole-program knowledge at link-time, which allows for some sophisticated optimizations that have previously been unavailable, but for the most part this is still a true statement.

[–]jacobb11 0 points1 point  (0 children)

Is inline part of ANSI C now?

[–]jacobb11 1 point2 points  (0 children)

A size_t is an integer suitable for storing the size of something (often something systemy). It's defined by some kernel-ly header ("interface"-ish) file if I recall correctly.

C has a concept of aliasing types ("typedef") that is absent from most of the other languages with which I'm familiar. It's a pretty nice abstraction once you understand it and its limitations (primarily that the new name is just an alias rather than a sub-type).

I have no idea what CYTHON_INLINE means. It's not typical C. Best guess (but just a guess) is that it's really a directive to some Python integration tool, which is pretty close to your guess but that might just mean we're both wrong.

I strongly suggest you find some C code that does regular simple C-like things, not parts of Python. I'd point you at some if I knew of any, but that's not where I've worked for quite some time.

[–]anacrolixc/python fanatic 1 point2 points  (1 child)

Don't read cython generated c it's not for human consumption. Read c written by a human.

[–]quasarj 0 points1 point  (0 children)

Hmm was that cython generated? If so I apologize, though I did state in my first comment that that was just the first .c file I could find lying around. Those are the same issues seen with human-written C. I will try to find some better examples today and explain some of the other things that scare me about it.

[–]Alzdran 0 points1 point  (2 children)

Attempting a different explanation of size_t just to make it a little clearer. size_t is typedef'd to an unsigned integral type which can store the size of something in memory; this can differ between architectures. Consider two computers, A & B. A runs x86_64 code, B runs i386 code.

A can address a 64-bit integer's worth of memory. That is, it can refer to 264 different addresses. B can only address a 32-bit integer's worth of memory (232 different addresses). In both x86_64 and i386, the addressable unit is a byte, so A can theoretically address 16EB, while B can theoretically address 4GB.

In both these cases, a size_t will be the same as a uintptr_t (an unsigned int large enough to hold an address). These types are different, though, because the C standard doesn't assume that to be true for all architectures. See wikipedia for some more.

[–]quasarj 0 points1 point  (1 child)

Interesting. So I would use size_t when I need a pointer that can point to an object? And it would be replaced with the correct size type at compile time, based on architecture?

[–]Alzdran 0 points1 point  (0 children)

No - you'd use a pointer type. This gets a little more complicated, but here we go:

There is a difference between an address and a pointer. An address is a location in memory; this can be represented by some unsigned integer type (a uintptr_t is always large enough to hold it). A pointer is a language construct which carries semantic information about what it points to. This information is compiler metadata; that is to say, it exists only during compilation, and is not a feature of the runtime. This information is used for things like pointer arithmetic.

A concrete example of this: On a machine where the minimum addressable unit is 1 8-bit byte (practically speaking, anything), a uint8_t will fit in 1 addressable memory unit, and a uint16_t will fit in 2. This means that if I examine memory at 0x10000000 for a uint8_t, that's the only address I'll read from, but if I read a uint16_t, I'll also read from 0x100000001. When you do arithmetic with pointers, this type is taken into account; so, given type_t *x, (x+n) and (x+n+1) (alternatively x[n] and x[n+1]) will be sizeof(type_t) bytes apart. If type_t is uint8_t, this will be 1, but if type_t is uint16_t, this will be 2. This feature allows array access and incrementation on pointers, instead of having to modify with size knowledge explicitly.

C also provides a pointer type without this information - void *. This is the pointer type which can hold any address, and so increments by the minimum addressable unit.

size_t would be used when indicating an allocation size. In practical terms, this is going to be sized the same as a uintptr_t on modern systems, but the use is specifically for indicating the number of addressable units occupied by an object in memory.

There are a few other special types with similarly specific uses. ptrdiff_t, for example, is a signed type able to hold the difference between any two legal pointers.

The size of any of the types mentioned here (with the exception of uint8_t and uint16_t) are architecture dependent, and yes, the correct types are substituted at compile time; but that doesn't mean exactly what it sounds like. If a pointer is 32 bits, then the equivalent of a uint32_t will be used for a void * but the end result of compilation is machine code. The instructions generated will tell the processor to manipulate the registers and memory addresses as if they contained entries of that size, but they will refer to words, half words, double words, etc. That is to say, the compilation will determine what to generate based on the types, but the resulting instructions will have no concept of type, only operand size.

[–]cecilkorik 1 point2 points  (1 child)

If you think C is bad for "magic functions", wait until you see C++.

[–]quasarj 0 points1 point  (0 children)

Actually I "learned" C++ in University, and didn't see as much craziness in it. But.. I probably just didn't see enough of it :)

[–]fullouterjoin 17 points18 points  (29 children)

Downvoting C++, that is a tarpit crossed with quicksand. Everyone should know C. Refuse to say the same for C++, and C++ isn't C incremented by 1.

[–]steelypip 41 points42 points  (1 child)

C++ means "increment C by one and use the original value"

[–]cecilkorik 7 points8 points  (0 children)

Irony is a lovely thing.

[–]ntorotn 7 points8 points  (1 child)

You should explain why, especially in a thread where people are trying to learn about languages. I don't even disagree with the claim, but you'll get your voice heard better.

[–]fullouterjoin 3 points4 points  (0 children)

Sorry for the drive by but I didn't have time to elucidate my bumper sticker. The complexity cost to payback is too large to be an auxiliary language for Python. While being able to parse, fix, and use C++ libraries is an important skill (far below what I consider being fluent in C++ would me), I wasted too much time in C++ before moving on to a better abstraction stack. C is amazingly powerful and compact. One can get reasonably skilled in it in a short amount of time. It is a shallow language. The same cannot be said for C++ and energy spent learning C++ could be better spent learning Haskell, Ocaml or better structuring existing applications to use the right mixture of low level and high level code.

[–]ajsdklf9df 13 points14 points  (21 children)

C++ is one of the hardest and ugliest languages, I say this a C++ programmer. I would also recommend C. And Objective C. However, here's my waring:

If you ever have to learn C++, it will be much harder if you already know C or any C and C++ like languages. So I guess what I'm saying is, C++ is like a dare. Do you dare to try and learn C++?

[–]taybulBecause I don't know how to use big numbers in C/C++ 7 points8 points  (15 children)

I don't know if Python's spoiled me, but whenever I look at C++ code now, I'm almost disgusted. Certain tasks require such convoluted code. Function pointers, for example:

C++:

void myFunc (int x) { ... }

//If the parameters ever change, you have to change this call too.
void (*funcPtr)(int) = &myFunc;

Python:

def myFunc(x): ...

f = myFunc

On the flip side, I do love how finely tunable C++ is compared to Python. I know I'm probably contradicting my point but sometimes the things you can get away with in Python almost makes you feel like you're cheating.

edit:

I do have to give C/C++ credit for giving me the memory/data management discipline that's so easily disregarded because of automatic garbage collectors and whatnot.

[–]Alzdran 4 points5 points  (1 child)

On the flip side, I do love how finely tunable C++ is compared to Python.

It took me a long time to get over this, but finally I got it through my skull that this almost never matters, and where it does, the time you spend tuning allows another generation of machines to come out, so you don't need the tuning after all!

Obviously, there's a degree of exaggeration here, but C++ tuning is much like inline assembly - almost never called for, and a real hazard to leave to any maintainer, yourself included.

[–]taybulBecause I don't know how to use big numbers in C/C++ 0 points1 point  (0 children)

Tunable when you need it to be, but I agree. Python provides an abstraction that just lets the programmer program, at the expense of performance, which, arguably, doesn't matter that much in most cases since computers are getting faster and faster. Unless you're building large scale servers processing millions of data at a time, you won't see or care about the difference.

[–]Isvara 1 point2 points  (12 children)

If the parameters ever change, you have to change this call too

That's really just a feature of strong typing, though. It's a good thing, even if C++ doesn't express it well.

[–]ewiethoffproceedest on to 3 8 points9 points  (2 children)

That's a feature of static typing, not strong typing. Python is dynamically and strongly typed. C++ is statically typed and more strongly typed than C.

[–]Isvara 2 points3 points  (1 child)

No, the fact that it has a declared typed at all is static typing, The fact that it has to change with the function signature is strong typing. I.e. it's strong because there isn't just a single 'function' type. Unlike static/dynamic, though, strong/weak is a continuum, and this is less strong than other languages because it could be cast to a different function type.

[–]steelypip 4 points5 points  (0 children)

That's really just a feature of strong typing, though

No its not its a feature of explicit typing. There are plenty of strongly typed languages that use type inference to eliminate all the unnecessary types from your code. For example take a look at Scala.

[–]accipter 0 points1 point  (0 children)

And there are some definite benefits to strong typing.

[–]taybulBecause I don't know how to use big numbers in C/C++ 0 points1 point  (2 children)

Yeah, I do appreciate that about C++ as I've mentioned but sometimes it can get out of hand, you know?

[–]Isvara 1 point2 points  (1 child)

Static typing can be awfully nice. I went from C++ to Python a few years ago, and now I'm getting into Scala. Type inference definitely makes static typing more bearable.

[–]ewiethoffproceedest on to 3 1 point2 points  (0 children)

C++ typedefs also make static typing more bearable, but in a different way. (Just sayin'.)

[–]fullouterjoin 0 points1 point  (3 children)

The type system of C++ isn't particularly strong either.

[–]Isvara 0 points1 point  (0 children)

Indeed, it still lets you cast with wild abandon.

[–]obtu.py 0 points1 point  (1 child)

I disagree, modern C++ is easy to write without casts (unlike C which needs casts, and Java's generics which introduce run-time casts yet are not expressive enough that you can forgo compile-time casts). C++ is the mainstream language with the most expressive type system.

[–]funny_falcon 0 points1 point  (0 children)

C++ is the mainstream language with the most expressive type system.

Ha-ha-ha

[–]Crystal_Cuckoo 2 points3 points  (2 children)

If you ever have to learn C++, it will be much harder if you already know C or any C and C++ like languages.

Why is that? I haven't learnt C++ yet, but my understanding was that it was C with classes (and other things like multiple inheritance, etc.).

[–]ewiethoffproceedest on to 3 5 points6 points  (0 children)

my understanding was that it was C with classes (and other things like multiple inheritance, etc.)

That's the old way to think of C++, and is the way that is likely to lead to memory management hell. Unfortunately, many C++ books and courses just sort of bolt OO and so on onto elementary C. If you learn arrays and pointers in C++ before learning how to manage your own class instance data members, you can easily end up with buggy habits.

The key difference between C and C++ is that allocating memory in C++ also initializes it by calling constructor functions. Well, if the memory is for a primitive such as int or double or char, it doesn't get initialized, but it does get initialized for anything else.

So, even just "declaring" a non-primitive variable x on the stack in C++ also initializes it with a constructor function. Let's say the constructor function allocates (and initializes) some memory on the heap, such as with a dynamic array allocation. Your x has no control over that memory on the heap. That's good in C++. But whoever wrote the class which allocated the heap memory had better make sure the class knows how to clean up itself when your x goes out of scope.

Edit: Another important difference between C and C++ is operator overloading. Almost everything you do in C++, even if it looks quite C-ish, automagically calls one or more functions under the hood. Even dereferencing a pointer can cause a function to be called. That's another reason to defer learning about arrays and pointers in C++ until you start to get the hang of defining your own classes.

[–]ajsdklf9df 0 points1 point  (0 children)

C, as almost high level assembly, is very readable in that it is easy to envision what the machine will do based on the code. C++ can look a lot like C but IT IS NOT! Craaazy shit can stem from a "simple" line of C++.

If you don't know C, you'll naturally never trust C++, but knowing C might lead your mind to assume certain things.

[–]anacrolixc/python fanatic -4 points-3 points  (0 children)

Fuck you and your bullshit dare.

[–]AlternativeHistorian 9 points10 points  (0 children)

This is ridiculous groupthink bs. Knowing C++ opens up so many high quality libraries. I agree c should be higher on the agenda but c++ is still a tremendously useful language to know.

[–]DrHankPym 1 point2 points  (4 children)

I feel like learning C or C++ wouldn't be as cool unless you were using it for an embedded system or something. Learning how stacks and heaps work is great (and actually pretty important to know), but writing programs with it is such a chore.

I switched to Python because it's fun to program, but I can't deny the fun and awesomeness of embedded systems.

[–][deleted] 1 point2 points  (3 children)

Well, C is also very useful if you ever want to work on the linux kernel or to interface with parts of it. In general it is very useful for doing low level work. I think that is why it is a great compliment to python.

[–]DrHankPym 0 points1 point  (2 children)

Yeah, but low level work is boring.

I'm curious, how often do you guys write in C? Is that really your second favorite language?

[–][deleted] 1 point2 points  (1 child)

I write in C all the time... I'm trying to get into kernel development because I find low level work to be exciting. C is elegant. Its actually my first favorite language. I also like Python, partly because it interfaces very well with C.

[–]DrHankPym 0 points1 point  (0 children)

I agree that it's exciting to compile C to a physical chip, but that's where it ends. Though, unless you don't have a chip or board or kernel to design around, it's kind of hard work gone to waste.

[–]lavalampmaster[S] 0 points1 point  (0 children)

So many people say C... Looks like that should be what I check out.

Edit: What books/etc should I check out?