Jump to content
43oh

Newbie general C code question


Recommended Posts

What is the proper way of using counting variables in loops. Lets say I have 5 loops in a sketch and they all use a counting variable "i". Should this be a unique variable for all 5 loops say i1, i2, i3... or is it ok to use the same variable in all loops and just reset (i=0) it before the loop?

 

Thanks

 

Skickat fr

Link to post
Share on other sites

if you declare i within the loop it will be local to that loop only. Outside of that loop i will never exist. Nested loops may require a unique variable name, but entirely unrelated loops can all be called i without issue.

 

for (int i = 0; i < 5; i++) {

    for (int i1 = 0; i < 5; i1++) {

        delay(i);

    }

}

for (int i = 0; i < 5; i++) {

    delay(i1);   // <----------------it would crash at this point because i1 technically does not exist anymore. however I have not had to manually reset i, that is explicit in the loop declaration.

}

Link to post
Share on other sites

if you declare i within the loop it will be local to that loop only. Outside of that loop i will never exist. Nested loops may require a unique variable name, but entirely unrelated loops can all be called i without issue.

 

for (int i = 0; i < 5; i++) {

    for (int i1 = 0; i < 5; i1++) {

        delay(i);

    }

}

for (int i = 0; i < 5; i++) {

    delay(i1);   // <----------------it would crash at this point because i1 technically does not exist anymore. however I have not had to manually reset i, that is explicit in the loop declaration.

}

Odd, I'd expect that statement you commented to not compile, since the compiler wouldn't know what i1 was (as you note, it's not in scope).

 

Declaring a variable within a for statement as in this code is a C99 or C++ feature, and the scope of the variable is the for statement including its body. With C98 or earlier it's a syntax error.

 

To the point of the original question: If you declare a variable in the enclosing scope (e.g., at the start of the function) you certainly may reuse it for multiple loops within that scope.

Link to post
Share on other sites

A real programmer uses gotos and pointers, not indexing variables and for loops.

 

:laugh:

 

Joking aside, TI's ULP parser will indeed throw warnings if you have an incrementing indexer rather than a decrementing indexer.  This is because comparison with zero uses fewer instructions than does comparison with a constant.  It is just something to think about if you are interested in learning about the ISA.

Link to post
Share on other sites

May I suggest using uint8_t instead of int?

 

As a matter of fact, the definition of int differs from one MCU to another. It could be a 16-bit or a 32-bit integer.

 

int{8|16|32|64}_t is much cleaner than unsigned char, char, unsigned int, int unsigned word, word, unsigned long, long, unsigned long long, long long and alike. It also ensures you have the exact same number of bits whatever the MCU.

Link to post
Share on other sites

I use unsigned int for any non-negative number, mostly counters where the range is not that important. I use (signed) int only when the value is supposed to be negative in some circumstances. For any situation where the range is important or where data is to be transported over a wire I use uint16_t by preference, or any of the other stdint fixed size types.

Also I tend to use the appropriate type when possible: the difference between two pointers is a ptrdiff_t, the size of a struct is a size_t, the element of a string is a char, etc.

Link to post
Share on other sites

May I suggest using uint8_t instead of int?

I see that happening more and more; I blame it on injudicious application of MISRA rule 6.3.

 

It's absolutely the case that types with specific sizes should be used when the range is potentially beyond the native word size of the processor or data is exchanged over a communication link, including via a file.

 

I have yet to see a cogent argument why specific-size types should be used in preference to "int" and "unsigned int" for indexes and offsets where the native word size is appropriate, or when you're manipulating a processor peripheral that is known to be that native word size.

 

On one hand, you might select a type that's too large for a target processor, impacting portability. TI did this recently to the CC3000 host interface, changing it so every parameter is a uint32_t even if its value range is less than one octet. This unnecessarily bloats code and hampers performance on the MSP430. Sure, when it goes out over SPI it needs to be 4 octets: but that's an encoding issue and should not propagate up to the library API.

 

Or you might go too small, and select uint8_t. Two problems: (1) it's a pain if you wrote code to index over an array, and somebody increases the array size above 255. (2) A uint8_t value will promote to the signed type int when used in expression calculations. I've got some discussion of this on my blog; the non-C++ stuff related to uint8_t is at the end. tl;dr: -x doesn't mean what you probably think it does.

 

The advice @@roadrunner84 gave is better, though if the "appropriate type" argument is applied consistently one should use size_t for all variables used as indexes as well as sizes. Which will hurt you badly on the MSP430 if you're using a memory model that supports objects larger than 64 kiBy, so I prefer unsigned int there too.

 

Note: If you are going to use the size-specific types, get in the habit of explicitly including either <inttypes.h> or its lesser cousin <stdint.h>. Yes, for MSP430 and ARM the vendor headers provide it for you, but if you don't remember that it comes from the C library, not the C language, you'll be confused when you graduate to host platforms where it's not part of the null context.

Link to post
Share on other sites

I see that happening more and more; I blame it on injudicious application of MISRA rule 6.3.

 

Thank you for the reference. Actually, I didn't know about the MISRA rules. 

 

The use of int{8|16|32|64}_t instead of int was prompted by need, when I went from 8-bit Arduino to 32-bit PIC32. 

 

Programs that used to work no longer work. The culprit was the int variable that had different definitions on different platforms. Maybe it was the poor implementation of int by the respective compilers for each of the mentioned platforms

Link to post
Share on other sites

The use of int{8|16|32|64}_t instead of int was prompted by need, when I went from 8-bit Arduino to 32-bit PIC32. 

 

Programs that used to work no longer work. The culprit was the int variable that had different definitions on different platforms. Maybe it was the poor implementation of int by the respective compilers for each of the mentioned platforms

Link to post
Share on other sites

OK, fine, though my recommendation of the JSF-AV document was intended as general advice rather than in the context of this specific issue. I'm still not gonna use or recommend using a size-qualified type for local variables used solely for indexing arbitrary objects. (I would endorse use of size_t, except that it's a pain when using msp430-elf-gcc.)

 

