The Big Programming Thread - Page 643
Forum Index > General Forum |
Thread Rules 1. This is not a "do my homework for me" thread. If you have specific questions, ask, but don't post an assignment or homework problem and expect an exact solution. 2. No recruiting for your cockamamie projects (you won't replace facebook with 3 dudes you found on the internet and $20) 3. If you can't articulate why a language is bad, don't start slinging shit about it. Just remember that nothing is worse than making CSS IE6 compatible. 4. Use [code] tags to format code blocks. | ||
Nesserev
Belgium2760 Posts
| ||
Ropid
Germany3557 Posts
I'm trying to think about what this will do to make things better, but I'm failing, can't get to a clear opinion. ![]() | ||
Manit0u
Poland17189 Posts
On June 20 2015 20:23 Nesserev wrote: It's probably a good idea to bundle the array and its size in a simple struct, because when the array is returned, you only get the pointer to the array back, but you don't know what the size of said array is. But I get the impression (sorry if I'm wrong, the internet :S) that you're thinking of addings some sort of destructor to that struct... but in C, structs can only be used to organize data, and can't have any 'class methods'. So, structs won't solve any problems on that front. I seem to vaguely recall that in the past I used to create some functions to release the memory... Yeah. Found it. Not sure if it's the working version though, but I can definitely use something like that:
| ||
Itsmedudeman
United States19229 Posts
| ||
Nesserev
Belgium2760 Posts
| ||
Itsmedudeman
United States19229 Posts
The code itself might not have been compiled and may have just been an idea he was throwing around looking back on his post. | ||
Nesserev
Belgium2760 Posts
| ||
Acrofales
Spain17848 Posts
On June 20 2015 07:35 Manit0u wrote: With the structure I wasn't asking about QNX specifically. I'm just toying around with ANSI C in my leisure time. I was asking about QNX because a friend of mine asked me for help and I simply can't refuse stuff that involves learning some new code. The task at hand was relatively simple but I've found QNX's online documentation to be pretty lacking (as in, requiring you to have really good knowledge of the system to understand it, at which point you probably don't need it). I also hat it when they always seem to do for( ; ; ) instead of while(true) or something. But I digress... The problem he had to solve was the password/pin thingie. Where you give the user a message, wait 5 seconds for input and reset ad infinitum. The task specifically asked to use the alarm() function. While making a simple loop and doing alarm(5) inside of it is easy, actually handling the alarm is not. From what I read in the docs, when the time for the alarm elapses, it sends the SIGALRM which kills the current process, thus exiting the loop and all. I've read that you should handle this signal with sigaction() or somesuch but I couldn't really find a way to make sigaction() resume/restart the loop. Could you shed some light on the matter? Is it a bad thing? Edit: Also, the code purist in my heart tells me to refactor the code, remove fill_range_reverse and simply pass another argument to the fill range function (a boolean) that controls incrementation/decrementation inside of the loop to remove code duplication. + Show Spoiler [like so] +
Question: why use a boolean flag, rather than directly give the incrementation step. Sure, this could lead to code like passing a step size of 2, which may result in unexpected behaviour down the line (but I don't really see why) and it would make your check_increment (which I feel is ugly) method redundant like so:
| ||
![]()
tofucake
Hyrule18975 Posts
return fill_range(x, size, range, (x > y ? 1 : -1)); just sayin | ||
Nesserev
Belgium2760 Posts
| ||
spinesheath
Germany8679 Posts
On June 23 2015 05:18 Nesserev wrote: Personally, I kinda dislike the conditionat operator, and prefer a simple 'if ... else ...' structure in most cases. One of the problems is that code using the conditional operator looks very cluttered, very fast. My personal rules for using it are: - don't embed it in other code (separate statement on its own) - the statement has to be very short and simple, so that your brain can immediately interpret what's going on. Absolutely agree with that. If nothing else, separating the statement at least allows you to give it a reasonable name. | ||
Khalum
Austria831 Posts
For example I always use brackets for if/else statements when it's part of the "intended" flow but I keep early exit checks as short as possible, like if (..) throw std::exception(..);all in one line. I like to do it that way because I feel like it creates a better "balance" when you look at the code. First get all the necessary checks out of the way quickly and then do the actual work. When I look at code that is very excessive when it comes to just doing simple things I feel like it distracts from the actually relevant stuff. It's kinda hard to explain for me right now, it's like a gut feeling - it's inaesthetic to me... the rhytm is somehow off... you get my point. On the other hand it's of course not just hard to read at times but can also be a nightmare when debugging things when you put too much stuff into one statement. So the "main functionality" of any function would be written in a more verbose way. What I found myself doing a lot is stuff like typedef std::vector<SomeType> SomeTypeVector;which enables me to write more explicit code afterwards without unnecessary clutter. It's just important to pick representative names - but that's true for everyting you give a name. And then there's templates. Incredibly useful yet incredibly shitty to read at a certain complexity level.. [edit] I actually wanted to respond to the return statement criticism. The if/else would - in my opinion - not be an improvement. What I'd do is: int step = x>y ? 1 : -1; | ||
Manit0u
Poland17189 Posts
| ||
RoyGBiv_13
United States1275 Posts
There are many different places where data exist, and every time you use data, it has to be stored somewhere in memory. This guide will provide a set of rules for how to ensure your C program never leaks memory nor loses valid data. ====== To begin, lets define how data is passed in the C language: Passing by Reference: All data exist at some address where it can be accessed, when calling a function, passing the address where data is stored rather than the data itself is known as Passing by Reference. Passing by Value: When calling a function, passing the data itself will make a copy of that data, and put it on the Stack. For example,
======== Let's also define all the regular places where data can be stored in memory in a C program: Stack: Every process of every program contains a section of memory in RAM called the stack. At the beginning of every function call, a new "Stack Frame" is pushed (added) onto the top of the stack. This Stack Frame contains the address the program is supposed to branch to upon returning from that function call, along with any variables declared within that function. A Stack Frame is popped (removed) when returning from a function, and all the variables that had data stored within that Stack Frame are no longer valid. Since the stack is used in every function call, running out of space in the stack is very bad and you will segfault (or worse). Heap: programs have a section of memory in RAM where you can allocate and free memory programatically. Unlike the stack, where data has a lifespan dependent on the function it is declared in, the heap will keep the data until explicitly told it no longer needs to. This region of memory is typically very large, and the Operating System will usually continue to expand this if your program needs even more space. Linker Sections: when building your program, global variables and structures will be placed into memory when your program loads. Data located in global variables and structures will be placed in one of several linker sections depending on whether it is initialized or declared as constant. Uninitialized data will be placed in the ".bss" section to be cleared quickly when loading the program. Initialized, non-constant data will be placed in the ".data" section, and constant data will be placed in the ".rodata" section. The ".rodata" section is special as it may not be present in RAM, but rather read-only memory. ======== One of the very important parts of writing Fast C code is to reduce the number of times where you have to copy the same data if it's being accessed by the same program. In addition, each time you copy memory, you have one more piece of data to ensure is freed at the right time. Thus, it's important to place your data in the right spot where it will always be valid when you need it and pass the data around via reference rather than value to avoid copying larger buffers. This can be done by statically allocating when possible, and putting small variables on the stack. If a char, short, int, long, float, double, string, buffer or struct does not change its value during a program, make it a global const. If a char, short, int, long, float, double, statically sized string, statically sized buffer or struct is accessed regularly throughout the program during various functions declare it as a global outside any function (or static to reduce it's scope to just that file or function). A char, short, int, long, float, or double should be declared on the stack and can be passed by value or reference. If you don't know which function to declare it in, start at main() and work your way down to the first level which uses the value, then declare it there. A dynamically sized buffer or string can be declared on the stack, if it is small (my rule of thumb is under 100 bytes or 1 line in a file), or placed on the heap if it is large. It should always be passed by reference. As for data structures (like lists, sets, variable length arrays), C++ containers tend to store copies of data added to them on the heap. When destroying the container, it has recorded all the memory it used, and frees it. I like that model of dynamic memory management. ====== FAQ: Q: You say "buffer", do you mean array? A: Both words refer to the same thing, a contiguous block of memory, but are generally used to reflect how the underlying data are used. In this case, since I'm referring to size it takes up within RAM, I use "buffer". Q: What If i'm writing a new function to modify existing data? A: There are two ways to modify data, either modify it in place, or make a copy, modify that copy, and return it. When possible, you should modify data in place, and allow the calling function decide whether it wants to copy the data or not. Q: I have a function that needs to return a large buffer, should I put it on the heap and return it? A: Instead, pass a reference to the function that already has space allocated. The calling function is then able to decide where to put the buffer. If this convention is used throughout the program, it is very easy to ensure you never leak memory. Q: Is there anything wrong with declaring every variable in the program as a global? A: In practice, no. This just means your program is simple enough to allocate all memory it needs during its build, and does not need to programatically allocate anything. Teachers might get upset if you get in this practice, though, since you aren't learning anything about memory management. It could also bloat the size of your program if you put huge amounts of initialized data in your .data or .rodata section. Q: What about smart pointers, like auto_ptr in C++? A: C has a few implementations of smart pointers itself, and you are welcome to use them or create your own. If you are having trouble ensuring your heap memory is properly free'd, try thinking of the heap as an extension of the stack. Put all of your malloc() calls at the start of a function and free() at the end of the same function. Now you know the scope of that memory. Q: What happens if you mistakenly use a reference to no longer valid data? A: If you reference old data in the heap, this is called a "use after free" bug, and a great many tools exist to find these. Potentially, other calls to malloc could reallocate that space and you would be using whatever data was there. Similarly, referencing stack memory that has been popped will likely reference another stack frame's data, which may look valid but be incorrect. These can be some of the hardest bugs to find or debug. Q: What is a "Stack Overflow" or "Buffer Overflow" A: When a buffer is allocated on the stack, it reserves a fixed size determined by how large the buffer is. If you don't check your bounds before writing to or reading from that buffer, it's possible to mistakenly write past the end of the buffer. If you write far enough, it's possible to cross neighboring data in the same data structure, such as the next stack frame. This ia called a "Buffer Overflow". Since a stack frame includes an executable address to return to, a malicious Stack Overflow could overwrite that value, and cause the program to execute code that they located anywhere on the system. A "Stack Overflow" happens when the stack's size becomes too large for the space allocated for it. This can happen often during runaway recursion or if you allocate an enormous buffer to the stack. If you are running as a virtual process, many operating systems intentionally leave a bit of memory past the stack as unmapped. This causes programs that go past the end of the stack to segfault or throw a page fault instead of overwriting another processes. | ||
Nesserev
Belgium2760 Posts
| ||
spinesheath
Germany8679 Posts
On June 23 2015 09:53 RoyGBiv_13 wrote: Passing by Reference: All data exist at some address where it can be accessed, when calling a function, passing the address where data is stored rather than the data itself is known as Passing by Reference. "Passing by Reference" in quotes. It actually is Pass by Value, where the value is the address. As far as I'm aware there is no actual Pass by Reference in C. It's similar in practice, but technically not the same. | ||
sabas123
Netherlands3122 Posts
On June 23 2015 09:53 RoyGBiv_13 wrote: A dynamically sized buffer or string can be declared on the stack, if it is small (my rule of thumb is under 100 bytes or 1 line in a file), or placed on the heap if it is large. It should always be passed by reference. You mean if the buffer is big you want to pass it by reference right? Nice guide btw, I enjoyed it alot. | ||
Blisse
Canada3710 Posts
I have multiple lists of possibly > 10K integers, how do I efficiently perform computations on multiple lists at the same time? The lists get paged out a lot so it's really slow on large sets. | ||
Nesserev
Belgium2760 Posts
| ||
Acrofales
Spain17848 Posts
Other than that, we need more details. There is no magic solution (other than using C for that shit). | ||
| ||