Re: Getting started with AVR and C

Discussion in 'C Programming' started by Ivan Shmakov, Nov 24, 2012.

  1. Ivan Shmakov

    Ivan Shmakov Guest

    >>>>> Robert Roland <> writes:

    [Cross-posting to news:comp.lang.c, in the hope that someone
    could provide more suggestions, or correct me on C.]

    [...]

    > 2. Where do I start learning C? Is there a good online tutorial
    > somewhere? I'd also be willing to buy a book. Is there one that
    > stands out as the best?


    Frankly, I don't quite understand how did I learn C myself.
    FWIW, there were hardly any good book on that that I've read.

    Two aspects of C are probably to be highlighted:

    * first of all, unlike some other, and higher-level, languages
    (like Pascal, BASIC, etc.), C has a very concise set of
    syntactic constructs; most of the power lies in libraries, and
    should you end up using AVR Libc, be sure to check its
    reference manual [1] (it isn't as good as the GNU C Library
    manual I'm using for the most time I need information on C,
    but it's still useful);

    * the C constructs tend to be translated into assembly in a
    rather straightforward manner (unless advanced optimization is
    involved, that is); consider, e. g.:

    int8_t i; /* not necessarily translated; may force
    the compiler to "allocate" a register
    (say, r7), or a memory cell */
    i = 3; /* ldi r7, 3 */
    i += 2; /* adi r7, 2 */
    i++; /* inc r7 */
    if (i < 9) { /* cpi r7, 9 ; brge else_a */
    int8_t j = 5; /* ldi r8, 5 */
    while (j >= 3) { /* while_a:
    cpi r8, 3
    brlt end_while_a */
    PORTB ^= 1; /* in r9, PORTB
    eoi r9, 1
    out PORTB, r9 */
    } /* jmp while_a
    end_while_a: */
    } /* else_a: */

    Not to undermine its value, but as could be seen (I hope) from
    this example, for the most part, C only manages registers and
    memory (including function calling conventions) while the rest
    of its /syntax/ is comparable to that of a library of assembly
    language macros of some sort.

    A cheat sheet for most of the C operators would probably be
    something like the following (where a, b, c, ... are either
    numeric literals, variable identifiers, or expressions.) Note
    that whenever all the operands are integer, an integer operation
    is performed. (So, 7 / 3 is 2.) The result is as wide as the
    widest of the operands. (So, i + j is 0 if i is 255, j is 1,
    and both are declared to be of the 8-bit unsigned integer
    uint8_t type.)

    Operation Value Side-effect

    Operations free of side-effects

    + a a
    - a - a
    * a the value of the memory cell at address
    a
    & a the address of a (must be an "l-value")
    ~ a bitwise negated a
    ! a 1 if a is 0,
    0 otherwise
    a, b b
    NB: a is evaluated first, its result discarded.
    a + b a + b
    a - b a - b
    a * b a b
    a / b a / b (quotient of)
    a % b remainder of a / b
    a & b a (bitwise and) b
    a | b a (bitwise or) b
    a ^ b a (bitwise exclusive or) b
    a << b a times 2 ^b (shift left)
    a >> b a times 2 ^(-b) (shift right)
    a < b 1 if a is less than b,
    0 otherwise
    a > b 1 if a is greater than b,
    0 otherwise
    a == b 1 if a is equal to b,
    0 otherwise
    a <= b 1 if a is less than or equal to b,
    0 otherwise
    a >= b 1 if a is greater than or equal to b,
    0 otherwise

    Conditional operations

    a && b a is evaluated;
    if a is non-zero, the value is b;
    otherwise, the value is 0, while b is
    not evaluated at all
    a || b a is evaluated;
    if a is zero, the value is b;
    otherwise, the value is a, while b is
    not evaluated at all
    a ? b : c a is evaluated first;
    if a is non-zero, the value is b;
    otherwise, the value is c;
    the other ("unused") expression is not
    evaluated

    Operations with side-effects

    NB: a must be an "l-value"
    a++ a a set to a + 1
    a-- a a set to a - 1
    ++a a + 1 a set to value
    --a a - 1 a set to value
    a = b b a set to value
    a += b a + b a set to value
    a -= b a - b a set to value
    a *= b a b a set to value
    a /= b a / b a set to value
    a &= b a (bitwise and) b a set to value
    a |= b a (bitwise or) b a set to value
    a ^= b a (bitwise xor) b a set to value
    a <<= b a times 2 ^b a set to value
    a >>= b a times 2 ^(-b) a set to value

    Naturally, both "=" and "," can be "nested", thus:

    for (a = b = 0, c = 5; c > 0; a++, b++, c--) {
    /* ... */
    }

    To note is that the for () <statement> form is just a short-hand
    for a specific while () loop. For instance, the for ()
    statement above can be rewritten as follows:

    a = b = 0, c = 5;
    while (c > 0) {
    /* ... */
    a++, b++, c--;
    }

    Thus, the only convenience of for () is that it allows for the
    "at-the-end-of-the-loop" part to be written above the loop body
    itself (i. e., together with the loop condition.)

    One more thing to note is that there're two basic contexts: the
    statement context, and the expression context. The switch from
    the former to the latter usually takes place in obvious places,
    while it isn't possible (in standard C; AFAIK) to switch from
    the latter to the former. E. g.:

    /* statement context */
    while (a < 5 /* expression context */) {
    /* statement context */
    b = 4 /* expression context */ ;
    /* NB: cannot switch back to the statement context, like: */
    /* c = while (b > 0) { /* ... */ } ; */
    }

    As one may need a conditional operator in either context, C has
    both the ?:-operator (see above), and (perhaps a more
    conventional) if ():

    if (a) {
    /* the code here will be executed iff a is non-zero */
    } else {
    /* the code here will be executed otherwise */
    }

    The { }-grouping is only necessary if more than one statement is
    needed as the body; otherwise, it may be elided, like:

    if (a) b = c;

    This allows for convenient nesting, like:

    if (a) {
    /* ... */
    } else if (b) {
    /* ... */
    } else {
    /* ... */
    }

    A similar idiom is possible for the ?:-operator just as well.
    Consider, e. g.:

    a = (b ? c
    : d ? e
    : f);

    which is not dissimilar to more verbose (and error-prone):

    if (b) {
    a = c;
    } else if (d) {
    a = e;
    } else {
    a = f;
    }

    An example program for an AVR could be as follows.

    #include <avr/io.h> /* for PORTB, DDRB, etc. */
    #include <util/delay.h> /* for _delay_ms () */

    /* global variable declarations; not necessary in this example */

    static void
    blink_led (void)
    {
    PORTB ^= (1 << PB0); /* toggle PB0 */
    _delay_ms (500); /* wait for 0.5 s */
    PORTB ^= (1 << PB0); /* toggle PB0 again */
    _delay_ms (500); /* wait for 0.5 s more */
    }

    /* the main () function is the conventional program's entry point */
    int
    main ()
    {
    /* set up PB0 for output, all the other tri-stated */
    DDRB = (DDRB0 << 1);

    /* enter infinite loop */
    while (1) {
    /* call our function */
    blink_led ();
    }

    /* never reached */
    return 0;
    }

    For sure, there's over than a dozen of individual syntactic
    constructs more (and then there's a handful or so of the
    preprocessor #-directives, too), but I hope that with the above,
    reading the sources would become a bit easier task.

    [1] http://www.nongnu.org/avr-libc/user-manual/

    --
    FSF associate member #7257
     
    Ivan Shmakov, Nov 24, 2012
    #1
    1. Advertising

  2. On 11/24/12 4:18 PM, Ivan Shmakov wrote:
    > for (a = b = 0, c = 5; c > 0; a++, b++, c--) {
    > /* ... */
    > }
    >
    > To note is that the for () <statement> form is just a short-hand
    > for a specific while () loop. For instance, the for ()
    > statement above can be rewritten as follows:
    >
    > a = b = 0, c = 5;
    > while (c > 0) {
    > /* ... */
    > a++, b++, c--;
    > }


    This is not completely correct in general. For instance if you replace
    the /* ... */ with continue; then the for loop jumps from the continue
    statement to the increment clause of the loop, but the while loop jumps
    to the beginning of the loop body (causing an infinite loop).
     
    Richard Damon, Nov 24, 2012
    #2
    1. Advertising

  3. Ivan Shmakov <> writes:

    >>>>>> Robert Roland <> writes:

    >
    > [Cross-posting to news:comp.lang.c, in the hope that someone
    > could provide more suggestions, or correct me on C.]


    I'll have a look...

    <snip>
    > [...] Note
    > that whenever all the operands are integer, an integer operation
    > is performed. (So, 7 / 3 is 2.) The result is as wide as the
    > widest of the operands. (So, i + j is 0 if i is 255, j is 1,
    > and both are declared to be of the 8-bit unsigned integer
    > uint8_t type.)


    No the return will be of type int in that case. The rules are rather
    involved, but the gist of it is that everything "smaller" than an int
    gets converted to an int. When the types involved are int or larger,
    both get converted to the larger type. Mixed signed and unsigned types
    generally result in the signed operand being converted to the type of
    the unsigned operand (after promotion).

    The standard takes pages to describe these rules, so there is no way I
    can summarise them here with 100% accuracy. Given that you summary is
    not short, it might be worth including them. You'd then need to say
    to which operators they apply (for example they don't apply to the
    shift operators).

    > Operation Value Side-effect
    >
    > Operations free of side-effects
    >
    > + a a
    > - a - a
    > * a the value of the memory cell at address
    > a


    This rather reinforces a view of C are lower-level than it really is.
    The result might not square with how people think of a memory cell (for
    example, if a is a function pointer, or when it is pointer to a struct
    type).

    > & a the address of a (must be an "l-value")
    > ~ a bitwise negated a
    > ! a 1 if a is 0,
    > 0 otherwise


    You don't talk about array the indexing operator, [], not the function
    call operator, (). there are others, too, like sizeof and cast
    operators. In tabular summary, I don't think it hurts to be complete.

    > a, b b
    > NB: a is evaluated first, its result discarded.
    > a + b a + b
    > a - b a - b
    > a * b a b
    > a / b a / b (quotient of)
    > a % b remainder of a / b
    > a & b a (bitwise and) b
    > a | b a (bitwise or) b
    > a ^ b a (bitwise exclusive or) b
    > a << b a times 2 ^b (shift left)
    > a >> b a times 2 ^(-b) (shift right)


    These ones are tricky because of all the corner cases (a shift equal or
    greater than the operand size, a shift of a bit into the sign position,
    a right shift of a negative quantity). In a summary like this may just
    a footnote to "beware".

    > a < b 1 if a is less than b,
    > 0 otherwise
    > a > b 1 if a is greater than b,
    > 0 otherwise
    > a == b 1 if a is equal to b,
    > 0 otherwise
    > a <= b 1 if a is less than or equal to b,
    > 0 otherwise
    > a >= b 1 if a is greater than or equal to b,
    > 0 otherwise
    >
    > Conditional operations
    >
    > a && b a is evaluated;
    > if a is non-zero, the value is b;
    > otherwise, the value is 0, while b is
    > not evaluated at all
    > a || b a is evaluated;
    > if a is zero, the value is b;
    > otherwise, the value is a, while b is
    > not evaluated at all
    > a ? b : c a is evaluated first;
    > if a is non-zero, the value is b;
    > otherwise, the value is c;
    > the other ("unused") expression is not
    > evaluated
    >
    > Operations with side-effects
    >
    > NB: a must be an "l-value"


    Yes and it might help to say which of the expression yield and lvalue.
    For example, you can write ++*a but not ++!a. It's might well be
    obvious, but you could have a column for "is an lvalue".

    > a++ a a set to a + 1
    > a-- a a set to a - 1
    > ++a a + 1 a set to value
    > --a a - 1 a set to value
    > a = b b a set to value
    > a += b a + b a set to value
    > a -= b a - b a set to value
    > a *= b a b a set to value
    > a /= b a / b a set to value


    a %=b is missing. But maybe it's better to generalise: a op= b and say what
    op can be?

    > a &= b a (bitwise and) b a set to value
    > a |= b a (bitwise or) b a set to value
    > a ^= b a (bitwise xor) b a set to value
    > a <<= b a times 2 ^b a set to value
    > a >>= b a times 2 ^(-b) a set to value
    >
    > Naturally, both "=" and "," can be "nested", thus:


    In most tables of operators, you see both priority and associativity.
    Assign ment does not yield an lvalue, so a = b = 0 only works because =
    associates to the right a = (b = 0). Most C binary operators associate
    to the left (i.e. a - b - c means (a - b) - c).

    You could make a really rich summary table that shows priority,
    associativity, whether the expression denotes an lvalue and what happens
    to the operands (are they just promoted as for the shift operands or are
    the "usual arithmetic conversions" applied as for + and *). I can see
    why you would want to avoid too much detail in a simple explanation like
    this, but it does seem like a useful thing to do.

    > for (a = b = 0, c = 5; c > 0; a++, b++, c--) {
    > /* ... */
    > }
    >
    > To note is that the for () <statement> form is just a short-hand
    > for a specific while () loop. For instance, the for ()
    > statement above can be rewritten as follows:
    >
    > a = b = 0, c = 5;
    > while (c > 0) {
    > /* ... */
    > a++, b++, c--;
    > }


    Provided that /* ... */ contains no continue statements (except as part
    of a nested statement of course).

    > Thus, the only convenience of for () is that it allows for the
    > "at-the-end-of-the-loop" part to be written above the loop body
    > itself (i. e., together with the loop condition.)
    >
    > One more thing to note is that there're two basic contexts: the
    > statement context, and the expression context. The switch from
    > the former to the latter usually takes place in obvious places,
    > while it isn't possible (in standard C; AFAIK) to switch from
    > the latter to the former. E. g.:
    >
    > /* statement context */
    > while (a < 5 /* expression context */) {
    > /* statement context */
    > b = 4 /* expression context */ ;
    > /* NB: cannot switch back to the statement context, like: */
    > /* c = while (b > 0) { /* ... */ } ; */
    > }


    A sad omission for fans of BCPL!

    > As one may need a conditional operator in either context, C has
    > both the ?:-operator (see above), and (perhaps a more
    > conventional) if ():
    >
    > if (a) {
    > /* the code here will be executed iff a is non-zero */
    > } else {
    > /* the code here will be executed otherwise */
    > }
    >
    > The { }-grouping is only necessary if more than one statement is
    > needed as the body; otherwise, it may be elided, like:
    >
    > if (a) b = c;
    >
    > This allows for convenient nesting, like:
    >
    > if (a) {
    > /* ... */
    > } else if (b) {
    > /* ... */
    > } else {
    > /* ... */
    > }
    >
    > A similar idiom is possible for the ?:-operator just as well.
    > Consider, e. g.:
    >
    > a = (b ? c
    > : d ? e
    > : f);
    >
    > which is not dissimilar to more verbose (and error-prone):


    You will find disagreement about that parenthetical remark in comp.lang.c.

    > if (b) {
    > a = c;
    > } else if (d) {
    > a = e;
    > } else {
    > a = f;
    > }


    <snip example>
    --
    Ben.
     
    Ben Bacarisse, Nov 24, 2012
    #3
  4. On Nov 24, 9:18 pm, Ivan Shmakov <> wrote:
    > >>>>> Robert Roland <> writes:

    >
    >         [Cross-posting to news:comp.lang.c, in the hope that someone
    >         could provide more suggestions, or correct me on C.]
    >
    > [...]
    >
    >  > 2. Where do I start learning C?  Is there a good online tutorial
    >  > somewhere?  I'd also be willing to buy a book.  Is there one that
    >  > stands out as the best?
    >
    >         Frankly, I don't quite understand how did I learn C myself.
    >         FWIW, there were hardly any good book on that that I've read.


    K&R?
     
    Nick Keighley, Nov 25, 2012
    #4
  5. Ivan Shmakov

    Rosario1903 Guest

    On Sun, 25 Nov 2012 04:18:32 +0700, Ivan Shmakov <>
    wrote:


    > int8_t i; /* not necessarily translated; may force
    > the compiler to "allocate" a register
    > (say, r7), or a memory cell */
    > i = 3; /* ldi r7, 3 */
    > i += 2; /* adi r7, 2 */
    > i++; /* inc r7 */
    > if (i < 9) { /* cpi r7, 9 ; brge else_a */
    > int8_t j = 5; /* ldi r8, 5 */
    > while (j >= 3) { /* while_a:
    > cpi r8, 3
    > brlt end_while_a */
    > PORTB ^= 1; /* in r9, PORTB
    > eoi r9, 1
    > out PORTB, r9 */
    > } /* jmp while_a
    > end_while_a: */
    > } /* else_a: */


    for me

    > i = 3; /* ldi r7, 3 */
    > i += 2; /* adi r7, 2 */
    > i++; /* inc r7 */


    are the same
    one call "r7", "i" and are the same

    for the remain i possibly prefer my macroized
    part of the right side

    than in the right side one can controll better the stack...

    >[1] http://www.nongnu.org/avr-libc/user-manual/
     
    Rosario1903, Nov 27, 2012
    #5
  6. Ivan Shmakov

    Ivan Shmakov Guest

    statement / expression contexts in C

    >>>>> Dave Nadler <> writes:
    >>>>> On Saturday, November 24, 2012 5:12:09 PM UTC-5, Ben Bacarisse wrote:


    [Cross-posting to news:comp.lang.c, and dropping
    news:comp.arch.embedded from Followup-To:.]

    [...]

    >>> /* statement context */
    >>> while (a < 5 /* expression context */) {
    >>> /* statement context */
    >>> b = 4 /* expression context */ ;
    >>> /* NB: cannot switch back to the statement context, like: */
    >>> /* c = while (b > 0) { /* ... */ } ; */
    >>> }


    >> A sad omission for fans of BCPL!


    > Wow ! I last programmed in BCPL in late 1977. And I still miss this
    > construct !


    Well, it's available as a GCC extension [1] at the least.
    Consider, e. g.:

    $ cat < g4u68ss8m8usbkqkx33wtfwws8.c
    /*** g4u68ss8m8usbkqkx33wtfwws8.c -*- C -*- */

    #include <stdio.h> /* for printf () */

    int
    main ()
    {
    int a = ({
    int x = 21, y;
    if (1) {
    y = 2 * x;
    } else {
    y = 13;
    }
    /* . */
    y;
    });

    printf ("a = %d\n", a);

    /* . */
    return 0;
    }

    /*** g4u68ss8m8usbkqkx33wtfwws8.c ends here */
    $ make g4u68ss8m8usbkqkx33wtfwws8
    cc g4u68ss8m8usbkqkx33wtfwws8.c -o g4u68ss8m8usbkqkx33wtfwws8
    $ ./g4u68ss8m8usbkqkx33wtfwws8
    a = 42
    $

    [1] http://gcc.gnu.org/onlinedocs/gcc-4.7.2/gcc/Statement-Exprs.html

    > Though I don't miss the Lvalue/Rvalue persnickity business (though
    > BCPL wasn't as silly as BLISS).


    > Thanks for the memories,


    --
    FSF associate member #7257
     
    Ivan Shmakov, Nov 28, 2012
    #6
  7. Ivan Shmakov

    Ivan Shmakov Guest

    >>>>> Ben Bacarisse <> writes:
    >>>>> Ivan Shmakov <> writes:


    [...]

    >> Note that whenever all the operands are integer, an integer
    >> operation is performed. (So, 7 / 3 is 2.) The result is as wide as
    >> the widest of the operands. (So, i + j is 0 if i is 255, j is 1,
    >> and both are declared to be of the 8-bit unsigned integer uint8_t
    >> type.)


    > No, the return will be of type int in that case. The rules are
    > rather involved, but the gist of it is that everything "smaller" than
    > an int gets converted to an int. When the types involved are int or
    > larger, both get converted to the larger type.


    Thus:

    Generally, the result is as wide as the widest of the operands, or
    "int", if no operand is wider than "int". The result then may be
    truncated on function application or assignment. (For instance,
    i += j is 0 if i is 1, j is 255, and both are declared to be of the
    8-bit unsigned integer uint8_t type.)

    > Mixed signed and unsigned types generally result in the signed
    > operand being converted to the type of the unsigned operand (after
    > promotion).


    > The standard takes pages to describe these rules, so there is no way
    > I can summarise them here with 100% accuracy. Given that you summary
    > is not short, it might be worth including them. You'd then need to
    > say to which operators they apply (for example they don't apply to
    > the shift operators).


    Perhaps. Though this seems to open yet another can of worms.

    [...]

    >> * a the value of the memory cell at address
    >> a


    > This rather reinforces a view of C are lower-level than it really is.
    > The result might not square with how people think of a memory cell
    > (for example, if a is a function pointer, or when it is pointer to a
    > struct type).


    Yes, but nowhere in the summary I talk about struct's (even
    should they be regarded as one of the essential conveniences of
    the language), and function pointers seem far too advanced a
    concept for those just starting to use C. And yes, pointers in
    C are typed, which made me hesitate to mention them at all in
    this summary.

    >> & a the address of a (must be an "l-value")
    >> ~ a bitwise negated a
    >> ! a 1 if a is 0,
    >> 0 otherwise


    > You don't talk about array the indexing operator, [], not the
    > function call operator, (). there are others, too, like sizeof and
    > cast operators.


    ... And also . and ->. But then, I've omitted both arrays and
    structs altogether, and show function calls and variable
    declarations only on examples.

    > In tabular summary, I don't think it hurts to be complete.


    Frankly, I've tried to focus on "consistency", not completeness.
    That is, this summary was intended to provide an example of
    "basic", "self-contained" C programming, even if unsuitable for
    the majority of practical tasks.

    [...]

    >> a << b a times 2 ^b (shift left)
    >> a >> b a times 2 ^(-b) (shift right)


    > These ones are tricky because of all the corner cases (a shift equal
    > or greater than the operand size, a shift of a bit into the sign
    > position,


    Shouldn't these issues be already familiar to those coming from
    the embedded programming background?

    > a right shift of a negative quantity).


    Seems like an interesting case, indeed. (I fail to recall if I
    ever needed to do that.)

    > In a summary like this may just a footnote to "beware".


    ACK, thanks.

    [...]

    >> Operations with side-effects


    >> NB: a must be an "l-value"


    > Yes and it might help to say which of the expression yield and
    > lvalue. For example, you can write ++*a but not ++!a. It's might
    > well be obvious, but you could have a column for "is an lvalue".


    Indeed, thanks.

    [...]

    >> a += b a + b a set to value
    >> a -= b a - b a set to value
    >> a *= b a b a set to value
    >> a /= b a / b a set to value


    > a %= b is missing.


    Indeed. Seems like quite a rarely used operator, though.

    > But maybe it's better to generalise: a op= b and say what op can be?


    It may have its merits, but as long as simple text search is
    considered, it makes sense to mention the exact form of all the
    operators. (Even if they share the description.)

    [...]

    >> Naturally, both "=" and "," can be "nested", thus:


    > In most tables of operators, you see both priority and associativity.
    > Assign ment does not yield an lvalue, so a = b = 0 only works because
    > = associates to the right a = (b = 0). Most C binary operators
    > associate to the left (i. e. a - b - c means (a - b) - c).


    > You could make a really rich summary table that shows priority,
    > associativity, whether the expression denotes an lvalue and what
    > happens to the operands (are they just promoted as for the shift
    > operands or are the "usual arithmetic conversions" applied as for +
    > and *). I can see why you would want to avoid too much detail in a
    > simple explanation like this, but it does seem like a useful thing to
    > do.


    Well, it seems like there already is such a table at [1].

    My point is that, more often than not, one doesn't bother about
    precedence: the arithmetics follows the usual rules (a + b * c =
    a + (b * c)), and when the other operators are involved, it does
    no harm to parenthesize the subexpressions to make the order
    explicit.

    [1] http://en.wikibooks.org/wiki/C_Programming/Reference_Tables#Table_of_Operators

    >> for (a = b = 0, c = 5; c > 0; a++, b++, c--) {
    >> /* ... */
    >> }


    >> To note is that the for () <statement> form is just a short-hand for
    >> a specific while () loop. For instance, the for () statement above
    >> can be rewritten as follows:


    >> a = b = 0, c = 5;
    >> while (c > 0) {
    >> /* ... */
    >> a++, b++, c--;
    >> }


    > Provided that /* ... */ contains no continue statements (except as
    > part of a nested statement of course).


    Indeed, I've missed this case. Thanks!

    [...]

    >> A similar idiom is possible for the ?:-operator just as well.
    >> Consider, e. g.:


    >> a = (b ? c
    >> : d ? e
    >> : f);


    >> which is not dissimilar to more verbose (and error-prone):


    >> if (b) {
    >> a = c;
    >> } else if (d) {
    >> a = e;
    >> } else {
    >> a = f;
    >> }


    > You will find disagreement about that parenthetical remark in
    > comp.lang.c.


    My point here is that when one (for whatever reason) has to
    rename "a", it's easier to forget to update all the "if"
    branches in the former example than the single reference in the
    latter.

    The same logic dictates the preference for a += b; over more
    "Fortran-friendly" a = a + b;.

    --
    FSF associate member #7257
     
    Ivan Shmakov, Nov 28, 2012
    #7
  8. Ivan Shmakov <> writes:

    >>>>>> Ben Bacarisse <> writes:
    >>>>>> Ivan Shmakov <> writes:

    <snip>
    > >> a << b a times 2 ^b (shift left)
    > >> a >> b a times 2 ^(-b) (shift right)

    >
    > > These ones are tricky because of all the corner cases (a shift equal
    > > or greater than the operand size, a shift of a bit into the sign
    > > position,

    >
    > Shouldn't these issues be already familiar to those coming from
    > the embedded programming background?


    The trouble is that people often think that what happened on the CPUs
    they've used before is what will happen on the next one. In other words
    there's a tendency to extrapolate form "what happens" to "what is
    defined to happen". But maybe the world of embedded programming is so
    diverse that people rarely make these assumptions.

    <snip>
    > > You could make a really rich summary table that shows priority,
    > > associativity, whether the expression denotes an lvalue and what
    > > happens to the operands (are they just promoted as for the shift
    > > operands or are the "usual arithmetic conversions" applied as for +
    > > and *). I can see why you would want to avoid too much detail in a
    > > simple explanation like this, but it does seem like a useful thing to
    > > do.

    >
    > Well, it seems like there already is such a table at [1].


    Well I meant something more than that, but I understand your desire to
    keep things simple.

    The interesting things about C operators are the result type and value,
    the conversions that are done to the operands (simple promotion or "the
    usual arithmetic conversions"), the precedence and associativity,
    whether the result denotes an lvalue, and any side effects. Maybe
    that's too much for a single table, but I might have a go though.

    <snip>
    --
    Ben.
     
    Ben Bacarisse, Nov 28, 2012
    #8
  9. Ivan Shmakov

    Frank Miles Guest


    > Generally, the result is as wide as the widest of the operands, or
    > "int", if no operand is wider than "int". The result then may be
    > truncated on function application or assignment. (For instance, i
    > += j is 0 if i is 1, j is 255, and both are declared to be of the
    > 8-bit unsigned integer uint8_t type.)
    >


    And you have to be careful about how/when any expansions occur. For
    example with gcc-avr, if you want

    int32_t = int16_t * int16_t

    (the full 32 bit result of a 16x16 bit multiply), you have to cast each
    of the 16-bit operands to 32bits.
     
    Frank Miles, Nov 28, 2012
    #9
  10. On 2012-11-28, Frank Miles <> wrote:
    >
    >> Generally, the result is as wide as the widest of the operands, or
    >> "int", if no operand is wider than "int". The result then may be
    >> truncated on function application or assignment. (For instance, i
    >> += j is 0 if i is 1, j is 255, and both are declared to be of the
    >> 8-bit unsigned integer uint8_t type.)
    >>

    >
    > And you have to be careful about how/when any expansions occur. For
    > example with gcc-avr, if you want
    >
    > int32_t = int16_t * int16_t
    >
    > (the full 32 bit result of a 16x16 bit multiply), you have to cast each
    > of the 16-bit operands to 32bits.


    Shouldn't casting just one of the 16 bit values work the same as
    casting both of them?

    --
    Grant Edwards grant.b.edwards Yow! It's NO USE ... I've
    at gone to "CLUB MED"!!
    gmail.com
     
    Grant Edwards, Nov 28, 2012
    #10
  11. Ivan Shmakov

    James Kuyper Guest

    On 11/28/2012 12:53 PM, Frank Miles wrote:
    ....
    > And you have to be careful about how/when any expansions occur. For
    > example with gcc-avr, if you want
    >
    > int32_t = int16_t * int16_t
    >
    > (the full 32 bit result of a 16x16 bit multiply), you have to cast each
    > of the 16-bit operands to 32bits.


    I'm not familiar with gcc-avr. That constitutes a significant deviation
    from standard C, where casting either operand would be sufficient to
    guarantee implicit conversion of the other operand, in accordance with
    the "usual arithmetic conversions". What is the reason for this difference?
     
    James Kuyper, Nov 28, 2012
    #11
  12. Ivan Shmakov

    Arlet Ottens Guest

    On 11/28/2012 07:15 PM, James Kuyper wrote:
    > On 11/28/2012 12:53 PM, Frank Miles wrote:
    > ...
    >> And you have to be careful about how/when any expansions occur. For
    >> example with gcc-avr, if you want
    >>
    >> int32_t = int16_t * int16_t
    >>
    >> (the full 32 bit result of a 16x16 bit multiply), you have to cast each
    >> of the 16-bit operands to 32bits.

    >
    > I'm not familiar with gcc-avr. That constitutes a significant deviation
    > from standard C, where casting either operand would be sufficient to
    > guarantee implicit conversion of the other operand, in accordance with
    > the "usual arithmetic conversions". What is the reason for this difference?


    There's no difference. For gcc-avr it also suffices to cast just one
    operand.
     
    Arlet Ottens, Nov 28, 2012
    #12
  13. In comp.lang.c Tim Wescott <> wrote:
    > On Wed, 28 Nov 2012 18:05:06 +0000, Grant Edwards wrote:


    >> On 2012-11-28, Frank Miles <> wrote:


    >>> And you have to be careful about how/when any expansions occur. For
    >>> example with gcc-avr, if you want


    >>> int32_t = int16_t * int16_t


    >>> (the full 32 bit result of a 16x16 bit multiply), you have to cast each
    >>> of the 16-bit operands to 32bits.


    >> Shouldn't casting just one of the 16 bit values work the same as casting
    >> both of them?


    > Yes. But that's if you take "should" as indicating a moral direction,
    > rather than as an indication of what you can reasonably expect from every
    > tool chain.


    > I would expect that gcc would be ANSI compliant, and would therefore
    > promote both 16-bit integers to 32-bit before doing the multiply.


    Maybe I am missing something here, but are there versions of gcc for 16
    bit processors, with 16 bit int? If so, then promotion to int won't
    promote to 32 bits without a cast.

    -- glen
     
    glen herrmannsfeldt, Nov 28, 2012
    #13
  14. On 2012-11-28, Tim Wescott <> wrote:
    > On Wed, 28 Nov 2012 18:05:06 +0000, Grant Edwards wrote:


    >>> And you have to be careful about how/when any expansions occur. For
    >>> example with gcc-avr, if you want
    >>>
    >>> int32_t = int16_t * int16_t
    >>>
    >>> (the full 32 bit result of a 16x16 bit multiply), you have to cast each
    >>> of the 16-bit operands to 32bits.

    >>
    >> Shouldn't casting just one of the 16 bit values work the same as casting
    >> both of them?

    >
    > Yes. But that's if you take "should" as indicating a moral direction,
    > rather than as an indication of what you can reasonably expect from every
    > tool chain.
    >
    > I would expect that gcc would be ANSI compliant, and would therefore
    > promote both 16-bit integers to 32-bit before doing the multiply.


    Nope. On the target in question (AVR), an "int" is 16 bits (at least
    by default). Same for msp430 (and maybe for some of the H8 targets).
    I think there is a command-line option for some 16-bit targets to tell
    gcc to use 32-bit representations for "int" instead of the defautl 16
    bits.

    > But I've worked with compilers in the past that didn't do this, so
    > when writing code that may be used in multiple places, I up-cast the
    > same way one votes in Chicago: early and often.


    If, like AVR and msp430, an "int" is 16 bits, then you must cast at
    least one of the two operands to a 32 bit integer type if you want a
    16x16=>32 multiply.

    --
    Grant Edwards grant.b.edwards Yow! Mr and Mrs PED, can I
    at borrow 26.7% of the RAYON
    gmail.com TEXTILE production of the
    INDONESIAN archipelago?
     
    Grant Edwards, Nov 28, 2012
    #14
  15. Ivan Shmakov

    James Kuyper Guest

    On 11/28/2012 06:32 PM, Tim Wescott wrote:
    > On Wed, 28 Nov 2012 22:26:20 +0000, Grant Edwards wrote:
    >
    >> On 2012-11-28, Tim Wescott <> wrote:

    ....
    >>> I would expect that gcc would be ANSI compliant, and would therefore
    >>> promote both 16-bit integers to 32-bit before doing the multiply.

    >>
    >> Nope. On the target in question (AVR), an "int" is 16 bits (at least by
    >> default). Same for msp430 (and maybe for some of the H8 targets). I
    >> think there is a command-line option for some 16-bit targets to tell gcc
    >> to use 32-bit representations for "int" instead of the defautl 16 bits.

    >
    > You mean "Absolutely", not "Nope". At least you do if you're referring
    > to a 16-bit int as being conformant to ANSI C.
    >
    > Per ANSI C99 (http://www.open-std.org/jtc1/sc22/WG14/www/docs/n1256.pdf),
    > page 34, the minimum allowable value of INT_MAX is 32767. That fits
    > nicely inside a 16-bit signed number.


    I'm not sure the point you're making here. The type resulting from
    integer promotions is always either an 'int' or an 'unsigned int', and
    they occur only for values of other integer types whose entire range can
    be represented in the promoted type. Therefore, if int is 16 bits,
    int16_t operands will not be promoted at all, much less promoted to a
    32-bit int, in conflict with what you said you expected of an ANSI
    compliant compiler. That's what his "Nope" was referring to.

    On such a platform, the usual arithmetic conversions will cause one of
    the operands to be converted implicitly to a 32-bit int if the other one
    is explicitly converted to a 32-bit int. However, section 6.3.1.1p2
    defines what an "integer promotion" is, and that definition doesn't
    include those conversions.
     
    James Kuyper, Nov 29, 2012
    #15
  16. On 29.11.2012 01:35, David Brown wrote:

    > The correct behaviour for C standards compliance is that when you
    > multiply two operands of different int size, the smaller one is promoted
    > to the size of the larger one.


    Close, but not cigar. You forgot about types smaller than the
    platform's "int". Those will be converted up to either signed or
    unsigned int anyway, i.e. even if both operands are of the same size.

    > So the bit-size of the processor, and the bit-size of "int" on that
    > particular target, is irrelevant.


    Incorrect. It is very relevant as soon as either of the operands' types
    is smaller than "int" on the particular target.

    The rule to remember is that C never does arithmetic on anything smaller
    than an 'int'.

    > Given:
    >
    > int16_t a, b;
    > int32_t c
    >
    > If you write just "c = a * b", then the multiplication is carried it at
    > 16-bit, then promoted to 32-bit.


    Not if you're on a 32-bit target it isn't. Default conversion to 32-bit
    int takes place first, so both operands are first converted to 32-bit,
    then a 32 x 32 --> 32 bit multiply is carried out. At least in
    principle (that is: modulo the "as-if rule").
     
    Hans-Bernhard Bröker, Nov 29, 2012
    #16
  17. David Brown <> writes:
    <snip>
    > The correct behaviour for C standards compliance is that when you
    > multiply two operands of different int size, the smaller one is
    > promoted to the size of the larger one.


    Not exactly, no, though there is some confusion because you talk of
    different int sizes. int is one C type so there is only one int size,
    but I'm assuming you meant "integer types of different size".

    If that's what you meant, it's not quite right because multiplying a
    short by a char (for example) will involve promoting both operands to
    int. Other more outlandish examples include multiplying a char by a
    _Bool and many cases involving bit fields.

    > Then the multiply is carried
    > out modulo the size of the larger one.


    That's one commonly observed behaviour but it is not "the correct
    behaviour". If the common type arrived at by the arithmetic conversions
    is a signed type, the multiplication may overflow and anything at all
    can happen (i.e. what happens is undefined by the C standard). Unsigned
    integer arithmetic does not overflow.

    > Then the result is truncated
    > or extended as needed to fit the target variable.


    Again, not quite. The result is converted to type of the object it is
    being assigned to, and a great deal of leeway is given to
    implementations when the target type is a signed int. If the result
    can't be represented in the target type, either the result is
    implementation defined or an implementation defined signal is raised.

    For unsigned types, the behaviour is entirely defined by the C standard
    (conversion modulo 2^width which is, as you say, truncation).

    > So the bit-size of the processor, and the bit-size of "int" on that
    > particular target, is irrelevant. And the size of the result variable
    > is also irrelevant (this catches out some newbies).
    >
    > Given:
    >
    > int16_t a, b;
    > int32_t c
    >
    > c = (int32_t)a * b;
    >
    > Then b is cast to int32_t, the 32-bit multiplication is carried out,
    > and the result assigned to c.


    (unless int happens to be wider than 32 bits)

    > If you write just "c = a * b", then the multiplication is carried it
    > at 16-bit, then promoted to 32-bit. This applies regardless of the
    > bit-size of the target - you will get the same effect on a 64-bit cpu
    > as on the 8-bit AVR.


    The machine bit-size is not really the thing that matters. What matters
    is the sizes assigned to the various types by the C implementation.
    What you say is roughly correct for an implementation with a 16 bit int
    type ("roughly" because of the possibility of overflow).

    The size given to int is often the natural one (or one of the natural
    ones) for the machine in question. When this is the case, the bit size
    of the target does matter, but only because of the differing int sizes.

    > If your compiler does 16-bit multiplications when you have "c =
    > (int32_t) a * b", and requires two "int32_t" casts to do 32-bit
    > multiplication, then your compiler is very badly broken. As Tim says,
    > badly broken compilers /do/ exist, so if you have to use them, then
    > you need to use two casts. But I personally don't think you need to
    > write your code to work with broken toolchains unless you actually
    > have to.


    It leads to a special kind of hell! When you can't ever shake off the
    idea that x, y or z once went wrong on compiler p, q or r, you end up
    having to fold every trick you ever used to get your code past bad
    compilers into every program.

    --
    Ben.
     
    Ben Bacarisse, Nov 29, 2012
    #17
  18. In comp.lang.c David Brown <> wrote:

    (snip, someone wrote)
    >>> I would expect that gcc would be ANSI compliant, and would therefore
    >>> promote both 16-bit integers to 32-bit before doing the multiply.


    (snip, then I wrote)
    >> Maybe I am missing something here, but are there versions of gcc for 16
    >> bit processors, with 16 bit int? If so, then promotion to int won't
    >> promote to 32 bits without a cast.


    > The correct behaviour for C standards compliance is that when you
    > multiply two operands of different int size, the smaller one is promoted
    > to the size of the larger one. Then the multiply is carried out modulo
    > the size of the larger one. Then the result is truncated or extended as
    > needed to fit the target variable.


    I haven't read the standard so recently, but I thought that was only
    after the default promotions. Values smaller than int would be promoted
    to int, then the size of the multiply (and product) determined.

    If you multiply two 8 bit unsigned char values, is the product
    modulo 256? I don't think so.

    > So the bit-size of the processor, and the bit-size of "int" on that
    > particular target, is irrelevant. And the size of the result variable
    > is also irrelevant (this catches out some newbies).


    > Given:


    > int16_t a, b;
    > int32_t c


    > c = (int32_t)a * b;


    > Then b is cast to int32_t, the 32-bit multiplication is carried out, and
    > the result assigned to c.


    Maybe not completely irrelevent, consider a system with a 64 bit int.

    > If you write just "c = a * b", then the multiplication is carried it at
    > 16-bit, then promoted to 32-bit. This applies regardless of the
    > bit-size of the target - you will get the same effect on a 64-bit cpu as
    > on the 8-bit AVR.


    Regardless of the target size, but not of int size.

    > If your compiler does 16-bit multiplications when you have "c =
    > (int32_t) a * b", and requires two "int32_t" casts to do 32-bit
    > multiplication, then your compiler is very badly broken. As Tim says,
    > badly broken compilers /do/ exist, so if you have to use them, then you
    > need to use two casts. But I personally don't think you need to write
    > your code to work with broken toolchains unless you actually have to.


    Now it gets interesting. When were the int_32_t and int_16_t added to C?

    Seems to me that compilers only claiming a version of the standard
    before they were added wouldn't have to use the same rules.

    Consider that a compiler might have a int_128_t that it could add and
    subtract, but not multiply or divide. Maybe it can generate a 128 bit
    product from two 64 bit operands. Does the standard prohibit a
    compiler from offering those operations?

    -- glen
     
    glen herrmannsfeldt, Nov 29, 2012
    #18
  19. glen herrmannsfeldt <> writes:

    > In comp.lang.c David Brown <> wrote:
    >
    > (snip, someone wrote)
    >>>> I would expect that gcc would be ANSI compliant, and would therefore
    >>>> promote both 16-bit integers to 32-bit before doing the multiply.

    >
    > (snip, then I wrote)
    >>> Maybe I am missing something here, but are there versions of gcc for 16
    >>> bit processors, with 16 bit int? If so, then promotion to int won't
    >>> promote to 32 bits without a cast.

    >
    >> The correct behaviour for C standards compliance is that when you
    >> multiply two operands of different int size, the smaller one is promoted
    >> to the size of the larger one. Then the multiply is carried out modulo
    >> the size of the larger one. Then the result is truncated or extended as
    >> needed to fit the target variable.

    >
    > I haven't read the standard so recently, but I thought that was only
    > after the default promotions. Values smaller than int would be promoted
    > to int, then the size of the multiply (and product) determined.


    Yes, it's two-stage process.

    In case it helps, here is the terminology as used by the C standard:

    "integer promotions" These are the conversions that often occur prior
    to preforming an arithmetic operation. They form part of the:

    "usual arithmetic conversions" which is how a common type is arrived at
    for arithmetic operations that requite it. Unless complex or floating
    types are involved, the integer promotions are performed on both
    operands and then a set of rules is used to determine the common type.
    For integer types it is usually simply the widest of the two, even if
    that is an unsigned type and the other is a signed type.

    "default argument promotions" apply to function calls in the absence of
    a prototype. These are the integer promotions augmented by a conversion
    of float to double.

    The standard never refers to a conversion as a cast (a cast is an
    operator that performs an explicit conversion) and it uses the term
    "promotion" only in the context of the implicit conversions described
    above. A conversion of one type to a wider one in some other context is
    not called a promotion.

    > If you multiply two 8 bit unsigned char values, is the product
    > modulo 256? I don't think so.


    No, it's not.

    <snip>
    > Now it gets interesting. When were the int_32_t and int_16_t added to
    > C?


    1999. The types are intN_t and uintN_t (no extra _) for various N.
    They are optional, but must be defined if the implementation has
    suitable types (basically 2's complement and no padding bits). Other,
    similar, types like int_leastN_t and int_fastN_t are required in all
    implementations. For example, int_least32_t is the smallest type that
    has at least 32 (value) bits.

    > Seems to me that compilers only claiming a version of the standard
    > before they were added wouldn't have to use the same rules.


    True.

    > Consider that a compiler might have a int_128_t that it could add and
    > subtract, but not multiply or divide. Maybe it can generate a 128 bit
    > product from two 64 bit operands. Does the standard prohibit a
    > compiler from offering those operations?


    I don't think it could define int128_t unless it could multiply them.

    --
    Ben.
     
    Ben Bacarisse, Nov 29, 2012
    #19
  20. In comp.lang.c Ben Bacarisse <> wrote:

    (snip, I wrote)

    >> If you multiply two 8 bit unsigned char values, is the product
    >> modulo 256? I don't think so.


    > No, it's not.


    For comparison purposes, I believe that Fortran does not have this rule.

    If you add, subtract, multiply, or (I believe) divide 8 bit integers
    the result is, generally, eight bits. One should, at least, not be
    surprised if the result is computed modulo some small value.

    I like the C rules better.

    PL/I generally tries to keep the bits until it reaches the
    implementation maximum. That is complicated when scaled fixed
    point (non-integer) values are used, where it keeps the appropriate
    digits (binary or decimal) to the right of the radix point,
    possibly truncating on the left.

    -- glen
     
    glen herrmannsfeldt, Nov 29, 2012
    #20
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. avishay

    AVR core and patents

    avishay, Jun 9, 2005, in forum: VHDL
    Replies:
    0
    Views:
    730
    avishay
    Jun 9, 2005
  2. Replies:
    0
    Views:
    592
  3. Affan Syed

    Explain Macro from avr-libc

    Affan Syed, Mar 8, 2005, in forum: C Programming
    Replies:
    6
    Views:
    508
    Mark F. Haigh
    Mar 9, 2005
  4. RadGar

    CCITT in C++ for AVR

    RadGar, Nov 27, 2005, in forum: C++
    Replies:
    2
    Views:
    531
    RadGar
    Nov 27, 2005
  5. lorenzvvv

    wctomb function for AVR

    lorenzvvv, Apr 4, 2007, in forum: C Programming
    Replies:
    3
    Views:
    365
    Keith Thompson
    Apr 5, 2007
Loading...

Share This Page