FWIW, the claim in the JSF-AV appendix related to whether char is signed or unsigned is not good advice; char is signed for every GCC target I know, and probably for LLVM as well. Best to only use unqualified char for text (which JSF-AV may recommend somewhere else), and explicitly convert it to an integral type if treating it as an integer.

Link to post
Share on other sites

May I suggest using uint8_t instead of int?

When the max value is less than 2^8 (256): uint_fast8_t

When the max value is less than 2^16 (65536): uint_fast16_t

When the max value is less than 2^32 (4,294,967,296): uint_fast32_t

When the max value is less than 2^64 (aprox 1.8E19): uint_fast64_t

 

These fast variants will be promoted to the best suitable integer type. That will typically be an integer multiple of the register width. So on the MSP430, uint_fast8_t becomes unsigned [16 bit]. On x86 it would be 16, 32 or 64 bits depending on target operating mode. This doesn't matter much on the MSP430, but it does on some others such as 16 bit PIC.

Link to post
Share on other sites

These fast variants will be promoted to the best suitable integer type. That will typically be an integer multiple of the register width. So on the MSP430, uint_fast8_t becomes unsigned [16 bit]. On x86 it would be 16, 32 or 64 bits depending on target operating mode. This doesn't matter much on the MSP430, but it does on some others such as 16 bit PIC.

This is a very good point, one I usually forget because where I use size-specific types I'm almost always focused on enforcing and communicating to the maintainer the representation range and the number of octets used in storage. If you wish to express clearly in the code your intent that a code sequence manipulates only a specific value range, and you care about speed but do not care about data size, then use these versions.

 

I suspect the only reasonable use of these types is in declaring local variables for calculations on size-constrained values, e.g. when scaling (and I should start using them for that). I can see no reason they would ever be appropriate in a data structure.

Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...