Hot answers tagged

47

No, it does not. In C, variables have a fixed set of memory addresses to work with. If you are working on a system with 4-byte ints, and you set an int variable to 2,147,483,647 and then add 1, the variable will usually contain -2147483648. (On most systems. The behavior is actually undefined.) No other memory locations will be modified. In essence, ...


40

Eventually, I thought you will become a better programmer knowing this because you'll know what's happening rather than assuming that everything is magic. These are not contradictory things. I have no idea how to pave a road, but I know that it is not magic. But a month ago, I came across this book called Structure and Interpretation of Computer ...


31

I know why abstraction is great, but doesn't that prevent you from learning how computers work? Certainly not. If you want to understand the abstractions at work, then study those abstractions. If you want to understand the low-level technical details of a real, physical, computer then study those details. If you want to understand both, study both. (In ...


24

Signed integer overflow is undefined behavior. If this happens your program is invalid. The compiler is not required to check this for you, so it may generate an executable that appears to do something reasonable, but there is no guarantee that it will. However, unsigned integer overflow is well-defined. It will wrap modulo UINT_MAX+1. The memory not ...


23

No, this is not bad practice, it is even encouraged to do so, although one could even use conventions like struct foo *foo_new(); and void foo_free(struct foo *foo); Of course, as a comment says, only do this where appropriate. There is no sense in using a constructor for an int. The prefix foo_ is a convention followed by a lot of libraries, because it ...


20

No, abstractions don't prevent you from understanding how things work. Abstractions allow you to understand why (to what end) things work the way they do. First off, let's make one thing clear: pretty much everything you've ever known is at a level of abstraction. Java is an abstraction, C++ is an abstraction, C is an abstraction, x86 is an abstraction, ...


16

A key skill in programming is simultaneously thinking at multiple levels of abstraction. Another key skill is building abstractions; this skill uses the previous one. Low-level programming is valuable in part because it exercises and expands both these skills. SICP models and implements interpreters, simulators for a machine model, and a compiler to that ...


14

So, there are two things here: the language level: what are the semantics of C the machine level: what are the semantics of the assembly/CPU you use At the language level: In C: overflow and underflow are defined as modulo arithmetic for unsigned integers, thus their value "loops" overflow and underflow are Undefined Behavior for signed integers, ...


12

No is the correct answer. However, seeing that both variables have been declared on stack right after each other, you might actually write p++ to maybe obtain the pointer to memory location where b is, if you are lucky. Note this is in no case safe and can never be relied upon, as it relies on undefined behaviour.


11

From http://www.jslint.com/chistory.html ("The Development of the C Language" by Dennis M. Ritchie): Many other changes occurred around 1972-3, but the most important was the introduction of the preprocessor, partly at the urging of Alan Snyder [Snyder 74], but also in recognition of the utility of the the file-inclusion mechanisms available in BCPL and ...


10

The compiler is only required to honor legal, valid constructs. Compilers have myriads of choices to make in code generation. Compilers have many, many ways of doing the same thing. If the difference between one choice or another can't be observed -- by the program, using only valid language constructs -- then it will generally opt for the more ...


10

What happens when you didn't mean to have a multi-line string, but instead forgot to close the quote? The parser will chew through the code until it hits another quote in a completely different part of the program, then proceed as normal. This will very likely lead to confusing, unrelated errors since the string is no longer the parse error. At worst, you ...


8

You hit on one of the only reasons this is useful: mapping external data structures. Those include memory-mapped video buffers, hardware registers, etc. They also include data transmitted intact outside the program, like SSL certificates, IP packets, JPEG images, and pretty much any other data structure that has a persistent life outside the program.


5

I know why abstraction is great, but doesn't that prevent you from learning how computers work? Am I missing something? Go to a magic show and you'll be entertained but you won't understand how the tricks work. Read a book on magic and you'll learn how tricks work but you still won't be entertaining. Do both. Work hard. And you might be both. ...


5

No one said unit tests have to be run all on the same platform - but no one said you could reach 100% test coverage either. As a first step, #ifdef out the code, preferably factoring it into a platform-specific function. Write a suitable implementation of this function for x86. However, I don't think it is appropriate to select the code to be compiled based ...


