Programming in C - questions

You can talk about almost anything that you want to on this board.

Moderator: Moderators

User avatar
Zepper
Formerly Fx3
Posts: 3262
Joined: Fri Nov 12, 2004 4:59 pm
Location: Brazil
Contact:

Programming in C - questions

Post by Zepper »

1. The structure.member or structure->pointer?

Code: Select all

typedef struct {
 int value1, value2; 
} STRU_T;

//this
static STRU_T mystru;

//OR...

static STRU_T tmp;
static STRU_T *mystru = &tmp;
2. memcpy() or memset() VS "manual" set to zero.

Code: Select all

memset(buffer,0,8);

//OR...

unsigned char *buf = buffer;
*buf = 0; buf++; //1
*buf = 0; buf++; //2
*buf = 0; buf++; //3
*buf = 0; buf++; //4
*buf = 0; buf++; //5
*buf = 0; buf++; //6
*buf = 0; buf++; //7
*buf = 0; //8
3. Buffer I/O

Code: Select all

unsigned char *buffer = malloc(0x100);
...
buffer[index] = data;

//OR...

*(buffer+index) = data;
lidnariq
Posts: 11432
Joined: Sun Apr 13, 2008 11:12 am

Re: Programming in C - questions

Post by lidnariq »

In terms of maintainability, never unroll your loops manually. Modern compilers and modern CPUs will know better than you if it's beneficial. (Memset, in particular, is often already a specially optimized function. You're not going to be able to write something faster, especially not in C, and probably not even in asm)

For numbers 1 and 3, "it depends".
User avatar
DRW
Posts: 2225
Joined: Sat Sep 07, 2013 2:59 pm

Re: Programming in C - questions

Post by DRW »

Zepper wrote:

Code: Select all

//this
static STRU_T mystru;

//OR...

static STRU_T tmp;
static STRU_T *mystru = &tmp;
Definitely the first one. Why would you want to use indirect access when you can access the variable directly? Direct access is smaller and faster (at least for global variables. Local variables are internally accessed via stack pointer anyway, but even here there's no reason to add yet another pointer.)

Pointers only make sense here if the pointer is the parameter variable of a function:

Code: Select all

void SomeFunction(MyStruct *pStruct)
Zepper wrote:2. memcpy() or memset() VS "manual" set to zero.
memset if you want to set all bytes to the same value.

Take care if your array is of type int (or any other non-byte type) and you want to initialize with anything non-zero: The value that you set refers to every single byte, not every array item:

Code: Select all

int array[5];
memset(array, 1, 5 * sizeof(int));
This will not set all five values to 1. It will set all separate bytes to 1.
So, if the int consists of two bytes, then each array item will now have the value 257 (0x0101).

memcpy if you want to copy data from another array.
(Using memcpy to initialize with 0 or any other single byte value makes no sense. Where do you take the source data from? Do you have some constant array with all zeroes in it that's only there to initialize other arrays with 0 by using it with memcpy?)

And for everything else (for example if you really have an int array that you want to initialize with a certain value per entry and not per byte), you should use a for loop and not initialize every entry individually:

Code: Select all

int i; /* Use unsigned char instead of int if you program for the NES
          and your array is less than 255 entries. */
for (i = 0; i < 5; ++i)
    array[i] = 1;
But even if you do initialize each item individually (because each item gets another value that cannot be calculated somehow), the best method is

Code: Select all

array[0] = 25;
array[1] = 7;
array[2] = 130;
etc. and not

Code: Select all

unsigned char *pointer = array;
*pointer = 25;
++pointer;
*pointer = 7;
++pointer;
*pointer = 130;
++pointer;
An additional suggestion (at least for NES programming. It isn't necessary in PC programs): If your arrays are never larger than 255 bytes, write your own CopyData and FillData function in Assembly where the size parameter is of type byte instead of size_t. Makes the function smaller and faster than the official memcpy and memset functions.
Zepper wrote:

Code: Select all

buffer[index] = data;

//OR...

*(buffer+index) = data;
The compiler shouldn't make a difference here, but the typical style in C is to access array entires with the brackets, not by adding the index to the pointer address.
At first glance, the latter part looks like you're doing some hacky pointer tricks while the brackets immediately show that you simply want to access an array item in the regular way.
Last edited by DRW on Sat Apr 14, 2018 3:38 pm, edited 1 time in total.
My game "City Trouble":
Gameplay video: https://youtu.be/Eee0yurkIW4
Download (ROM, manual, artworks): http://www.denny-r-walter.de/city.html
User avatar
dougeff
Posts: 3079
Joined: Fri May 08, 2015 7:17 pm

Re: Programming in C - questions

Post by dougeff »

Are you programming an NES game in C, or something else?
nesdoug.com -- blog/tutorial on programming for the NES
User avatar
Dwedit
Posts: 4924
Joined: Fri Nov 19, 2004 7:35 pm
Contact:

Re: Programming in C - questions

Post by Dwedit »

Devkitarm has shipped with suboptimal memset and memcpy before, so I had to rewrite them in asm.
Here come the fortune cookies! Here come the fortune cookies! They're wearing paper hats!
User avatar
rainwarrior
Posts: 8732
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: Programming in C - questions

Post by rainwarrior »

Zepper wrote:2. memcpy() or memset() VS "manual" set to zero.
In most modern compilers memset() is an intrinsic rather than an actual library function. Where the sizes of data are known, it will optimize to whatever equivalent is small or fast, appropriately, rather than use a function call.

In general you'll find that with optimizing compilers it makes little difference how you initialize when the initializers are relatively simple, so you should just do the one that feels best for you to write, because the only differences are usually cosmetic.

However, if the performance of an initializer is critical for your application there is no general advice anyone can give for how to write it the "best" way. Every compiler has different foibles. You will have to check the assembly output yourself. If it's not critical, don't worry because it's going to do a very optimal thing most of the time.

If you're asking for advice regarding a specific compiler (e.g. cc65), then there could be a more definitive answer to this.


Also, any incomplete initializer for an array or struct will be padded out with zeroes, so this also conveniently initializes buffer to 0:

Code: Select all

buffer[5] = {0};
Last edited by rainwarrior on Tue Apr 17, 2018 8:49 am, edited 2 times in total.
User avatar
koitsu
Posts: 4201
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Re: Programming in C - questions

Post by koitsu »

My responses are with regards to general use on x64/x86 architecture today, with gcc or clang. If this is for NES or some other architecture, I have no experience with cc65's compiler.

1. It depends. Neither is better or worse than the other. If you don't need the additional indirect addressing (2nd example), then don't use it. If you do plan on changing mystru to point to a different allocated STRU_T somewhere, then the 2nd example is a better choice. The 2nd example is also necessary if you need to dynamically allocate structures using malloc/calloc and then referencing them (example: a linked list of structs).

2. You didn't declare buffer. I'm going to assume you either were using char buffer[8]; or char *buffer; buffer = malloc(8);.

In either case: memset is recommended. For small-ish buffers, however, allocating them on the heap is fine. For large-ish, always allocate dynamically, and use calloc() if you know definitively the contents need to be zeroed.

If you're allocating it on the heap though, you could just do char buffer[8] = {0}; to have it pre-allocated to zero by the linker/loader.

3. It depends, but going entirely off the example you gave, I would prefer the former. This question distantly reminds me of char *argv[] vs. char **argv; I always prefer the former.

If you're using clang/LLVM, and you're really concerned about performance (cycle counts) or size (number of instructions), then I strongly suggest using -S -mllvm (and you might be interested in --x86-asm-syntax=intel if you prefer Intel syntax; I do) then looking at the resulting .s files. You might be very surprised the difference things like -O vs. -O2 vs. -O3 vs. -Os makes.

The reason I mention this: last month I saw the FreeBSD project commit this. My reaction: "why are they using that temporary variable t? They can do this without a temporary variable: just use the XOR swap method!" So I replaced their version with mine: p[0] ^= p[1]; p[1] ^= p[0]; p[0] ^= p[1];. The result was slower and longer than using a temporary variable. -O2 greatly helped but it was still longer. My point is that compilers sometimes generate code that a human writing assembly wouldn't. I generally trust compilers to make optimal decisions these days, but there are always cases where humans do better.
Hangin10
Posts: 37
Joined: Thu Jun 04, 2009 9:07 am

Re: Programming in C - questions

Post by Hangin10 »

Some compilers (GCC in particular) will even optimize some loops into calls to memcpy/move/set functions.
User avatar
Drew Sebastino
Formerly Espozo
Posts: 3496
Joined: Mon Sep 15, 2014 4:35 pm
Location: Richmond, Virginia

Re: Programming in C - questions

Post by Drew Sebastino »

I hope I'm not hijacking this thread too much, but I didn't think it was worth starting a new thread over. Anyway, does anyone know of any good recourses for learning C from Java? (As in, something that highlights the differences, because they seem to share a lot.) I want to write my own tools, but I'm not at all enthusiastic about Java, and I assume C would be better. All the resources I've seen for C online assume you have zero programming knowledge, and are just gigantic.
User avatar
koitsu
Posts: 4201
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Re: Programming in C - questions

Post by koitsu »

Espozo wrote:I hope I'm not hijacking this thread too much, but I didn't think it was worth starting a new thread over. Anyway, does anyone know of any good recourses for learning C from Java? (As in, something that highlights the differences, because they seem to share a lot.) I want to write my own tools, but I'm not at all enthusiastic about Java, and I assume C would be better. All the resources I've seen for C online assume you have zero programming knowledge, and are just gigantic.
I don't know of any resources for people who know Java that want to learn C, but I do know of a top-notch resource for C in general: C Programming: A Modern Approach by K. N. King. Don't let anyone tell you the K&R book (also see Wikipedia) is a great starter book -- it isn't. I'm not going to derail the thread, nor am I going to get into a discussion about PL advocacy, but for general tooling you might also consider Go (it sort of makes me think of a "C-like" language that results in a single self-contained binary -- no libraries/frameworks/runtimes/other crap to install).
Oziphantom
Posts: 1565
Joined: Tue Feb 07, 2017 2:03 am

Re: Programming in C - questions

Post by Oziphantom »

Going from Java to C. Forget everything java has taught you, its all bad and will lead you to ruin. Start from scratch and a learn the proper-way of doing things with C.

C++ is a bit different, and has some concepts shared with Java(objects, methods, static, public, etc ) , but you need to learn pointers, pointer arithmetic, how to delete things, when you delete things, and when you use a pointer,a reference or actually just have the object/data type. So basically forget everything Java taught you and go back to C from scratch ;) there is a lot of fundamentals that Java just avoids that are critical to a C/C++ program.
User avatar
Sumez
Posts: 919
Joined: Thu Sep 15, 2016 6:29 am
Location: Denmark (PAL)

Re: Programming in C - questions

Post by Sumez »

Yeah, Java's strength is in its strongly typed syntax and object oriented structure. C++ lets you do what you want, requires you to manage your own memory usage, and has OOP awkwardly tacked on (though the last part is somewhat subjective I guess).

I wouldn't say you can't use what you've learned from Java, though. It does force some sensible patterns that a lot of C++ programmers tend to forget/willingly ignore.
User avatar
gauauu
Posts: 779
Joined: Sat Jan 09, 2016 9:21 pm
Location: Central Illinois, USA
Contact:

Re: Programming in C - questions

Post by gauauu »

koitsu wrote:If this is for NES or some other architecture, I have no experience with cc65's compiler.
The structure.member or structure->pointer?
If I'm understanding question correctly (which I'm not sure I am) -- for 6502 with cc65, structure.member will give a lot better performance than pointer->member. You might likely get

Code: Select all

lda _structure+3 ;3 being the number of bytes offset to "member"
which is just 4 clock cycles.

Doing it with pointers will first copy the address of the pointer into a temporary location in zero page, to do an indirect indexed lookup, like:

Code: Select all

lda _structurePtr
sta $10 ;or some other temporary place in zero page
lda _structurePtr+1
sta $11
ldy #3 ;3 being the offset
lda ($10),y
Which is like 21 clock cycles -- more than 3 times slower. This really falls apart if you do it in a loop where it has to repeatedly copy the pointer(s) to zero page over and over again.

(Edit: realized the first version was wrong, fixed)
Last edited by gauauu on Tue Apr 17, 2018 7:19 am, edited 1 time in total.
User avatar
Zepper
Formerly Fx3
Posts: 3262
Joined: Fri Nov 12, 2004 4:59 pm
Location: Brazil
Contact:

Re: Programming in C - questions

Post by Zepper »

The C compiler is GCC.
User avatar
koitsu
Posts: 4201
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Re: Programming in C - questions

Post by koitsu »

You can use gcc -S -masm=intel to review the generated assembly (.s files) in Intel format. Rest of my advice about optimisation flags etc. from and earlier post still applies. Don't be surprised if what you find scares you.
Post Reply