4

There's numerous ways to acheive this, all depending on what kind of environment you're working with. Another concern is of course the latency you can afford. If you are working on a modern operating system, you most likely have an API that will put your thread to sleep until the given time has arrived. Posix has nanosleep() and C11 has the thread_sleep() ...


4

First (assuming C99 standard), you may want to include <stdint.h> standard header and use some of the types defined there, notably int32_t which is exactly a 32 bits signed integer, or uint64_t which is exactly a 64 bits unsigned integer, and so on. You might want to use types like int_fast16_t for performance reasons. Read others answers explaining ...


4

To further @StevenBurnap's answer, the reason this happens is because of how computers work at machine-level. Your array is stored in memory (e.g. in RAM). When an arithmetic operation is performed, the value in memory is copied into the input registers of the circuit that performs the arithmetic (the ALU: Arithmetic Logic Unit), the operation is then ...


4

There are several benefits, the obvious one is at compile time to ensure that things like function parameters match the values being passed in. But I think you are asking about what is happening at runtime. Keep in mind that the compiler will create a runtime that embeds knowledge of the data types in the operations it performs. Each chunk of data in ...


4

C is a low-level language, nearly a portable assembler, so its data structures and language constructs are close to the metal (data structures have no hidden costs - except padding, alignment and size constraints imposed by hardware and ABI). So C indeed does not have dynamic typing natively. But if you need it, you could adopt a convention that all your ...


4

When programming in C I have found it invaluable to pack structs using GCCs __attribute__((__packed__)) [...] Since you mention __attribute__((__packed__)), I assume your intention is to eliminate all padding within a struct (make each member have a 1-byte alignment). Is there no standard for packing structs that works in all C compilers? ... ...


3

Software engineering has multiple levels of detail. Your question is "what is the most rewarding, worthy, interesting level?" It depends on your task or on what you want to be, what you care about. For big systems you should not care much about bit shifting and clock cycles. For embedded software running on a simple micro controller you will probably want ...


3

The abstractions we teach in computer science are the things which, historically, have been found most beneficial to most people writing most programs. You can't write a modern program in assembly, just due to the sheer size of modern programs and the time constraints business will place on a developer. You have to be ready to accomplish your goals without ...


3

What time is it? Is it time to become a know-it-all programmer or is it time to become a productive programmer? Knowing the abstraction layers that exist below those among which you work is a good thing, it grants you a better understanding behind the structure of your work and it will even allow creating better solutions. Yet, you do that, you study when ...


3

Unit tests are supposed to be for external functions only. Nevertheless, if you have a lot of "internal code", it should be tested too right? So how? Make smaller functions, separated from you code (another .c and .h), and use those functions as a library that can be unit tested. Then you should be able to test those functions that are used by the internal ...


3

The answer depends on the context of the question. If the context of the question is whether there is any way to do this without respect to the compiler, in a completely standard way without relying on undefined behavior, that works everywhere, then the answer is a clear and unambiguous "no". However with that said... In some contexts, it may be very ...


3

FWIW, Ocaml accepts a limited form of multi-line string literal : String literals are delimited by " (double quote) characters. The two double quotes enclose a sequence of either characters different from " and \, or escape sequences from the table given above for character literals. To allow splitting long string literals across lines, the sequence ...


2

What you have here is a Tail Call, and even more, it is Tail Recursion (Direct Tail Recursion) to be precise. Many languages have Proper Tail Calls (e.g. Scheme, ECMAScript), and even more languages have Proper Tail Recursion (e.g. Scala, at least for Direct Tail Recursion). Unfortunately, neither C nor Java support Proper Tail Calls or even the much weaker ...


2

As someone who uses this idiom in some contexts — although I generally put \n at the end to ensure flushing —, I can offer a justification: I tend to use this when formatting many lines, especially if starting with an empty line, so that the \ns are aligned. This means that it is somewhat easier to (a) check that all lines do indeed include a \n or (b) to ...


2

It's not bad, it's excellent. Object Oriented Programming is a good thing (unless you get carried away, you can have too much of a good thing). C is not the most suitable language for OOP, but that shouldn't stop you getting the best out of it.



Only top voted, non community-wiki answers of a minimum length are eligible